babeltrace.git
7 years agoImplement the component connection interface
Jérémie Galarneau [Fri, 10 Feb 2017 15:50:13 +0000 (10:50 -0500)] 
Implement the component connection interface

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoText: tone down the rainbow text color scheme
Jérémie Galarneau [Tue, 14 Feb 2017 15:01:12 +0000 (10:01 -0500)] 
Text: tone down the rainbow text color scheme

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoRename "query info" to "query" everywhere, and "action" to "object"
Philippe Proulx [Mon, 13 Feb 2017 22:01:33 +0000 (17:01 -0500)] 
Rename "query info" to "query" everywhere, and "action" to "object"

A "query" here is a request for a specific named object using or not
parameters. This is analogous to a REST (HTTP) GET stateless request:

    GET /path/to/object?param1=value1&param2=value2

Also expect the `metadata-info` object in the ctf.fs query method
instead of the `get-metadata-info` action.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agotext: mask some internal fields
Julien Desfossez [Mon, 23 Jan 2017 20:39:39 +0000 (15:39 -0500)] 
text: mask some internal fields

Some fields from the stream packet context are not really useful to the
end user of the text output, so we mask them by default. They can be
displayed using the -v/--verbose flag. The masked fields are the same as
in previous version:
 - timestamp_begin
 - timestamp_end
 - events_discarded
 - content_size
 - packet_size
 - packet_seq_num

Signed-off-by: Julien Desfossez <jdesfossez@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd the verbose parameter to the legacy text options
Julien Desfossez [Mon, 23 Jan 2017 20:37:32 +0000 (15:37 -0500)] 
Add the verbose parameter to the legacy text options

Signed-off-by: Julien Desfossez <jdesfossez@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobabeltrace(1): support -o metadata legacy option
Philippe Proulx [Sat, 11 Feb 2017 23:26:13 +0000 (18:26 -0500)] 
babeltrace(1): support -o metadata legacy option

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoctf.fs source: add `get-metadata-info` query info action
Philippe Proulx [Sat, 11 Feb 2017 22:30:46 +0000 (17:30 -0500)] 
ctf.fs source: add `get-metadata-info` query info action

The `get-metadata-info` query info action of the ctf.fs source returns,
if successful, the (possibly decoded) metadata string and if the
metadata file is packetized or not. It only needs the `path` parameter
which is the trace directory path (non-recursive).

You can test it with the query-info command:

    babeltrace query-info --source ctf.fs get-metadata-info
                          -p 'path="/path/to/trace"'

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobabeltrace(1): print value with colors
Philippe Proulx [Sat, 11 Feb 2017 21:05:12 +0000 (16:05 -0500)] 
babeltrace(1): print value with colors

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobabeltrace(1): add the query-info command
Philippe Proulx [Sat, 11 Feb 2017 20:37:09 +0000 (15:37 -0500)] 
babeltrace(1): add the query-info command

The new query-info command is the interface for the query info component
class API:

    babeltrace query-info --sink plugin.sink get-something
       --params meow=mix,zoom=18,consider-this=yes

The action is mandatory, as well as exactly one --source/--filter/--sink
option with the plugin and component class names to query info from. If
--params is not omitted, the query info method receives bt_value_null.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobabeltrace-cfg: put comp. class type within struct bt_config_component
Philippe Proulx [Sat, 11 Feb 2017 19:20:21 +0000 (14:20 -0500)] 
babeltrace-cfg: put comp. class type within struct bt_config_component

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobt2: test query info API
Philippe Proulx [Sat, 11 Feb 2017 18:54:52 +0000 (13:54 -0500)] 
bt2: test query info API

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobt2: add support for the "query info" API
Philippe Proulx [Sat, 11 Feb 2017 18:27:06 +0000 (13:27 -0500)] 
bt2: add support for the "query info" API

Any user-defined component class can now define a _query_info() static
method which accepts an action name and parameters (value object, or
None) and returns a bt2.create_value()-compatible object:

    class MySink(bt2.UserSinkComponent):
        def _consume(self):
            pass

        @staticmethod
        def _query_info(action, params):
            if action == 'get-stuff':
                return {
                    'stuff': get_stuff(),
                    'id': get_id()
                }
            elif action == 'get-thing':
                return the_thing()

This static method can raise any exception: the caller of
bt_component_class_query_info() gets NULL.

On the Python side, either with a user-defined component class or with
a component class wrapper:

    results = comp_class.query_info('get-stuff', {'name': 'wrerzvr'})

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobt2: raise when bt2.create_plugin_from_name() finds nothing
Philippe Proulx [Sat, 11 Feb 2017 18:23:12 +0000 (13:23 -0500)] 
bt2: raise when bt2.create_plugin_from_name() finds nothing

Returning None from this function when there's no plugin is Pythonically
weird:

    plugin = bt2.create_plugin_from_name()
    print(plugin.name)

The execution should not reach print() here when the plugin is not
found.

Raise the new bt2.NoSuchPluginError exception instead.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd query info API tests
Philippe Proulx [Sat, 11 Feb 2017 04:44:44 +0000 (23:44 -0500)] 
Add query info API tests

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoSO plugin API: add comp. class query info method macro and use it
Philippe Proulx [Sat, 11 Feb 2017 04:29:28 +0000 (23:29 -0500)] 
SO plugin API: add comp. class query info method macro and use it

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd bt_component_class_query_info() API
Philippe Proulx [Sat, 11 Feb 2017 04:19:27 +0000 (23:19 -0500)] 
Add bt_component_class_query_info() API

When you create a component class, you can set an optional "query info"
class method with bt_component_class_sink_set_query_info_method().

A "query info" class method is a method which accepts an action name
and custom parameters and returns custom results. It can be used for
component class-specific requests such as getting the beginning and end
timestamps of a trace, its total size, and other statistics. On some
real-time source component class, it can be used to get a list of
endpoints to which to connect.

You can use bt_component_class_query_info() to query a component class.
The action name and parameters are mandatory, although you can use
bt_value_null for the parameters.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobabeltrace(1): handle legacy -o dummy option
Philippe Proulx [Sat, 11 Feb 2017 03:56:33 +0000 (22:56 -0500)] 
babeltrace(1): handle legacy -o dummy option

Legacy -o dummy option instantiates a utils.dummy sink component.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd utils.dummy component class, move plugins/trimmer to plugins/utils
Philippe Proulx [Sat, 11 Feb 2017 03:49:22 +0000 (22:49 -0500)] 
Add utils.dummy component class, move plugins/trimmer to plugins/utils

This patch adds a plugins/utils directory where the utils plugin sources
are. plugins/trimmer is moved to this new directory because the trimmer
component class is part of the utils plugin.

Also in the utils plugin is the dummy sink component class. This sink
does absolutely nothing with its notifications. It is the equivalent of
the BT 1.x dummy format. At consume time, it gets one notification per
input notification iterator and returns
BT_NOTIFICATION_ITERATOR_STATUS_END when all the iterators are at the
end.

The legacy `-o dummy` option is not handled specifically in this commit.
You can use the dummy sink as usual:

    babeltrace /path/to/trace -o utils.dummy

The dummy component class ignores its initialization parameters.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agotext plugin: add color support
Philippe Proulx [Sat, 11 Feb 2017 01:31:56 +0000 (20:31 -0500)] 
text plugin: add color support

This patch adds terminal color support to the text plugin.

The text component class accepts a new initialization parameter `color`
which you can set to one of the following strings:

never:
  Never output color codes.

auto:
  Output color codes if the standard output is connected to a
  color-capable terminal and if the file stream to use is the standard
  output.

always:
  Always output color codes. This is useful to print colors even if the
  standard output is not connected to a terminal, for example:

      babeltrace /path/to/trace -o text.text -p color=always | less -R

The semantic of this parameter is the same as ls(1)'s and grep(1)'s
--color option.

The chosen color scheme is open to debate. In an ideal world, it should
be configurable by the component parameters, or read $LS_COLORS.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobabeltrace(1): add --connect option and connection management
Philippe Proulx [Fri, 10 Feb 2017 20:54:23 +0000 (15:54 -0500)] 
babeltrace(1): add --connect option and connection management

This patch adds the --connect option to connect component instances
by name (and optional port name):

    --connect SRC[SRCPORT]:DST[DSTPORT]

Example:

    babeltrace convert --source a.a --name A1 --source a.a --name A2
                       --sink b.b --name B
                       --connect A1.port:B
                       --connect A2.port:B.other-port

The code in babeltrace-cfg-connect.c validates that:

1. All the endpoint specified in connection arguments exist.

2. All connections are in the correct direction (source to filter,
   source to sink, filter to filter, filter to sink).

3. SRC and DST are not the same above.

4. All component instances are connected (no orphan component).

5. There's no duplicate connection.

There's a remaining validation to be done: ensure that there's no cycle
in the graph created by those connections.

If no --connect options are specified, babeltrace-cfg-connect.c connects
the component instances automatically:

1. It gives a unique name to unnamed component instances. The automatic
   name is TYPE-PLUGIN.COMPCLS.INDEX, where:

   TYPE:
     `source`, `filter`, or `sink`

   PLUGIN:
     Plugin name

   COMPCLS:
     Component class name

   INDEX:
     Automatic index to avoid collisions

2. It creates a multiplexer filter component configuration
   (`utils.mux`).

3. It connects the default ports of all the configured sources to the
   default port of this multiplexer filter.

4. It connects this multiplexer filter to the default ports of all the
   configured sinks.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobabeltrace convert: add --name option
Philippe Proulx [Fri, 10 Feb 2017 06:33:06 +0000 (01:33 -0500)] 
babeltrace convert: add --name option

The --name option sets the name of the current (latest) component
instance, e.g.:

    babeltrace convert --source=abc.xzy --name=my-component ...

This can be useful to locate specific instances in the conversion graph,
in particular in case of error.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobabeltrace(1): add help command
Philippe Proulx [Fri, 10 Feb 2017 06:12:57 +0000 (01:12 -0500)] 
babeltrace(1): add help command

The new `help` command shows all the information of a specific plugin
or of a specific component class.

The syntax to get help for a specific plugin is:

    babeltrace help PLUGIN

and for a specific component class:

    babeltrace help (--source | --filter | --sink) PLUGIN.COMPCLS

The full help of the component class (bt_component_class_get_help())
is printed when available.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobabeltrace-cfg.c: improve error messages
Philippe Proulx [Fri, 10 Feb 2017 00:02:49 +0000 (19:02 -0500)] 
babeltrace-cfg.c: improve error messages

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoMake babeltrace(1)'s CLI Git-like and implement the list-plugins command
Philippe Proulx [Thu, 9 Feb 2017 19:42:57 +0000 (14:42 -0500)] 
Make babeltrace(1)'s CLI Git-like and implement the list-plugins command

This patch adds a command concept to the babeltrace(1) program, so that
the usage becomes:

    babeltrace [GENERAL OPTIONS] [COMMAND] [COMMAND OPTIONS]

The idea is to support multiple actions performed by the same program
without having to deal with a single command-line option parsing phase.

The default command is `convert`: it builds a trace conversion graph and
runs it. Therefore the current behaviour without a command name remains
the same.

The general command-line options, which can apply to all commands, are:

    -d, --debug        Enable debug mode
    -h  --help         Show general help and quit
        --help-legacy  Show Babeltrace 1.x legacy help and quit
    -v, --verbose      Enable verbose output
    -V, --version      Show version and quit

You can get the usage of a specific command with:

    babeltrace COMMAND --help

Because there is a default command, the parsing of the general options
is done manually to catch the first unknown option OR positional
argument. If an unknown option is found, all the arguments are given
back to the convert command. This is why the convert command also
parses --verbose, for example, because of this situation:

    babeltrace --debug --sink=my.sink --verbose

Here, the general options parsing stops at --sink because it's unknown,
and no explicit command name was found so far, so everything starting
with --debug is given to the convert command.

I also implemented the `list-plugins` command which lists plugins, their
component classes, and their properties:

    babeltrace list-plugins

You can still specify a plugin path or use the BABELTRACE_PLUGIN_PATH
environment variable with this command:

    babeltrace list-plugins --plugin-path=/path/to/other/plugins

I added color support in libbabeltrace-common and used it in the
`list-plugins` command. Color codes are only printed when color support
is detected, that is, when the standard output is connected to a
color-compatible terminal.

I also improved the way bt_value objects are printed with print_value()
in babeltrace.c to make it look more YAML-ish. This is easier on the
eyes.

This patch introduces a backward compatibility break in the very
specific scenario where an explicit command name is used and a directory
in the CWD exists with this name:

    babeltrace list-plugins

If `list-plugins` is a directory in the CWD, the legacy behaviour is to
recursively find CTF traces in it. With this patch, plugins are listed.
However a message is printed at the end of the command in this case:

    NOTE: The `list-plugins` command was executed. If you meant to
    convert a trace located in the local `list-plugins` directory,
    please use:

        babeltrace convert list-plugins [OPTIONS]

In other words, `babeltrace convert` (with 2.x) is a drop-in replacement
of `babeltrace` (with 1.x).

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobabeltrace.c: replace printf_verbose() with printf() where appropriate
Philippe Proulx [Thu, 9 Feb 2017 06:24:55 +0000 (01:24 -0500)] 
babeltrace.c: replace printf_verbose() with printf() where appropriate

Only check if babeltrace_verbose is active and use printf() directly if
so instead of always using printf_verbose() which prints the [verbose]
prefix.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoCleanup babeltrace-cfg, use BABELTRACE_PLUGIN_PATH
Philippe Proulx [Thu, 9 Feb 2017 03:04:45 +0000 (22:04 -0500)] 
Cleanup babeltrace-cfg, use BABELTRACE_PLUGIN_PATH

This commit changes a few things at once:

* Cleanups the help message (orders long options alphabetically,
  adds metavars where missing, etc.).

* Removes is_setuid_setgid() to use the common bt_is_setuid_setgid().

* Makes the CLI load plugins *non-recursively*, which is more in line
  with typical lists of paths such as PATH, LD_LIBRARY_PATH, PYTHONPATH,
  etc., from the following directories, in this order:

  1. BABELTRACE_PLUGIN_PATH environment variable
  2. --plugin-path option
  3. ~/.local/lib/babeltrace/plugins
  4. System path (/usr/lib/babeltrace/plugins)

  For the in-tree version (conveter/babeltrace), Makefile.am passes all
  the in-tree plugin paths so that it can find everything that is known
  within the tree.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobt2: test comp. class help attribute
Philippe Proulx [Wed, 8 Feb 2017 01:20:07 +0000 (20:20 -0500)] 
bt2: test comp. class help attribute

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobt2: add support for comp. class help
Philippe Proulx [Wed, 8 Feb 2017 01:12:39 +0000 (20:12 -0500)] 
bt2: add support for comp. class help

The description of a user component class is the first line of the
class's docstring; the help is everything else starting at line 3:

class MySink(bt2.UserSinkComponent):
    '''This is the description.

    This is the help.

    This too:

        * And this.
        * And also that.

    Voilà.
    '''

    def _consume(self):
        # ...

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd shared object comp. class help attribute test
Philippe Proulx [Wed, 8 Feb 2017 00:56:36 +0000 (19:56 -0500)] 
Add shared object comp. class help attribute test

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoShared object plugin: add component class help support
Philippe Proulx [Wed, 8 Feb 2017 00:55:43 +0000 (19:55 -0500)] 
Shared object plugin: add component class help support

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd component class help property
Philippe Proulx [Wed, 8 Feb 2017 00:40:49 +0000 (19:40 -0500)] 
Add component class help property

The help of a component class is a full text which describes its
purpose, the expected params for its constructor, etc. It does not have
to start with a description since there's already a description property for
this.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobt2: add create_plugin_from_name()
Philippe Proulx [Wed, 8 Feb 2017 00:12:33 +0000 (19:12 -0500)] 
bt2: add create_plugin_from_name()

This function wraps bt_plugin_create_from_name().

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd bt_plugin_create_from_name() tests
Philippe Proulx [Tue, 7 Feb 2017 23:30:48 +0000 (18:30 -0500)] 
Add bt_plugin_create_from_name() tests

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd bt_plugin_create_from_name()
Philippe Proulx [Tue, 7 Feb 2017 06:22:10 +0000 (01:22 -0500)] 
Add bt_plugin_create_from_name()

This new function creates a new bt_plugin object from a simple plugin
name. It returns the first plugin which has the given name within the
following directories, in this order:

1. The colon-separated directories listed in the BABELTRACE_PLUGIN_PATH
   environment variable.
2. ~/.local/lib/babeltrace/plugins
3. $libdir/babeltrace/plugins, where $libdir is the value of --libdir
   at configure time (/usr/local/lib by default, typically /usr/lib
   when installed through a distribution).
4. The statically built-in plugins.

This makes it easier for any application using libbabeltrace to find a
Babeltrace plugin in a standard way without having to know the exact
location of the plugin file:

    ctf_plugin = bt_plugin_create_from_name("ctf");

Since babeltrace(1) also does this search, but has a --plugin-path
parameter to insert other directories at a specific place within the
search list, the common functions are put in the new
common/libbabeltrace-common.la convenience library. This is where new
(and existing) common functions between libbabeltrace and other parts of
the source tree (converter, plugins, etc.) should be put.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd support for plugins written in Python
Philippe Proulx [Fri, 3 Feb 2017 20:30:38 +0000 (15:30 -0500)] 
Add support for plugins written in Python

Now that the bt2 package exists and allows a user to create its own
component classes, we're not so far from a Python plugin support, a
Babeltrace plugin being only a set of basic attributes and a list of
component classes.

The chosen approach here is to add a module to the bt2 package,
py_plugin, which contains the following:

* plugin_component_class(): Function to be used as a decorator to tag a
  given user component class as being part of a Babeltrace plugin.

* register_plugin(): Function to be called anywhere from a Python module
  to register this module as a Babeltrace plugin. This call receives
  the name of the plugin and other optional attributes (description,
  author, license, etc.).

* _try_load_plugin_module(): Function reserved for the Babeltrace
  library to try to get a plugin information object from a given
  path (Python file).

  The same logic could be implemented with the Python C API, but since
  a Babeltrace Python plugin needs to import the bt2 package anyway,
  we're sure that the package exists and that we can use it. Arguably
  this function could be located in another module, outside the bt2
  package, for example in /usr/lib/babeltrace/plugin_utils.py.

Here's a very simple Python plugin which contains a single sink
component class named MySink:

    import bt2

    @bt2.plugin_component_class
    class MySink(bt2.UserSinkComponent):
        def __init__(self, params, name):
            self._stuff = params['stuff']

        def _consume(self):
            got_one = False

            for notif_iter in self._input_notification_iterators:
                try:
                    notif = next(notif_iter)
                except StopIteration:
                    continue

                got_one = True

                if isinstance(notif, bt2.TraceEventNotification):
                    event = notif.event
                    print('>>>', self._stuff, event.name)

            if not got_one:
                raise bt2.Stop

    bt2.register_plugin(__name__, name='my_plugin',
                        author='Philippe Proulx', license='MIT',
                        version=(2, 35, 3, '-dev'))

Here's what happens when the C API user does this:

    plugins = bt_plugin_create_all_from_file(
        "python-plugins/bt_plugin_hello.py");

1. bt_plugin_create_all_from_file() calls
   bt_plugin_so_create_all_from_file() without success, because the
   extension does not match.

   Then it tries bt_plugin_python_create_all_from_file().

2. bt_plugin_python_create_all_from_file() makes sure that the base name
   of the file to load starts with `bt_plugin_` and ends with `.py`. The
   prefix is up for debate, but my argument is that we don't want to
   import all the Python files when loading all the plugins of a
   directory: some Python files could be modules imported by plugins,
   not actual plugins. Having the prefix at least reduces the
   possibility of importing a non-plugin Python module.

3. init_python() is called. This function initializes the Python
   interpreter if it's not already done. It also refuses to do so if
   BABELTRACE_DISABLE_PYTHON_PLUGINS=1 (environment variable). It also
   imports the bt2.py_plugin module and finds the
   _try_load_plugin_module() function (global object, put in the
   library's destructor).

   If the needed global objects are still not set after this call,
   bt_plugin_python_create_all_from_file() returns an error. If
   init_python() fails, all the future calls to
   bt_plugin_python_create_all_from_file() will also fail (we don't
   attempt to initialize Python again if it failed once).

4. bt_plugin_python_create_all_from_file() calls
   _try_load_plugin_module() (Python) with the path, let's say
   `python-plugins/bt_plugin_hello.py`.

  a) A module name is created for this plugin. Since the plugin system
     can load plugins from files having the same base name, but in
     different directories, we cannot use the base name here. For
     example, we cannot name the module `bt_plugin_hello`: module names
     are unique in Python (for the whole interpreter) and are registered
     in the sys.modules dictionary.

     What's done here is to hash the path and prefix this with
     `bt_plugin_`. For example, this module would have the name
     bt_plugin_9dc80c7b58e49667cb5898697a5197d22f3a09386d017e08e2....

  b) The function tries to import the module using importlib. The module
     is executed from top to bottom:

    i) The @bt2.plugin_component_class decorator is called on the MySink
       class (which, thanks to its metaclass, has an associated native
       BT component class created): as a reminder, this is the
       equivalent of:

           MySink = bt2.plugin_component_class(MySink)

       This function adds an attribute to MySink: it sets
       MySink._bt_plugin_component_class to None. The mere existence
       of this attribute, whatever its value, means that this user
       component class is part of the plugin.

    ii) The module calls bt2.register_plugin(), passing __name__ as the
        first argument. This is used for bt2.register_plugin() to find
        the module from which it is called. In this case, __name__
        is bt_plugin_9dc80c7b58e49667cb5898697a5197d22f3a09386d017e...
        bt2.register_plugin() gets the actual module object from
        sys.modules and adds the _bt_plugin_info attribute to it after
        checking that all the arguments are correct. This is an object
        which contains, so far, the name and other optional properties
        of the plugin.

  c) If the import is successful, the function looks for the
     _bt_plugin_info attribute in the module object. If it's not found,
     an error is raised.

  d) The function then uses the inspect module to find all the classes
     in the module which contain the _bt_plugin_component_class
     attribute. All the native BT component class addresses are appended
     to a list which is set as the comp_class_addrs attribute of the
     plugin info object.

     _try_load_plugin_module() returns this plugin info object.

5. bt_plugin_python_create_all_from_file() calls
   bt_plugin_from_python_plugin_info() to convert the returned Python
   object to a bt_plugin object, which is returned to the user.

If anything goes wrong on the Python side during this call, and if
BABELTRACE_VERBOSE=1, the Python traceback is printed.

With the plugin above, we can run the converter like this to use its
sink component class:

    babeltrace --plugin-path python-plugins \
               -i ctf.fs -P /path/to/trace \
               -o my_plugin.MySink -p 'stuff="hello there!"'

We get something like this:

    >>> hello there! sched_switch
    >>> hello there! sys_open
    >>> hello there! sched_wakeup
    ...

It seems like Py_InitializeEx() and PyImport_ImportModule() can override
the current SIGINT handler, which is why we save the old handler before
calling those and then restore it afterwards. This seems to work.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd Babeltrace 2 Python bindings tests
Philippe Proulx [Sat, 4 Feb 2017 03:40:47 +0000 (22:40 -0500)] 
Add Babeltrace 2 Python bindings tests

This patch adds a testing infrastructure for the Python bindings of
Balbetrace, as well as tests for the bt2 package.

To run those tests, you need the tappy Python project
(https://github.com/python-tap/tappy). The configure script checks that
this project is available when you specify the new
--enable-python-bindings-tests option.

A test runner script, tests/utils/python/testrunner.py, loads the test
cases from a directory passed as its first command-line argument, sets
the TAP runner, and runs the tests, exiting with 0 if everything was
successful.

The Python binding tests are in tests/bindings/python/bt2 and are only
executed if the project is configured with --enable-python-bindings.
In this directory, testall.sh is generated from testall.sh.in to
call the test runner script with the appropriate paths, and set the
appropriate PYTHONPATH and LD_LIBRARY_PATH environment variables.

You can run testall.sh directly or with TESTALL_COVERAGE=1 to check the
coverage of the bt2 package with the Python `coverage` tool. You can
also set TESTALL_COVERAGE_HTML=1 to get an HTML report, or
TESTALL_COVERAGE_REPORT=1 to get a plain text report. The .coveragerc
file in this directory is a coverage configuration file which omits
certain files and exclude certain lines. The coverage of most modules
by the the tests is in the 90%-95% range as of this patch:

    __init__.py               100%
    clock_class.py            97%
    component.py              97%
    ctf_writer.py             63%
    event.py                  97%
    event_class.py            98%
    field_types.py            100%
    fields.py                 96%
    notification.py           79%
    notification_iterator.py  91%
    packet.py                 98%
    plugin.py                 30%
    stream.py                 92%
    stream_class.py           97%
    trace.py                  99%
    values.py                 95%

It is expected that ctf_writer.py be tested by the tests of the original
`babeltrace` package which will wrap the bt2 package in a future patch.

test_values.py and test_fields.py test that value and field objects
behave like Python native objects. For this, test methods are generated
to try each possible operator between each possible object. The result
is compared with the same operator applied to native Python objects (or
if the operation raises something, the compared operation must raise the
same thing).

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd Babeltrace 2 Python bindings
Philippe Proulx [Sat, 4 Feb 2017 03:38:48 +0000 (22:38 -0500)] 
Add Babeltrace 2 Python bindings

This patch adds new Babeltrace 2 Python bindings to the Babeltrace
project.

Those bindings are compatible with Python 3 (only).

The new bindings still make use of SWIG to simplify the Python-to-native
and native-to-Python calls.

The new bindings, from Python's point of view, are available in the new
`bt2` package. This package imports (__init__.py does) everything that
is public from its modules, so that every public name is available as
bt2.something. This is considered more Pythonic than asking the user to
import specific modules from a given package.

The goal with this, to keep the "old" `babeltrace` package working, is
to make the `babeltrace` package a simple Python-only wrapper of the bt2
package (which offers much more, as you will discover in this commit
message).

Summary of features:

* All the current Babeltrace 2 APIs are wrapped:
  * Clock class
  * Component, component class, notification iterator, and notification
  * CTF writer, CTF writer clock, CTF writer stream
  * Event and event classe
  * Packet, stream, and stream class
  * Fields and field types
  * Plugin
  * Trace
  * Values
* Automatic BT reference count handling for the user (just like the
  `babeltrace` package does).
* Type checking of the arguments of each method/function (before it gets
  to SWIG, where the exception is not as obvious).
* Package exceptions:
  * Error
  * CreationError
  * FrozenError
  * UnsupportedFeature
  * TryAgain
  * Stop
  * IncompleteUserClassError
* Full support of user component classes.
* Package is as Pythonic as possible, with extensive use of collection
  ABCs, number and other protocols, iterators, properties, inheritance
  for the user, and exceptions.
* Easy to extend if we ever add new BT objects or APIs.
* The bindings only use the public Babeltrace 2 C API; they could be
  built outside the Babeltrace repository without altering the code.

Build system
============
Makefile.am does pretty much the same job as previously, although it is
organized so that it's easy to add Python modules and partial SWIG
interfaces. All the rules and commands are built from two simple lists.

SWIG
====
I created one SWIG interface file (.i) for each Babeltrace API. All the
native_bt*.i files are included at the end of native_bt.i. This is the
only input for SWIG.

native_bt.i does more than including partial interface files. It adds
rules to remove the bt_ and BT_ prefixes of all the wrapped functions
and enumeration items.

native_bt.i also adds a few custom typemaps to convert special arguments
back and forth between Python and the C API. For example,
`const char **BTOUTSTR` is a typemap to append a Python string (Unicode
object) to the current SWIG result tuple when the argument is named
as such, as in:

int bt_ctf_field_type_enumeration_get_mapping_signed(
        struct bt_ctf_field_type *enum_field_type, int index,
        const char **BTOUTSTR, int64_t *OUTPUT, int64_t *OUTPUT);

Note that, in example above, OUTPUT is a typemap provided by SWIG for
very simple types.

Another typemap is BTUUID to accept and return Babeltrace UUIDs as
Python `bytes` objects:

    BTUUID bt_ctf_clock_class_get_uuid(struct bt_ctf_clock_class *clock_class);

    int bt_ctf_clock_class_set_uuid(struct bt_ctf_clock_class *clock_class,
            BTUUID uuid);

Modules
=======
I'll now go into the details of each module of the bt2 package, in a
relevant order.

Most of the objects described below are comparable. Their compare
function usually start with a simple address comparison, and then falls
back to a rich comparison (sometimes implemented in Python if the
equivalent C function is missing).

utils
-----
Small utility functions: type checking, automatic exception raising,
power of two, etc.

object
------
bt_object API and reference counting.

Base class for a wrapped BT object (with reference counting), not
meant to be instantiated by the user.

Provides:

* self._ptr: SWIG pointer (native BT object), for the functions of
  the bt2 package (private).

* __init__(): called by subclass to wrap a SWIG pointer.

* addr(): public property which returns the address (integer, not the
  SWIG pointer object) of the object, mostly for debug purposes (for the
  user) and for a user to know if two bt2 objects actually wrap the
  same BT native object.

* _create_from_ptr(): class method to wrap a given SWIG pointer as an
  objet of a given class:

      event_class = EventClass._create_from_ptr(ptr)

* __repr__(): Shows the type of the object and its native address.

* __del__(): Puts its BT object reference (calls bt_put()).

object.py also contains _Freezable, a mixin for publicly freezable
objects.

values
------
bt_value API.

The classes in bt2.values are full Python wrappers of the native
bt_value types.

Features:

* BoolValue acts just like Python's bool.
* IntegerValue acts just like Python's int.
* FloatValue acts just like Python's float.
* StringValue acts just like Python's str.
* ArrayValue acts just like Python's list.
* MapValue acts just like Python's dict.
* bt_value_null is the equivalent of None.

In other words:

* All types are comparable and copyable (copy/deep copy).
* All the needed operators are implemented to make the value objects
  act like Python native objects.
* Number classes inherit ABCs in the numbers module.

There's also a bt2.create_value() function which returns a bt2.values
object from any value (bt2.values or native Python object).

I decided to wrap actual bt_value objects instead of always converting
from native Python objects to them and vice versa, because they can
still be shared in this case. Otherwise the conversion would remove this
sharing feature which is implicit with BT reference counting.

A few examples:

    my_int = bt2.IntegerValue(23)
    my_int += 283
    print(my_int)
    some_flt = bt2.FloatValue(45.3)
    print(my_int * some_flt)

    s = bt2.create_value('hello there')
    print(s[3:9])

    my_map = bt2.create_value({'ho': 23, 'meow': (4, 5, False, None)})
    print(my_map)
    print(my_map['meow'][1] >= 5)
    print(my_map.addr)

    for k, v in my_map.items():
        print('{}: {}'.format(k, v))

field_types
-----------
bt_ctf_field_type API.

This looks pretty much like the original field type objects of
the `babeltrace.writer` module, except for the following features:

* `Declaration` suffix is replaced with `FieldType` suffix to match
  the C API convention.

* Copy, deep copy, and comparison support.

* You can pass all the properties of an object at construction time:

      int_ft = bt2.IntegerFieldType(size=23, align=16, is_signed=True,
                                    base=8, mapped_clock_class=cc)

* Enumeration field type honors the sequence protocol:

      for mapping in enum_ft:
          print(mapping.name, mapping.lower, mapping.upper)

* Enumeration field type mapping iterator support, e.g.:

      for mapping in enum_ft.mappings_by_name('APPLE'):
          print(mapping.name, mapping.lower, mapping.upper)

  It's easy to add the mappings of another enumeration field type:

      enum_ft += other_enum_ft

* EnumerationFieldType inherits IntegerFieldType so that you can do:

      enum_ft = bt2.EnumerationFieldType(size=23, align=16,
                                         is_signed=True,
                                         base=bt2.Base.HEXADECIMAL,
                                         byte_order=bt2.ByteOrder.BIG_ENDIAN)
      print(enum_ft.size)
      enum_ft.is_signed = False

  instead of getting the underlying integer field type object manually.

* Structure and variant field types honor the mapping protocol:

      for name, ft in struct_ft:
          print(name, ft)

* You can set the `min_alignment` property of a structure field type
  (but you cannot get it), and you can get its `alignment` property
  (but you cannot set it). Those names represent exactly what they
  mean (less ambiguous than the C API equivalent IMO).

* You can instantiate a field object from a field type object by
  "calling" the field type object. This is closer to the concept of
  a class in the Python world:

      my_field = int_ft()

fields
------
bt_ctf_field API.

A bt2._Field is the result of instantiating a field type object. The
type (and all its subclasses) starts with an underscore because you
cannot instatiate them directly (possibly with an initial value):

    int_field = bt2.IntegerFieldType(32)(17)
    str_field = bt2.StringFieldType()('hello there')

Features:

* Copy, deep copy, and comparison support.
* IntegerField and EnumerationField act just like Python's int.
* FloatingPointNumberField acts just like Python's float.
* StringField acts just like Python's str.
* ArrayField and SequenceField honor the mutable sequence protocol.
* StructureField honors the mutable mapping protocol.

Field objects are just like value objects: they act like native Python
objects:

    int_field = bt2.IntegerFieldType(32)(152)
    int_field += 194
    print(int_field % 51)

    str_field = bt2.StringFieldType()('hello there')
    print(len(str_field))
    str_field += ' World!'
    print(str_field)

    print(struct_field['oh']['noes'][23])

    print(variant_field.selected_field)

    for mapping in enum_field.mappings:
        print(mapping.name, mapping.lower, mapping.upper)

clock_class
-----------
bt_ctf_clock_class API.

A straightforward clock class wrapper, pretty much equivalent to the
previous one (CTFWriter.Clock), except that:

* Copy, deep copy, and comparison support.

* You can pass all the properties of an object at construction time:

      cc = bt2.ClockClass('my_clock', frequency=18000000,
                          is_absolute=True, precision=500,
                          offset=bt2.ClockClassOffset(seconds=22,
                                                      cycles=187232))

* A clock offset is represented with a ClockClassOffset object.

* You can create a clock value from a clock class object with a given
  number of cycles:

      clock_val = cc.create_clock_value(234)

  This clock value object is copyable, deep-copyable, and comparable.
  You can get its number of cycles (raw value), its clock class,
  and the number of nanoseconds since Epoch:

      print(clock_val.ns_from_epoch())

event_class
-----------
bt_ctf_event_class API.

Features:

* Copy, deep copy, and comparison support.

* You can pass all the properties of an object at construction time:

      ec = bt2.EventClass('my_event', id=23, payload_field_type=ft)

* Parent stream class access (returns None if not set):

      print(ec.stream_class.id)

* Attributes property which honor the mutable mapping protocol:

      event_class.attributes['model.emf.uri'] = 'http://diamon.org/'

* Payload and context field type R/W properties.

* Call the class to instantiate an event:

      my_event = my_event_class()

stream_class
------------
bt_ctf_stream_class API.

Features:

* Copy, deep copy, and comparison support.

* You can pass all the properties of an object at construction time:

      sc = bt2.StreamClass(name='my_stream_class',
                           event_header_field_type=ev_header_ft,
                           event_classes=(ec1, ec2, ec3))

* Parent trace access (returns None if not set):

      print(sc.trace)

* A stream class object honors the mapping protocol to access its
  event class children by name:

      ec = sc['my_event']

      for ec_name, ec in sc.items():
          print(ec_name, ec.id)

* Packet context, event header, and stream event context field type R/W
  properties.

* Call the class to instantiate a stream:

      my_stream = my_stream_class('optional_name')

trace
-----
bt_ctf_trace API.

Features:

* Copy, deep copy, and comparison support.

* You can pass all the properties of an object at construction time:

      trace = bt2.Trace(name='my_trace',
                        native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
                        env={'tracer_name': 'BestTracer', 'custom': 23},
                        packet_header_field_type=pkt_head_ft,
                        clock_classes=(cc1, cc2),
                        stream_classes=(sc1, sc2))

* A trace object honors the mapping protocol to access its stream
  class children by ID:

      sc = trace[23]

      for sc_id, sc in trace.items():
          print(sc_id, len(sc))

* Trace environment honors the mutable mapping protocol:

      trace.env['tracer_major'] = 1
      trace.env['tracer_minor'] = 2
      trace.env['uname_r'] = '4.7.2-1-ARCH'

      for k, v in trace.env.items():
          print(k, v)

* Trace clock classes honor the mapping protocol:

      cc = trace.clock_classes['my_clock']

      for cc_name, cc in trace.clock_classes.items():
          print(cc_name, cc.frequency)

* Packet header field type R/W property.

event
-----
bt_ctf_event API.

Features:

* Copy, deep copy, and comparison support.

* Event class, name, ID, and stream read-only properties.

* Event object implements __getitem__() to retrieve a field in different
  scopes:

      # can be found in context, if not in payload, for example
      my_event['cpu_id']

  This is the same behaviour as in the `babeltrace` package. Use the
  specific field properties instead of a field_with_scope() method:

      print(my_event.context_field['specific'])

* Packet property to set and get the event's packet:

      event.packet = my_packet

* Header, stream event context, context, and payload field R/W
  properties.

* You can assign a clock value mapped to a specific clock class and
  get it back:

      event.set_clock_value(some_clock_value)
      print(event.get_clock_value(some_cc).ns_from_epoch)

stream
------
bt_ctf_stream API.

This module defines a base class (_StreamBase) for _Stream (non-writer
stream) and _CtfWriterStream (writer stream).

Features:

* Copy, deep copy, and comparison support.

* You can create a packet from a non-writer stream:

      packet = stream.create_packet()

packet
------
bt_ctf_packet API.

Features:

* Copy, deep copy, and comparison support.

* Stream read-only property (gives back the stream object which
  created it).

* Packet context and packet header field R/W properties.

notification
------------
bt_notification API.

The classes in this module wrap their equivalent in the C API in a
pretty straightfoward way.

notification_iterator
---------------------
bt_notification_iterator API.

A notification iterator object is always created from a source/filter
component object:

    source_component.create_notification_iterator()

A notification iterator object has a next() method to go to the next
notification, and a `notification` property to get the current
notification:

    notif_iter.next()
    print(notif_iter.notification)

The next() method can raise:

* bt2.Stop: End of the iteration (inherits StopIteration).
* bt2.UnsupportedFeature: Unsupported feature.
* bt2.Error: Any other error.

A notification iterator also honors the iterator protocol, that is, you
can use it like any Python iterator:

    for notif in notif_iter:
        print(notif)

Note that the iteration can still raise bt2.UnsupportedFeature or
bt2.Error in this scenario (bt2.Stop stops the iteration: it's not
raised outside the iteration).

You can use the seek_to_time() method to make a notification iterator
seek to a specific time.

The `component` property of a notification iterator returns the original
source/filter component which was used to create it.

You can create your own notification iterator class (to be used by your
own source/filter component class) by inheriting
bt2.UserNotificationIterator. This asks you to write your own _next()
and _get() methods which are eventually called by
bt_notification_iterator_next() and
bt_notification_iterator_get_notification(). You can also define an
__init__() method, a _destroy() method, and a _seek_to_time() method.

Minimal user notification iterator class:

    class MyIterator(bt2.UserNotificationIterator):
        def _get(self):
            # ...

        def _next(self):
            # ...

Your _next() method can raise bt2.Stop to signal the end of the
iteration. If it raises anything else, bt_notification_iterator_next()
returns BT_NOTIFICATION_ITERATOR_STATUS_ERROR to its caller. Since
the C API user only gets a status from this function, any exception
value is lost during this translation. However the C next method could
still log this value and the traceback in verbose mode.

Your _get() method can raise anything so that the caller of
bt_notification_iterator_get_notification() gets an error status.

Complete user notification iterator class:

    class MyIterator(bt2.UserNotificationIterator):
        def __init__(self):
            # Called when the user calls
            # bt_component_source_create_notification_iterator() or
            # bt_component_filter_create_notification_iterator().
            # Anything you raise here makes this function return
            # NULL (creation error).

        def _get(self):
            # ...

        def _next(self):
            # ...

        def _seek_to_time(self, origin, time):
            # You can raise anything or bt2.UnsupportedFeature.
            # `origin` is one of the values of
            # bt2.NotificationIteratorSeekOrigin.

        def _destroy(self):
            # This is called when the actual native BT notification
            # iterator object is destroyed. Anything you raise here
            # is ignored. You cannot use __del__() for this for
            # implementation reasons.

You CANNOT manually instantiate a user notification iterator, e.g.:

    my_iter = MyIterator()

This makes no sense because a notification iterator is always created by
a source/filter component. It probably won't work anyway because
bt2.UserNotificationIterator.__new__() expects a SWIG pointer and your
__init__() probably does not.

component
---------
bt_component_class and bt_component APIs.

This is where the fun begins.

All component objects have the following properties:

* name: Component's name or None.
* component_class: Component class object.

Source components have:

* create_notification_iterator(): Returns a new notification
  iterator object.

Filter components have:

* create_notification_iterator(): Returns a new notification
  iterator object.
* add_notification_iterator(): Adds a notification iterator to the
  filter component.

Sink components have:

* consume(): Consumes notifications from its input notification
  iterators.
* add_notification_iterator(): Adds a notification iterator to the
  filter component.

A component class object has the following properties:

* name: Component class's name or None.
* description: Component class's description or None.

You can also call a component class object to create a component
object, with optional parameters and an optional component name:

    comp = comp_class(name='my_comp', params={'path': '/tmp/lel'})

The `params` argument is passed to bt2.create_value() so you can use
a direct *Value object or anything accepted by this utility function.

What is described above is the _generic_ part of components and
component classes. There's another part for user-defined component
classes. For a user of those classes, both generic and user-defined
classes expose the same interface. The relative complexity of how this
is achieved is justified by the great simplicity from the component
developer's perspective:

    class MySink(bt2.UserSinkComponent):
        def _consume(self):
            notif_iter = self._input_notification_iterators[0]
            notif = next(notif_iter)

            if isinstance(notif, bt2.TraceEventNotification):
                print(notif.event.name)

That's it: some kind of minimal sink component class. Note that
next(notif_iter) can raise bt2.Stop here which is passed to the eventual
caller of bt_component_sink_consume() as the BT_COMPONENT_STATUS_END
status.

Behind the scenes, bt2.UserSinkComponent uses _UserComponentType as its
metaclass. When the class itself is initialized, its metaclass checks if
the subclass has the required interface (depending on its base class,
bt2.UserSinkComponent in this case) and creates a bt_component_class
owned by the Python user class. This bt_component_class is associated to
the Python class thanks to a global GHashTable in the shared object
module (_native_bt.so). Both the key and the value are weak references.

The name of the created bt_component_class is the user class's name by
default (MySink above), but it can also be passed as a class argument.
The description of the created bt_component_class is the docstring of
the user class:

    class MySink(bt2.UserSinkComponent, name='another-name'):
        'this is a custom sink'

        def _consume(self):
            # ...

Source and filter user component classes need to specify a notification
iterator class to use when the user calls
bt_component_*_create_notification_iterator(). This is specified as
a class argument.

    class MyIterator(bt2.UserNotificationIterator):
        def __init__(self):
            # ...

        def _get(self):
            # ...

        def _next(self):
            # ...

        def _seek_to_time(self, origin, time):
            # ...

        def _destroy(self):
            # ...

    class MySource(bt2.UserSinkComponent,
                   notification_iterator_class=MyIterator):
        # no mandatory methods here for a source/filter component class

Note that, within the notification iterator methods, self.component
refers to the actual user Python object which was used to create the
iterator object. This is the way to access custom, component-wide data
when the notification iterator is created (self.component._whatever).

Optional methods for all user-defined component classes are:

    class AnyComponent(...):
        def __init__(self, params, name):
            # `params` is a *Value Python object (bt2.values module),
            # `name` is the optional (can be None) component name,
            # which you can also access as self.name at this point.

        def _destroy(self):
            # This is called when the actual native BT component
            # object is destroyed. Anything you raise here
            # is ignored. You cannot use __del__() for this for
            # implementation reasons.

Optional methods for filter and sink user-defined component classes
are:

    class FilterOrSinkComponent(...):
        def _add_notification_iterator(self, notif_iter):
            # This is called when a notification iterator is added to
            # the component (using bt_component_*_add_iterator()).

Additionally, the __init__() method of filter and sink component classes
can use the self._minimum_input_notification_iterator_count and
self._maximum_input_notification_iterator_count properties to set their
minimum and maximum number of allowed input notification iterators:

    class FilterOrSinkComponent(...):
        def __init__(self, params, name):
            self._maximum_input_notification_iterator_count = 10
            self._minimum_input_notification_iterator_count = 4

They can also use the self._input_notification_iterators property at the
appropriate time to get their connected input notification iterators.
This property honors the sequence protocol. For filter components, this
is most probably going to be used by the iterator class, as such:

    class MyIterator(bt2.UserNotificationIterator):
        def _get(self):
            # ...

        def _next(self):
            notif_iter = self.component._input_notification_iterators[0]
            # ...

The beauty of all this is that both a Python user and the C API side can
instantiate user components:

    Python:

        my_sink = MySink(params={'janine': 'sutto'})

        for _ in my_sink.consume():
            pass

    C API (provided you have access to the bt_component_class object
           created for the Python user class):

        my_sink = bt_component_create(my_sink_comp_class, NULL, params);

        while (true) {
            status = bt_component_sink_consume(my_sink);
            if (status == BT_COMPONENT_STATUS_END) {
                break;
            }
        }

This is possible thanks to the overridden metaclass's __call__() method:

* When a Python user instantiates a user-defined component class, the
  metaclass's __call__() method creates an uninitialized user component
  and calls bt_component_create_with_init_method_data(), giving to this
  function `self` (the uninitialized component).

  When the component initialization method is called with some init
  method data, it sets the bt_component pointer in the received Python
  object and calls its __init__() method so that its intialization
  happens within the bt_component_create_with_init_method_data() call
  (important because some functions cannot be called outside this
  function).

  If the user's __init__() method raises, the error is not cleared on
  the C side, so that the Python user who instantiates the component
  can catch the actual, original Python exception instead of getting
  a generic one.

  In this scenario, the created user component Python object OWNS its
  bt_component. The component is marked as NOT being owned by its
  bt_component:

      self._belongs_to_native_component = False

  The bt_component has the user Python object as its private data
  (borrowed reference).

* When a C user instantiates a user-defined Python component class, he
  calls bt_component_create(). Then the component initialization
  function for this class receives a NULL init method data and knows
  it is called from the C/generic side.

  The initialization method finds the corresponding Python component
  class thanks to the aforementioned global GHashTable. It calls it with
  the `__comp_ptr` keyword argument set to the native bt_component SWIG
  pointer and the `params` keyword argument set to the *Value object
  converted from the `params` parameter. This call (metaclass's
  __call__()), in this scenario, calls the user's __init__() method
  itself. This call returns a new component instance, which is set as
  the private data of the native bt_component.

  In this scenario, the created user component Python object as a
  borrowed reference to the native bt_component. The native bt_component
  OWNS the Python user's component:

      self._belongs_to_native_component = True

The self._belongs_to_native_component property is used for the following
situation:

    my_source = MySource()

    # At this point, my_source is a Python object which owns its
    # bt_component.

    notif_iter = my_source.create_notification_iterator()

    # notif_iter is a generic notification iterator (a dumb
    # bt_notification_iterator wrapper) here, not the actual user
    # Python notification iterator. This method only calls
    # bt_component_source_create_notification_iterator() and wraps the
    # returned pointer.

    del my_source

    # At this point, the Python reference count of the source component
    # object falls to zero. Its __del__() method is called. However
    # we don't want this object to be destroyed here, because it is
    # still needed by the user notification iterator. This __del__()
    # method, if self._belongs_to_native_component is false, inverts
    # the ownership, literally:
    #
    #     if not self._belongs_to_native_component:
    #         self._belongs_to_native_component = True
    #         native_bt.py3_component_on_del(self)
    #         native_bt.put(self._ptr)
    #
    # bt_py3_component_on_del() simply increments the given
    # Python object's reference count. With its reference count back
    # to 1, Python does not actually destroy the object. It is now
    # owned by the bt_component.

    del notif_iter

    # Now, the wrapper puts its bt_notification_iterator object. Its
    # reference count falls to zero. Its bt_component is put: its
    # reference count falls to zero. The user's (C) destroy method for
    # this component class decrements the reference count of its
    # private Python object (the same object referenced by my_source
    # above). __del__() is (possibly) called again, but is a no-op
    # now. Then the user's _destroy() method is called.

plugin
------
bt_plugin API.

You can create plugin objects with bt2.create_plugins_from_file().
This is the equivalent of bt_plugin_create_all_from_file(). You can
also use bt2.create_plugins_from_dir() which is the equivalent of
bt_plugin_create_all_from_dir().

The return value of those functions is a list of _Plugin objects.

Here's an example of printing all the event names of a CTF trace:

    import bt2
    import sys

    def print_all():
        plugins = bt2.create_plugins_from_file(sys.argv[1])
        fs_cc = plugins[0].source_component_class('fs')
        fs_comp = fs_cc(params={'path': sys.argv[2]})
        notif_iter = fs_comp.create_notification_iterator()

        for notif in notif_iter:
            if isinstance(notif, bt2.TraceEventNotification):
                print(notif.event.name)

    print_all()

You would run this script like this:

    python3 script.py /path/to/ctf-plugin.so /path/to/trace

You can access the properties of a plugin:

    print(plugin.path)
    print(plugin.name)
    print(plugin.description)
    print(plugin.author)
    print(plugin.license)
    print(plugin.version)

A plugin object honors the sequence protocol:

    for comp_class in plugin:
        print(comp_class.name, comp_class.description)

ctf_writer
----------
Everything in this module is exclusive to the CTF writer API:

* CtfWriterClock (bt_ctf_clock)
* _CtfWriterStream (bt_ctf_stream, CTF writer interface only)
* CtfWriter (bt_ctf_writer)

I removed the CTFWriter.Writer.create_stream() method because it's the
equivalent of this:

    writer.trace.add_stream_class(sc)
    stream = sc()

This returns a _CtfWriterStream object.

Also removed is CTFWriter.Writer.add_environment_field() which you can
do like this now:

    writer.trace.env['name'] = value

The CTFWriter.Writer.byte_order property is now the `native_byte_order`
property of the CTF writer's trace:

    writer.trace.native_byte_order = bt2.ByteOrder.BIG_ENDIAN

CtfWriter.add_clock() expects a CtfWriterClock object.

__init__
--------
This imports * from each module, thus exposing only the public names of
each one.

It also defines the package's exceptions. bt2.CreationError is raised
when an object cannot be created. bt2.FrozenError is raised when an
operation fails because the object is frozen. This is only raised for
bt2.values objects since this API has a status to indicate the exact
error. bt2.Error is a general error.

The package also does this:

    import bt2.native_bt as _native_bt
    import atexit

    atexit.register(_native_bt.py3_cc_exit_handler)
    _native_bt.py3_cc_init_from_bt2()

bt_py3_cc_init_from_bt2() is used to import some bt2 modules and objects
on the C side and the exit handler, bt_py3_cc_exit_handler(), puts those
objects.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobt_ctf_event_set_clock_value() does not need a clock class
Philippe Proulx [Sat, 4 Feb 2017 08:37:54 +0000 (03:37 -0500)] 
bt_ctf_event_set_clock_value() does not need a clock class

Since a clock value has a clock class member, and you can
get it with bt_ctf_clock_value_get_class(), it's redundant
to specify both the clock class and the clock value when
you call bt_ctf_event_set_clock_value().

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd bt_ctf_clock_value_get_class()
Philippe Proulx [Sat, 4 Feb 2017 08:18:06 +0000 (03:18 -0500)] 
Add bt_ctf_clock_value_get_class()

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoInstall notification headers in babeltrace/component/notification
Philippe Proulx [Sat, 4 Feb 2017 03:33:00 +0000 (22:33 -0500)] 
Install notification headers in babeltrace/component/notification

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agocomponent.c: return NULL when there's no name, not ""
Philippe Proulx [Thu, 2 Feb 2017 06:12:35 +0000 (01:12 -0500)] 
component.c: return NULL when there's no name, not ""

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agocomponent.c: iterator init method is optional
Philippe Proulx [Wed, 1 Feb 2017 19:15:49 +0000 (14:15 -0500)] 
component.c: iterator init method is optional

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoWriter fix: set packet size to 0 on flush failure
Jérémie Galarneau [Fri, 3 Feb 2017 02:21:26 +0000 (21:21 -0500)] 
Writer fix: set packet size to 0 on flush failure

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoWriter: only reset automatically-set fields
Jérémie Galarneau [Fri, 3 Feb 2017 02:21:01 +0000 (21:21 -0500)] 
Writer: only reset automatically-set fields

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoWriter: introduce try_set_structure_integer_field
Jérémie Galarneau [Fri, 3 Feb 2017 02:20:08 +0000 (21:20 -0500)] 
Writer: introduce try_set_structure_integer_field

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoWriter: set the discarded events's value before serializing
Jérémie Galarneau [Thu, 2 Feb 2017 17:57:57 +0000 (12:57 -0500)] 
Writer: set the discarded events's value before serializing

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoWriter: validating the packet header is not necessary
Jérémie Galarneau [Thu, 2 Feb 2017 17:54:49 +0000 (12:54 -0500)] 
Writer: validating the packet header is not necessary

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoTests: add empty packet writer test
Jérémie Galarneau [Fri, 3 Feb 2017 01:37:54 +0000 (20:37 -0500)] 
Tests: add empty packet writer test

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoFix: only serialize a field if it is set
Jérémie Galarneau [Fri, 3 Feb 2017 01:37:27 +0000 (20:37 -0500)] 
Fix: only serialize a field if it is set

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoFix: log structure field name on serialization failure
Jérémie Galarneau [Thu, 2 Feb 2017 17:23:09 +0000 (12:23 -0500)] 
Fix: log structure field name on serialization failure

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoTests: rename empty packet context test
Jérémie Galarneau [Thu, 2 Feb 2017 00:05:27 +0000 (19:05 -0500)] 
Tests: rename empty packet context test

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoRename *create_iterator*() -> *create_notification_iterator*()
Philippe Proulx [Tue, 31 Jan 2017 07:50:22 +0000 (02:50 -0500)] 
Rename *create_iterator*() -> *create_notification_iterator*()

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd bt_component_*_create_iterator_with_init_method_data()
Philippe Proulx [Tue, 31 Jan 2017 07:45:14 +0000 (02:45 -0500)] 
Add bt_component_*_create_iterator_with_init_method_data()

This is analogous to bt_component_create_with_init_method_data(),
but for notification iterators.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoRemove unused bt_component_class_filter_init_iterator_method
Philippe Proulx [Tue, 31 Jan 2017 07:44:11 +0000 (02:44 -0500)] 
Remove unused bt_component_class_filter_init_iterator_method

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoTests: close writer object before reading the trace produced
Jérémie Galarneau [Mon, 30 Jan 2017 23:47:04 +0000 (18:47 -0500)] 
Tests: close writer object before reading the trace produced

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoFix: missing NULL check in ctf_fs_iterator_destroy_data
Jérémie Galarneau [Mon, 30 Jan 2017 23:31:18 +0000 (18:31 -0500)] 
Fix: missing NULL check in ctf_fs_iterator_destroy_data

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoTest: remove produced trace at the end of the empty packet test
Jérémie Galarneau [Mon, 30 Jan 2017 23:29:17 +0000 (18:29 -0500)] 
Test: remove produced trace at the end of the empty packet test

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoClarify plug-in ABI version logging on plugin load
Jérémie Galarneau [Mon, 30 Jan 2017 22:42:08 +0000 (17:42 -0500)] 
Clarify plug-in ABI version logging on plugin load

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd missing .gitignore entries
Jérémie Galarneau [Mon, 30 Jan 2017 22:40:45 +0000 (17:40 -0500)] 
Add missing .gitignore entries

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoTests: add the plugin tests to the make check target
Jérémie Galarneau [Mon, 30 Jan 2017 22:25:03 +0000 (17:25 -0500)] 
Tests: add the plugin tests to the make check target

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoFix: add missing NULL check in ctf_fs_destroy_data
Jérémie Galarneau [Mon, 30 Jan 2017 22:24:29 +0000 (17:24 -0500)] 
Fix: add missing NULL check in ctf_fs_destroy_data

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoFix Makefile.am files regarding headers
Philippe Proulx [Fri, 27 Jan 2017 18:22:48 +0000 (13:22 -0500)] 
Fix Makefile.am files regarding headers

Header files for convenience library and private targets
can be listed in _SOURCES, and they must all be listed for
the distribution to work.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoRename input.h -> component-input-internal.h
Philippe Proulx [Fri, 27 Jan 2017 17:43:58 +0000 (12:43 -0500)] 
Rename input.h -> component-input-internal.h

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoSet notification iterator methods to the component class
Philippe Proulx [Fri, 27 Jan 2017 08:34:18 +0000 (03:34 -0500)] 
Set notification iterator methods to the component class

This follows the spirit of the previous refactorings by assigning
notification iterator methods to the component class instead of setting
them during their initialization method.

Conceptually here, a (source and filter) component class defines one,
and only one notification iterator class. Therefore the concept of an
iterator "class" is hidden to the user here, since there's no
one-to-many relationship. In any OO language, an iterator class would be
a class nested under a component class.

Source and filter component classes are created with two mandatory
iterator methods: get and next.

The initialization, destroy, and seek time iterator methods are
optional.

New functions:

* bt_component_class_source_set_notification_iterator_init_method()
* bt_component_class_source_set_notification_iterator_destroy_method()
* bt_component_class_source_set_notification_iterator_seek_time_method()
* bt_component_class_filter_set_notification_iterator_init_method()
* bt_component_class_filter_set_notification_iterator_destroy_method()
* bt_component_class_filter_set_notification_iterator_seek_time_method()

The plugin development interface (babeltrace/plugin/plugin-dev.h) is
updated accordingly.

Tests and existing plugins are updated accordingly.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoplugin-dev.h: put selector (type) close to union in structures
Philippe Proulx [Thu, 26 Jan 2017 22:14:27 +0000 (17:14 -0500)] 
plugin-dev.h: put selector (type) close to union in structures

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoPlugins: use UNUSED_VAR instead of casting to void
Philippe Proulx [Thu, 26 Jan 2017 22:09:53 +0000 (17:09 -0500)] 
Plugins: use UNUSED_VAR instead of casting to void

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd bt_component_create_with_init_method_data()
Philippe Proulx [Thu, 26 Jan 2017 10:22:16 +0000 (05:22 -0500)] 
Add bt_component_create_with_init_method_data()

This function has one more parameter than bt_component_create():
custom user data (void *) which is passed directly to the
init. method (if any) of the used component class. Nothing else
is done by the lib with this data.

bt_component_create() is a specific version of
bt_component_create_with_init_method_data() which passes NULL
as the init. method data.

You can use this custom data to exchange specific data between
an application and a component class. It is not recommended to
rely on this function when you implement Babeltrace plugins.

A component initialization method now has an additional parameter,
init_method_data.

This initialization method data is not related at all to a
component's private data: you can set the init. method data as
the component's private data, but it's just a design choice.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agobabeltrace.c: print plugin version when available
Philippe Proulx [Thu, 26 Jan 2017 10:05:11 +0000 (05:05 -0500)] 
babeltrace.c: print plugin version when available

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd tests for plugin version
Philippe Proulx [Thu, 26 Jan 2017 09:57:04 +0000 (04:57 -0500)] 
Add tests for plugin version

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd plugin (user) version information
Philippe Proulx [Thu, 26 Jan 2017 09:54:47 +0000 (04:54 -0500)] 
Add plugin (user) version information

This patch adds the BT_PLUGIN_VERSION_WITH_ID() and BT_PLUGIN_VERSION()
macros to the plugin development API (babeltrace/plugin/plugin-dev.h)
for the user to set a custom version (major, minor, patch, extra).

You can use the new bt_plugin_get_version() function to retrieve the
plugin's version, if set.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd bt_component_class_freeze()
Philippe Proulx [Thu, 26 Jan 2017 09:29:52 +0000 (04:29 -0500)] 
Add bt_component_class_freeze()

It is somewhat essential for a component class provider (plugin or
whatever else) to make sure that its created, shared component class is
not modified once properly configured.

The new bt_component_class_freeze() function freezes a component class
object so that it becomes immutable.

bt_component_create() also has the side effect of freezing its class.
This is analogous to bt_event_create() which freezes its class.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoPrefix {source,filter,sink}*.h file names with component-
Philippe Proulx [Thu, 26 Jan 2017 09:11:30 +0000 (04:11 -0500)] 
Prefix {source,filter,sink}*.h file names with component-

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoRefactor the component class and component API
Philippe Proulx [Thu, 26 Jan 2017 09:03:19 +0000 (04:03 -0500)] 
Refactor the component class and component API

This patch modifies the component class and component API so that the
user sets methods to the component class objects, not to the component
objects. This makes sense following the typical object-oriented
paradigm.

Changes:

* bt_component_class_create() is removed.

  You need to use a component class-specific function to create one,
  amongst:

  * bt_component_class_source_create()
  * bt_component_class_filter_create()
  * bt_component_class_sink_create()

  All the parameters of those functions are mandatory: they are the
  name and the mandatory _methods_ of the class, depending on the
  type.

* Component class-specific functions are declared in their own header:

  * babeltrace/component/component-class-source.h
  * babeltrace/component/component-class-filter.h
  * babeltrace/component/component-class-sink.h

  babeltrace/component/component-class.h only contains functions which
  you can use on any component class, whatever the type.

* enum bt_component_type and BT_COMPONENT_TYPE_* are renamed to
  enum bt_component_class_type and BT_COMPONENT_CLASS_TYPE_* since
  the type is a property of the component class.

* Once a component class is created, you can use functions to set
  optional methods:

  * bt_component_class_filter_set_add_iterator_method()
  * bt_component_class_sink_set_add_iterator_method()

  The component initialization and destroy methods are now both
  optional:

  * bt_component_class_set_init_method()
  * bt_component_class_set_destroy_method()

  You can also set the optional description with a function:

  * bt_component_class_set_description()

* New public utility function: bt_component_get_class_type(): returns
  the type of a component's class.

* Component functions which are specific to a class type are moved to
  their respective header in babeltrace/component
  (source.h, filter.h, sink.h).

* Plugin development interface (babeltrace/plugin/plugin-dev.h) is
  updated to follow the API changes. Component class descriptor macros
  contain the component class type in their name, for example:

      BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION()
      BT_PLUGIN_SOURCE_COMPONENT_CLASS_DESTROY_METHOD()
      BT_PLUGIN_FILTER_COMPONENT_CLASS_ADD_ITERATOR_METHOD()

* Existing plugins, tests, and the command-line converter are updated to
  follow the API changes.

* Other very minor fixes.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoMove enum bt_component_type to component.h
Philippe Proulx [Tue, 24 Jan 2017 17:44:49 +0000 (12:44 -0500)] 
Move enum bt_component_type to component.h

enum bt_component_type starts with bt_component, thus it
belongs to component.h.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoRefactor the plugin registration and loading machinery
Philippe Proulx [Sat, 21 Jan 2017 06:16:20 +0000 (01:16 -0500)] 
Refactor the plugin registration and loading machinery

This patch refactors the plugin registration and loading machinery: the
different, specific symbol names found in the shared object are dropped
in favor of two sections.

The goals of this refactoring are:

1. To allow optional plugin attributes now and in the future.
2. To allow optional component class attributes now and in the future.
3. To make backward and forward compatibility easier.
4. To simplify the plugin development API from a user's perspective.
5. To use the same mechanism to load plugins from a shared object and
   statically from the `babeltrace` binary.

You can still find the macros used to define plugins, plugin attributes,
and component classes in `babeltrace/plugin/plugin-dev`.

We introduce four (IV) new structures to the plugin subsystem:

* A *plugin descriptor* describes a single plugin. Note that one shared
  object can contain one or more plugin descriptors, thus it can contain
  one or more plugins.

  A plugin descriptor contains the interface version of the plugin it
  describes as well as its name (a C string).

  The BT_PLUGIN_WITH_ID() macro creates a plugin descriptor structure
  with a specific identifier in its variable name. It also creates a
  pointer to this structure which is added to the plugin descriptor
  section (`__bt_plugin_descriptors`).

  The BT_PLUGIN() macro is an easy-to-use version of
  BT_PLUGIN_WITH_ID(): it accepts a C identifier which is the name of
  the plugin. Its plugin descriptor ID is `auto`.

* A *plugin descriptor attribute* is an optional attribute attached to a
  specific plugin descriptor.

  A plugin descriptor attribute structure has a type (its key) as well
  as a value (the value's type depends on the attribute type). For debug
  purposes, we also put a string in this structure which is the name of
  the attribute type. This can be used by the plugin subsystem to give
  more information when it warns that an attribute with an unknown type
  was found (use case: current Babeltrace loading a future plugin).

  A plugin descriptor attribute also has a member pointing to its
  "parent" plugin descriptor.

  The following macros each create a plugin descriptor attribute
  structure and a pointer to this structure which is added to the plugin
  attribute section. Each macro accepts a plugin descriptor ID (to
  attach the attribute to the appropriate descriptor) and a value:

  * BT_PLUGIN_INIT_WITH_ID():        Initialization function.
  * BT_PLUGIN_EXIT_WITH_ID():        Exit function.
  * BT_PLUGIN_AUTHOR_WITH_ID():      Author.
  * BT_PLUGIN_LICENSE_WITH_ID():     License.
  * BT_PLUGIN_DESCRIPTION_WITH_ID(): Description.

  You can use the macros above in any order, but you need to use them
  after BT_PLUGIN_INIT*().

  There's an equivalent, easy-to-use macro for each of the macros above
  without the `_WITH_ID` suffix which uses the `auto` plugin descriptor.

* A *component class descriptor* describes a single component class
  attached to a specific plugin descriptor.

  The structure contains the mandatory attributes needed to build a
  component class (name, component type, component initialization
  function).

  The BT_PLUGIN_COMPONENT_CLASS_WITH_ID() macro creates a component
  class descriptor structure with a specific component class descriptor
  identifier in its variable name. The component source type is also
  part of the variable name, as well as the specified plugin descriptor
  ID. The macro also creates a pointer to this structure which is added
  to the component class descriptor section
  (`__bt_plugin_component_descriptors`).

  The easy-to-use BT_PLUGIN_COMPONENT_CLASS() macro uses the `auto`
  plugin descriptor and the name of the component class as the component
  class descriptor ID (a C identifier in this version, to allow this).

  With this new mechanism, the BT_PLUGIN_COMPONENT_CLASSES_BEGIN and
  BT_PLUGIN_COMPONENT_CLASSES_END macros do not exist anymore: you can
  use BT_PLUGIN_COMPONENT_CLASS*() macros anywhere, in any order, as
  long as they appear after BT_PLUGIN_INIT*().

* A *component class descriptor attribute* is an optional attribute
  attached to a specific component class descriptor.

  A component class descriptor attribute structure has a type (its key)
  as well as a value (the value's type depends on the attribute type).
  For debug purposes, we also put a string in this structure which is
  the name of the attribute type.

  A component class descriptor attribute also has a member pointing to
  its "parent" component class descriptor (which itself, as stated
  above, as a member pointing to its "parent" plugin descriptor).

  As of this patch, the only available component class descriptor
  attribute is its optional description. The
  BT_PLUGIN_COMPONENT_CLASS_DESCRIPTION_WITH_ID() macro creates such an
  attribute. BT_PLUGIN_COMPONENT_CLASS_DESCRIPTION() is the easy-to-use
  version which uses the `auto` plugin descriptor.

  You need to place BT_PLUGIN_COMPONENT_CLASS_DESCRIPTION*() after
  the associated BT_PLUGIN_COMPONENT_CLASS*().

We also add the macro BT_PLUGIN_DECLARE() to declare an `extern` plugin
descriptor structure to add plugin attributes to the same plugin
descriptor from different compilation units. Thanks to this, the simple
fact of linking a given object file or not to a shared object is enough
to conditionally add or not one or more component classes.

This approach also has the somewhat interesting side effect of refusing
to build if you define a plugin descriptor attribute without using
BT_PLUGIN_INIT*() (missing name) or if you define the same attribute
twice, since the macros create actual variables which must have unique
names.

When the plugin subsystem loads a shared object, it looks for the
beginning and end symbols for the four sections:

* Plugin descriptors.
* Plugin descriptor attributes.
* Component class descriptors.
* Component class descriptor attributes.

Those are enough to provide everything that is needed to create a plugin
object. A plugin's initialization function is called first, and then its
component classes are created and added sequentially according to the
discovered component class descriptors.

Since a single shared object file can now contain more than one plugin
(descriptor), the following function:

    struct bt_plugin *bt_plugin_create_from_file(const char *path);

is changed to this:

    struct bt_plugin **bt_plugin_create_all_from_file(const char *path);

In other words, the function returns a NULL-terminated array of plugin
objects, just like bt_plugin_create_append_all_from_dir() does.

All the existing plugins and tests are updated to follow the changes.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd bt_plugin test
Philippe Proulx [Thu, 19 Jan 2017 18:18:55 +0000 (13:18 -0500)] 
Add bt_plugin test

This new test proves that the bt_plugin subsystem works as expected.

There are three Babeltrace plugins built in
`tests/lib/test-plugin-plugins`. They are:

* `minimal`: Minimal, valid plugin with no component classes.
* `sfs`: Valid plugin with a source, a filter, and a sink component
  class.
* `invalid`: Invalid plugin (missing name).

Verified in this test:

* Behaviour of bt_plugin_*() functions with non-existing paths, invalid
  arguments, etc.

* bt_plugin_create_from_file() can load a valid plugin and we can access
  all its properties.

* The initialization and exit functions of a plugin are called when
  expected.

* We can access the component classes of a plugin which provides some.

* We can still create a component from a component class provided by
  a plugin after the associated plugin object is destroyed, that is, the
  associated shared library handle is not closed until it is known that
  no more user code found in the loaded object will ever be executed
  again in the future.

* bt_plugin_create_all_from_dir() works as expected.

As of this patch, Valgrind shows for this test:

    ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoDecouple component class from plugin subsystem, remove component factory
Philippe Proulx [Wed, 18 Jan 2017 18:30:07 +0000 (13:30 -0500)] 
Decouple component class from plugin subsystem, remove component factory

This is a refactoring of the component class and plugin subsystems of
Babeltrace 2. There's not much new code, it's mostly moved from one
file to another and some functions are renamed.

The goal of this patch is to decouple the component class objects from
plugin objects, that is, remove the dependency from the component class
to the plugin. This reference is not necessary, as component classes
can be provided by many sources, a plugin being one of them. A similar
plugin subsystem could be implemented outside of the Babeltrace library
with plugin-agnostic component class objects.

Also the component factory concept is completely removed. At the end
of this refactoring, it's nothing more than a hash table, so the name
is bad, and the object itself is just a very basic util.

Summary of changes:

* You can create a bt_component_class object manually with
  bt_component_class_create(). The function accepts the same parameters
  as before, except for a plugin reference.

* Internal code can use bt_component_class_add_destroy_listener() to add
  a custom destroy listener to a component class. Destroy listeners are
  called in reverse order just before the component class is destroyed
  when its last reference is dropped.

  This is needed to implement the plugin subsystem. It could be made
  public eventually to allow a custom component class provider system
  similar to the Babeltrace plugin subsystem. It could be generalized to
  bt_object_add_destroy_listener() or bt_add_destroy_listener().

* bt_component_class_get_plugin() is removed.

* There are three functions to create a bt_plugin object (in
  `babeltrace/plugin/plugin.h`):

  * bt_plugin_create_from_file(): Accepts a path and creates a plugin
    object from this single file. This must be a `.so` or `.la` file for
    the moment, but eventually we can support other plugins like Python
    plugins `.py`, Windows DLLs `.dll`, etc.

  * bt_plugin_create_all_from_dir(): Traverses a directory, optionally
    recursively, and creates one plugin object for each shared library
    found. Errors are ignored in this function because it's possible
    that a shared library in this directoy is not a Babeltrace plugin
    and we still want to continue the search.

    The return value is a NULL-terminated array of bt_plugin objects
    which the caller must free. Each plugin object in this array has its
    reference count set to 1.

  * bt_plugin_create_all_from_static(): Loads all the plugins found in
    the static sections of the Babeltrace binary. The return value has
    the same format as with bt_plugin_create_all_from_dir().

  There is no way to create an "empty", fresh bt_plugin object. It's
  always created from an existing file in the end.

* A bt_plugin object is a simple provider of component classes. Once
  it's created with one of the three functions above, you can access its
  global properties (name, license, description, etc.), as well as its
  component classes with bt_plugin_get_component_class_count(),
  bt_plugin_get_component_class(), and
  bt_plugin_get_component_class_by_name_and_type().

  The initialization function of a plugin can add new component classes
  to a bt_plugin object (passed as a parameter) thanks to the new
  bt_plugin_add_component_class() function. This function is exclusive
  to a plugin's initialization stage: once the initialization is done,
  the plugin is marked as frozen, and you cannot call
  bt_plugin_add_component_class() again on this plugin. This ensures
  that all the contained component classes were created by the plugin's
  code itself.

  There's a mechanism which involves a global hash table of
  component class adresses to shared library handles and a custom
  component class destroy listener to ensure that, even if a bt_plugin
  object is destroyed (reference count falls to zero), its associated
  shared library is not closed until all its component classes are
  also destroyed. See plugin.c:89 for more details.

* All the headers related to components, component classes, and
  notifications are moved to `babeltrace/component`. Plugin-specific
  headers are in `babeltrace/plugin`.

* Plugin development macros are in `babeltrace/plugin/plugin-dev.h`
  (instead of `plugin-macros.h`). This is the header that any plugin's
  main source file must include.

  * Two new function typedefs in `plugin-dev.h`:

    * bt_plugin_init_func: plugin's initialization function which
      accepts a plugin object to which to add component classes.

      You can set such a function with BT_PLUGIN_INIT().

    * bt_plugin_exit_func: plugin's exit function, if anything global to
      the plugin must be freed/released.

      This is not called when the bt_plugin object is destroyed: because
      component classes could still be alive when this happens, it's
      called just before the shared library is closed (when it is
      guaranteed that no user code found in this plugin will be called
      in the future of this process).

      You can set such a function with BT_PLUGIN_EXIT().

  * The BT_PLUGIN_COMPONENT_CLASS_*_ENTRY() macros create a component
    class of the associated type with bt_component_class_create(), and
    add it to the plugin object with bt_plugin_add_component_class().

    You can also do this manually in a custom initialization function.
    When you use BT_PLUGIN_COMPONENT_CLASSES_BEGIN and
    BT_PLUGIN_COMPONENT_CLASSES_END, BT_PLUGIN_INIT() and
    BT_PLUGIN_EXIT() (no-op exit function) are automatically used.

* Everything found in `babeltrace/plugin/plugin-system.h` is moved to
  the appropriate headers, depending on the types of the objects.

* Plugins, tests, and internal code are updated to use the new macros
  and header files.

* The converter is updated to use the updated subsystems instead of
  relying on a component factory.

  A global GLib pointer array is used to keep the currently loaded
  plugins. This is used instead of the component factory. When a new
  plugin is found, we check in this array if it was already added (same
  name) from another file or statically before adding it.

  The static find_plugin() and find_component_class() are the equivalent
  of the component factory interface.

  print_component_classes_found() also prints the number of loaded
  plugins.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoref.h: doc: fix typo
Philippe Proulx [Thu, 26 Jan 2017 17:32:22 +0000 (12:32 -0500)] 
ref.h: doc: fix typo

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoWriter: don't allow more than one packet without packet context
Jérémie Galarneau [Fri, 27 Jan 2017 05:41:39 +0000 (00:41 -0500)] 
Writer: don't allow more than one packet without packet context

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoTests writer: write a trace defining no packet context
Jérémie Galarneau [Fri, 27 Jan 2017 04:15:33 +0000 (23:15 -0500)] 
Tests writer: write a trace defining no packet context

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoWriter: minimize packet padding
Jérémie Galarneau [Fri, 27 Jan 2017 04:19:40 +0000 (23:19 -0500)] 
Writer: minimize packet padding

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoWriter: support traces defining no packet contexts
Jérémie Galarneau [Fri, 27 Jan 2017 04:18:19 +0000 (23:18 -0500)] 
Writer: support traces defining no packet contexts

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoUpdate test_ctf_writer.c header
Jérémie Galarneau [Thu, 26 Jan 2017 19:22:18 +0000 (14:22 -0500)] 
Update test_ctf_writer.c header

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoFix text plug-in: missing function name change
Jérémie Galarneau [Thu, 26 Jan 2017 17:00:21 +0000 (12:00 -0500)] 
Fix text plug-in: missing function name change

bt_ctf_field_type_enumeration_mapping_iterator_get_name was removed
and replaced by the bt_ctf_field_type_enumeration_mapping_iterator_get_signed
and bt_ctf_field_type_enumeration_mapping_iterator_get_unsigned
functions

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAPI doc: document include files and how to build
Philippe Proulx [Wed, 25 Jan 2017 19:35:09 +0000 (14:35 -0500)] 
API doc: document include files and how to build

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoref.h, values.h: doc: add #include in description
Philippe Proulx [Wed, 25 Jan 2017 19:08:17 +0000 (14:08 -0500)] 
ref.h, values.h: doc: add #include in description

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAPI doc: update main-page.dox
Philippe Proulx [Wed, 25 Jan 2017 08:34:15 +0000 (03:34 -0500)] 
API doc: update main-page.dox

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAPI doc: add more details about reference counting
Philippe Proulx [Wed, 25 Jan 2017 08:18:14 +0000 (03:18 -0500)] 
API doc: add more details about reference counting

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agovalues.h: doc: rephrase map foreach callback ref
Philippe Proulx [Wed, 25 Jan 2017 07:37:54 +0000 (02:37 -0500)] 
values.h: doc: rephrase map foreach callback ref

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoFix API doc's content and style for enum. FT mapping iterator
Philippe Proulx [Wed, 25 Jan 2017 01:11:53 +0000 (20:11 -0500)] 
Fix API doc's content and style for enum. FT mapping iterator

Also, remove bt_ctf_field_type_enumeration_mapping_iterator_get_name(),
since you can achieve the same with
bt_ctf_field_type_enumeration_mapping_iterator_get_signed() or
bt_ctf_field_type_enumeration_mapping_iterator_get_unsigned() with the
two last parameters set to NULL.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoFix: bt_config_init_from_args has no ownership of cfg
Jérémie Galarneau [Thu, 26 Jan 2017 01:19:49 +0000 (20:19 -0500)] 
Fix: bt_config_init_from_args has no ownership of cfg

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoFix: prepend to $program_transform_name instead overriding it
Michael Jeanson [Mon, 16 Jan 2017 22:34:34 +0000 (17:34 -0500)] 
Fix: prepend to $program_transform_name instead overriding it

This makes the configure options program-prefix and program-suffix work
as well as the renaming of babeltrace.bin to babeltrace at install time.

Signed-off-by: Michael Jeanson <mjeanson@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoDocs: document enumeration mapping iterator API
Jérémie Galarneau [Tue, 24 Jan 2017 04:41:18 +0000 (23:41 -0500)] 
Docs: document enumeration mapping iterator API

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoDocs: add documentation for bt_ctf_field_type_enumeration_find_mappings*
Jérémie Galarneau [Tue, 24 Jan 2017 02:31:05 +0000 (21:31 -0500)] 
Docs: add documentation for bt_ctf_field_type_enumeration_find_mappings*

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoAdd missing bt_ctf_field_type_enumeration_mapping_iterator declaration
Jérémie Galarneau [Tue, 24 Jan 2017 02:30:28 +0000 (21:30 -0500)] 
Add missing bt_ctf_field_type_enumeration_mapping_iterator declaration

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoRemove bt_ctf_field_type_enumeration_get_mapping_name
Jérémie Galarneau [Tue, 24 Jan 2017 02:30:06 +0000 (21:30 -0500)] 
Remove bt_ctf_field_type_enumeration_get_mapping_name

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoModify bt_ctf_field_enumeration_get_single_mapping to return iterator
Jérémie Galarneau [Tue, 24 Jan 2017 01:23:28 +0000 (20:23 -0500)] 
Modify bt_ctf_field_enumeration_get_single_mapping to return iterator

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoFix: memory leak when using mapping iterator
Jérémie Galarneau [Mon, 23 Jan 2017 23:59:10 +0000 (18:59 -0500)] 
Fix: memory leak when using mapping iterator

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoFix: check enum overlap as long as the type is not frozen
Jérémie Galarneau [Mon, 23 Jan 2017 20:49:29 +0000 (15:49 -0500)] 
Fix: check enum overlap as long as the type is not frozen

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 years agoFix: allow duplicate keys and overlapping ranges in enumerations
Mathieu Desnoyers [Fri, 16 Dec 2016 17:30:24 +0000 (18:30 +0100)] 
Fix: allow duplicate keys and overlapping ranges in enumerations

This changes the field and field types IR API. It adds a validation to
the variant that checks if there are overlapping ranges in its
enumeration tag.

Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
This page took 0.057092 seconds and 4 git commands to generate.