From: Philippe Proulx Date: Wed, 6 Sep 2017 19:15:16 +0000 (-0400) Subject: Add CONTRIBUTING.adoc X-Git-Url: http://git.efficios.com/?p=babeltrace.git;a=commitdiff_plain;h=f9ded0e0b7cec670982963369284612ec1a251c7 Add CONTRIBUTING.adoc This new Babeltrace contributor's guide documents the internals of the Babeltrace project for its developers and contributors. It overrides doc/logging-guide.adoc, doc/ref-counting.md, and doc/development.txt, which is why those files are removed. Signed-off-by: Philippe Proulx Signed-off-by: Jérémie Galarneau --- diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc new file mode 100644 index 00000000..e81d42b7 --- /dev/null +++ b/CONTRIBUTING.adoc @@ -0,0 +1,923 @@ += Babeltrace's contributor's guide +Jérémie Galarneau, Philippe Proulx +v0.1, 6 September 2017 +:toc: +:toclevels: 5 + + +This is a partial contributor's guide for the +http://diamon.org/babeltrace[Babeltrace] project. If you have any +questions that are not answered in this guide, please post them +on https://lists.lttng.org/cgi-bin/mailman/listinfo/lttng-dev[Babeltrace's mailing list]. + + +== Object reference counting and lifetime + +This section covers the rationale behind the design of Babeltrace's +object lifetime management. + +Starting from Babeltrace 2.x, all publicly exposed objects inherit a +common base: `bt_object`. This base provides a number of facilities to +all objects, chief amongst which are lifetime management functions. + +The reference count of all public objects is controlled by invoking the +`bt_get()` and `bt_put()` functions which respectively increment and +decrement an object's reference count. + +As far as lifetime management in concerned, Babeltrace makes a clear +distinction between regular objects, which have a single parent, and +root objects, which don't. + + +=== The problem + +Let us consider a problematic case to illustrate the need for this +distinction. + +A user of the CTF Writer library declares a trace, which *has* a +stream class (the declaration of a stream) and that stream class, in +turn, *has* an event class (the declaration of an event). + +Nothing prevents this user from releasing his reference on any one of +these objects in any order. However, all objects in the +__trace--stream class--event class__ hierarchy can be retrieved +from any other. + +For instance, the user could discard his reference on both the event +class and the stream class, only keeping a reference on the trace. From +this trace reference, stream classes can be enumerated, providing the +user with a new reference to the stream class he discarded earlier. +event classes can also be enumerated from stream classes, providing the +user with references to the individual event classes. + +Conversely, the user could also hold a reference to an event class and +retrieve its parent stream class. The trace, in turn, can then be +retrieved from the stream class. + +This example illustrates what could be interpreted as a circular +reference dependency existing between these objects. Of course, if the +objects in such a scenario were to hold references to each other (in +both directions), we would be in presence of a circular ownership +resulting in a leak of both objects as their reference counts would +never reach zero. + +Nonetheless, the API must offer the guarantee that holding a node to any +node of the graph keeps all other reachable nodes alive. + + +=== The solution + +The scheme employed in Babeltrace to break this cycle consists in the +"children" holding *reverse component references* to their parents. That +is, in the context of CTF IR, that event classes hold a reference to +their parent stream class and stream classes hold a reference to their +parent trace. + +On the other hand, parents hold *claiming aggregation references* to +their children. A claiming aggregation reference means that the object +being referenced should not be deleted as long as the reference still +exists. In this respect, it can be said that parents truly hold the +ownership of their children, since they control their lifetime. +Conversely, the reference counting mechanism is leveraged by children to +notify parents that no other child indirectly exposes the parent. + +When a parented object's reference count reaches zero, it invokes +`bt_put()` on its parent and does **not** free itself. However, from +that point, the object depends on its parent to signal the moment when +it can be safely reclaimed. + +The invocation of `bt_put()` by the last children holding a reference to +its parent might trigger a cascade of `bt_put()` from child to parent. +Eventually, a **root** object is reached. At that point, if this +orphaned object's reference count reaches zero, the object invokes the +destructor method defined by everyone of its children as part of their +base `struct bt_object`. The key point here is that the cascade of +destructor will necessarily originate from the root and propagate in +preorder to the children. These children will propagate the destruction +to their own children before reclaiming their own memory. This ensures +that a node's pointer to its parent is *always* valid since the parent +has the responsibility of tearing-down their children before cleaning +themselves up. + +Assuming a reference to an object is *acquired* by calling `bt_get()` +while its reference count is zero, the object acquires, in turn, a +reference on its parent using `bt_get()`. At that point, the child can +be thought of as having converted its weak reference to its parent into +a regular reference. That is why this reference is referred to as a +*claiming* aggregation reference. + + +=== Caveats + +This scheme imposes a number of strict rules defining the relation +between objects: + +* Objects may only have one parent. +* Objects, beside the root, are only retrievable from their direct + parent or children. + + +=== Example + +The initial situation is rather simple: **User A** is holding a +reference to a trace, **TC1**. As per the rules previously enounced, +stream classes **SC1** and **SC2** don't hold a reference to **TC1** +since their own reference counts are zero. The same holds true for +**EC1**, **EC2** and **EC3** with respect to **SC1** and **SC2**. + +image::doc/contributing-images/bt-ref01.png[] + +In this second step, we can see that **User A** has acquired a reference +on **SC2** through the trace, **TC1**. + +The stream class's reference count transitions from zero to one, +triggering the acquisition of a strong reference on **TC1** from +**SC2**. + +Hence, at this point, the trace's ownership is shared by **User A** and +**SC2**. + +image::doc/contributing-images/bt-ref02.png[] + +Next, **User A** acquires a reference on the **EC3** event class through +its parent stream class, **SC2**. Again, the transition of an object's +reference count from 0 to 1 triggers the acquisition of a reference on +its parent. + +Note that SC2's reference count was incremented to 2. The trace's +reference count remains unchanged. + +image::doc/contributing-images/bt-ref03.png[] + +**User A** decides to drop its reference on **SC2**. **SC2**'s reference +count returns back to 1, everything else remaining unchanged. + +image::doc/contributing-images/bt-ref04.png[] + +**User A** can then decide to drop its reference on the trace. This +results in a reversal of the initial situation: **User A** now owns an +event, **EC3**, which is keeping everything else alive and reachable. + +image::doc/contributing-images/bt-ref05.png[] + +If another object, **User B**, enters the picture and acquires a +reference on the **SC1** stream class, we see that **SC1**'s reference +count transitioned from 0 to 1, triggering the acquisition of a +reference on **TC1**. + +image::doc/contributing-images/bt-ref06.png[] + +**User B** hands off a reference to **EC1**, acquired through **SC1**, +to another object, **User C**. The acquisition of a reference on +**EC1**, which transitions from 0 to 1, triggers the acquisition of a +reference on its parent, **SC1**. + +image::doc/contributing-images/bt-ref07.png[] + +At some point, **User A** releases its reference on **EC3**. Since +**EC3**'s reference count transitions to zero, it releases its reference +on **SC2**. **SC2**'s reference count, in turn, reaches zero and it +releases its reference to **TC1**. + +**TC1**'s reference count is now 1 and no further action is taken. + +image::doc/contributing-images/bt-ref08.png[] + +**User B** releases its reference on **SC1**. **User C** becomes the +sole owner of the whole hierarchy through his ownership of **EC1**. + +image::doc/contributing-images/bt-ref09.png[] + +Finally, **User C** releases his ownership of **EC1**, triggering the +release of the whole hierarchy. Let's walk through the reclamation of +the whole graph. + +Mirroring what happened when **User A** released its last reference on +**EC3**, the release of **EC1** by **User C** causes its reference count +to fall to zero. + +This transition to zero causes **EC1** to release its reference on +**SC1**. **SC1**'s reference count reaching zero causes it to release +its reference on **TC1**. + +image::doc/contributing-images/bt-ref10.png[] + +Since the reference count of **TC1**, a root object, has reached zero, +it invokes the destructor method on its children. This method is +recursive and causes the stream classes to call the destructor method on +their event classes. + +The event classes are reached and, having no children of their own, are +reclaimed. + +image::doc/contributing-images/bt-ref11.png[] + +The stream classes having destroyed their children, are then reclaimed +by the trace. + +image::doc/contributing-images/bt-ref12.png[] + +Finally, the stream classes having been reclaimed, **TC1** is reclaimed. + +image::doc/contributing-images/bt-ref13.png[] + + +== Valgrind + +To use Valgrind on an application (for example, the CLI or a test) which +loads libbabeltrace, use: + + $ G_SLICE=always-malloc G_DEBUG=gc-friendly PYTHONMALLOC=malloc \ + BABELTRACE_NO_DLCLOSE=1 valgrind --leak-check=full \ + --suppressions=/path/to/babeltrace/extras/valgrind/popt.supp app + +`G_SLICE=always-malloc` and `G_DEBUG=gc-friendly` is for GLib and +`PYTHONMALLOC=malloc` is for the Python interpreter, if it is used by +the Python plugin provider (Valgrind will probably show a lot of errors +which originate from Python interpreter anyway). + +`BABELTRACE_NO_DLCLOSE=1` makes libbabeltrace not close the shared +libraries (plugins) which it loads. This is needed to see the +appropriate backtrace when Valgrind shows errors. + + +== Logging + +Logging is a great instrument for a developer to be able to collect +information about a running software. + +Babeltrace is a complex software with many layers. When a Babeltrace +graph fails to run, what caused the failure? It could be caused by any +component, any notification iterator, and any deeply nested validation +of a CTR IR object, for example. With the appropriate logging statements +manually placed in the source code, we can find the cause of a bug +faster. + +While <> when placing _INFO_ to _FATAL_ +logging statements, you should liberally instrument your Babeltrace +module with _DEBUG_ and _VERBOSE_ logging statements to help future you +and other developers understand what's happening at run-time. + + +=== Logging API + +The Babeltrace logging API is internal: it is not exposed to the users +of the library, only to their developers. The only thing that a library +user can control is the current log level of the library itself with +`bt_logging_set_global_level()` and the initial library's log level with +the `BABELTRACE_LOGGING_GLOBAL_LEVEL` environment variable. + +This API is based on https://github.com/wonder-mice/zf_log[zf_log], a +lightweight, yet featureful, MIT-licensed core logging library for C and +$$C++$$. The zf_log source files were modified to have the `BT_` and +`bt_` prefixes, and other small changes, like color support. + +The logging functions are implemented in the logging convenience +library (`logging` directory). + + +==== Headers + +The logging API headers are: + +``:: + Public header which a library user can use to control and read + libbabeltrace's current log level. + +``:: + Internal, generic logging API which you can use in any Babeltrace + subproject. This is the translation of `zf_log.h`. + +``:: + Specific internal header to use within the library. This header + defines `BT_LOG_OUTPUT_LEVEL` to a custom, library-wide hidden + symbol which is the library's current log level before including + ``. + +Do not include `` or +`` in a header which contains logging +statements: this header could be included in source files which define a +different <>, for example. See +<>. + + +==== Log levels + +The API offers the following log levels: + +* _VERBOSE_ +* _DEBUG_ +* _INFO_ +* _WARN_ +* _ERROR_ +* _FATAL_ + +See <> below. + +There are two important log level variables: + +[[build-time-log-level]]Build-time, minimal log level:: + The minimal log level, or build-time log level, is set at build + time and determines the minimal log level which can be executed. + This applies to all the subprojects and modules (CLI, library, + plugins, etc.). ++ +All the logging statements with a level below this level are **not built +at all**. All the logging statements with a level equal to or greater +than this level _can_ be executed, depending on the +<>. ++ +You can set this level at configuration time with the +`BABELTRACE_MINIMAL_LOG_LEVEL` environment variable, for example: ++ +-- +---- +$ BABELTRACE_MINIMAL_LOG_LEVEL=WARN ./configure +---- +-- ++ +The default build-time log level is `VERBOSE`. For optimal performance, +set it to `NONE`, which effectively disables all logging in all the +Babeltrace subprojects. ++ +The library's public API provides `bt_logging_get_minimal_level()` to +get the configured minimal log level. + +[[run-time-log-level]]Run-time, dynamic log level:: + The dynamic log level is set at run-time and determines the current, + active log level. All the logging statements with a level below this + level are not executed, but they evaluate the condition. All the + logging statements with a level equal to or greater than this level + are executed, provided that their level is also + <>. ++ +`zf_log` has a concept of a global run-time log level which uses the +`_bt_log_global_output_lvl` symbol. In practice, we never use this +symbol, and always make sure that `BT_LOG_OUTPUT_LEVEL` is defined to a +module-wise or subproject-wise hidden symbol before including +``. In the library, +`` does this job: just include this +header which defines `BT_LOG_OUTPUT_LEVEL` to the appropriate symbol +before it includes ``. In plugins, for +example, there is one log level per component class, which makes log +filtering easier during execution. ++ +In libbabeltrace, the user can set the current run-time log level with +the `bt_logging_set_global_level()` function, for example: ++ +-- +[source,c] +---- +bt_logging_set_global_level(BT_LOGGING_LEVEL_INFO); +---- +-- ++ +The library's initial run-time log level is defined by the +`BABELTRACE_LOGGING_GLOBAL_LEVEL` environment variable (`VERBOSE`, `DEBUG`, +`INFO`, `WARN`, `ERROR`, `FATAL`, or `NONE`), or set to _NONE_ if this +environment variable is undefined. ++ +Other subprojects have their own way of setting their run-time log +level. For example, the CLI uses the `BABELTRACE_CLI_LOG_LEVEL` +environment variable, and the `filter.utils.muxer` component class +initializes its log level thanks to the +`BABELTRACE_PLUGIN_UTILS_MUXER_FLT_LOG_LEVEL` environment variable +(also _NONE_ by default). ++ +Make sure that there is a documented way to initialize or modify the +log level of your subproject or module, and that it's set to _NONE_ +by default. + + +[[logging-statements]] +==== Logging statement macros + +The Babeltrace logging statement macros work just like `printf()` and +contain their log level in their name: + +`BT_LOGV("format string", ...)`:: + Standard verbose logging statement. + +`BT_LOGD("format string", ...)`:: + Standard debug logging statement. + +`BT_LOGI("format string", ...)`:: + Standard info logging statement. + +`BT_LOGW("format string", ...)`:: + Standard warning logging statement. + +`BT_LOGE("format string", ...)`:: + Standard error logging statement. + +`BT_LOGF("format string", ...)`:: + Standard fatal logging statement. + +`BT_LOGV_MEM(data_ptr, data_size, "format string", ...)`:: + Memory verbose logging statement. + +`BT_LOGD_MEM(data_ptr, data_size, "format string", ...)`:: + Memory debug logging statement. + +`BT_LOGI_MEM(data_ptr, data_size, "format string", ...)`:: + Memory info logging statement. + +`BT_LOGW_MEM(data_ptr, data_size, "format string", ...)`:: + Memory warning logging statement. + +`BT_LOGE_MEM(data_ptr, data_size, "format string", ...)`:: + Memory error logging statement. + +`BT_LOGF_MEM(data_ptr, data_size, "format string", ...)`:: + Memory fatal logging statement. + +`BT_LOGV_STR("preformatted string")`:: + Preformatted string verbose logging statement. + +`BT_LOGD_STR("preformatted string")`:: + Preformatted string debug logging statement. + +`BT_LOGI_STR("preformatted string")`:: + Preformatted string info logging statement. + +`BT_LOGW_STR("preformatted string")`:: + Preformatted string warning logging statement. + +`BT_LOGE_STR("preformatted string")`:: + Preformatted string error logging statement. + +`BT_LOGF_STR("preformatted string")`:: + Preformatted string fatal logging statement. + + +==== Conditional logging + +`BT_LOG_IF(cond, statement)`:: + Execute `statement` only if `cond` is true. ++ +Example: ++ +-- +[source,c] +---- +BT_LOG_IF(i < count / 2, BT_LOGD("Log this: i=%d", i)); +---- +-- + +To check the <>: + +[source,c] +---- +#if BT_LOG_ENABLED_DEBUG +... +#endif +---- + +This tests if the _DEBUG_ level was enabled at build-time. This means +that the current, <> _could_ be +_DEBUG_, but it could also be higher. The rule of thumb is to use only +logging statements at the same level in a `BT_LOG_ENABLED_*` conditional +block. + +The available definitions for build-time conditions are: + +* `BT_LOG_ENABLED_VERBOSE` +* `BT_LOG_ENABLED_DEBUG` +* `BT_LOG_ENABLED_INFO` +* `BT_LOG_ENABLED_WARN` +* `BT_LOG_ENABLED_ERROR` +* `BT_LOG_ENABLED_FATAL` + +To check the current, <>: + +[source,c] +---- +if (BT_LOG_ON_DEBUG) { + ... +} +---- + +This tests if the _DEBUG_ log level is dynamically turned on +(implies that it's also enabled at build-time). This check could have a +noticeable impact on performance. + +The available definitions for run-time conditions are: + +* `BT_LOG_ON_VERBOSE` +* `BT_LOG_ON_DEBUG` +* `BT_LOG_ON_INFO` +* `BT_LOG_ON_WARN` +* `BT_LOG_ON_ERROR` +* `BT_LOG_ON_FATAL` + +Those macros check the subproject-specific or module-specific log level +symbol (defined by `BT_LOG_OUTPUT_LEVEL`). + +Never, ever write code which would be executed only to compute the +fields of a logging statement outside a conditional logging scope, +for example: + +[source,c] +---- +int number = get_number_of_event_classes_with_property_x(...); +BT_LOGD("Bla bla: number=%d", number); +---- + +Do this instead: + +[source,c] +---- +if (BT_LOG_ON_DEBUG) { + int number = get_number_of_event_classes_with_property_x(...); + BT_LOGD("Bla bla: number=%d", number); +} +---- + +Or even this: + +[source,c] +---- +BT_LOGD("Bla bla: number=%d", get_number_of_event_classes_with_property_x(...)); +---- + + +[[how-to]] +=== Instrument a module + +Follow those steps to make your module loggable: + +. In your module's root directory, create a `logging.c` file with + this content: ++ +-- +[source,c] +---- +/* + * Copyright (c) 2017 EfficiOS Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL my_module_log_level +#include + +BT_LOG_INIT_LOG_LEVEL(my_module_log_level, "BABELTRACE_MY_MODULE_LOG_LEVEL"); +---- +-- ++ +Replace `my_module_log_level` with the name of the symbol which holds +the log level for your module (should be unique amongst all the log level +symbols of the project). Replace `BABELTRACE_MY_MODULE_LOG_LEVEL` with +the name of the environment variable from which to initialize your +module's log level at construction time. `BT_LOG_INIT_LOG_LEVEL()` +defines both the hidden log level symbol and a constructor which reads +the environment variable and sets the log level symbol accordingly. + +. In your module's root directory, create a `logging.h` file with + this content: ++ +-- +[source,c] +---- +#ifndef MY_MODULE_LOGGING_H +#define MY_MODULE_LOGGING_H + +/* + * Copyright (c) 2017 EfficiOS Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL my_module_log_level +#include + +BT_LOG_LEVEL_EXTERN_SYMBOL(my_module_log_level); + +#endif /* MY_MODULE_LOGGING_H */ +---- +-- ++ +Replace `my_module_log_level` with the name of your module's +log level symbol. + +. Include the `logging.c` and `logging.h` in the `_SOURCES` variable + of your module's object in your module's root `Makefile.am`. + + +[[logging-instrument-source]] +==== Instrument a C source file + +To instrument a C source file (`.c`): + +. At the top of the file, before the first `#include` line (if any), + define your file's tag name and include the local `logging.h`: ++ +-- +[source,c] +---- +#define BT_LOG_TAG "MY-MODULE-MY-FILE" +#include "logging.h" +---- +-- ++ +A logging tag name is a namespace for a specific source file. Its name +is part of every log message generated by the statements of the file. A +logging tag name _must_ be only uppercase letters, digits, and the +hyphen (`-`) character. See <> for +a list of standard tags. + +. Use the <> in the file's + functions to instrument it. + + +[[logging-instrument-header]] +==== Instrument a C header file + +To instrument a C header file (`.h`), if you have `static inline` +functions in it: + +. Do not include any logging header. +. Use the <> in the file's + functions to instrument it, making each of them conditional to the + existence of the macro you're using: ++ +-- +[source,c] +---- +static inline +void some_function(void) +{ + /* ... */ + +#ifdef BT_LOGV + BT_LOGV(...); +#endif + + /* ... */ + +#ifdef BT_LOGW_STR + BT_LOGW_STR(...); +#endif + + /* ... */ +} +---- +-- ++ +The C source files which include this header file determine if logging +is enabled or not for them (if the source file is instrumented itself), +and the tag of the included header's logging statement is the same as +the including C source file. + + +[[choose-a-tag]] +=== Choose a tag + +For plugins, the format of the tag name for a given source file _must_ +be: + +[verse] +PLUGIN-__PNAME__-__CCNAME__-__CCTYPE__[-__FILE__] + +__PNAME__:: + Plugin's name. + +__CCNAME__:: + Component class's name. + +__CCTYPE__:: + Component class's type (`SRC`, `FLT`, or `SINK`). + +__FILE__:: + Additional information to specify the source file name or module. + +Examples: + +* `PLUGIN-CTF-LTTNG-LIVE-SRC` +* `PLUGIN-CTF-LTTNG-LIVE-SRC-VIEWER` +* `PLUGIN-UTILS-MUXER-FLT` + + +[[log-level]] +=== Choose a log level + +Choosing the appropriate level for your logging statement is very +important. + +[options="header,autowidth",cols="default,default,asciidoc,default"] +|=== +|Log level |Description |Use cases |Impact on performance + +|_FATAL_ +|The program, library, or plugin cannot continue to work in this +condition: it must be terminated immediately. + +A _FATAL_-level logging statement should always be followed by +`abort()`. +| +* Unexpected return values from system calls. +* Logic error in internal code, for example an unexpected value in a + `switch` statement. +|Almost none: should be executed in production. + +|_ERROR_ +|An important error which is somewhat not fatal, that is, the program, +library, or plugin can continue to work after this, but you judge that +it should be reported to the user. + +Usually, the program cannot recover from such an error, but it can at +least exit cleanly. +| +* Memory allocation errors. +* Failed to perform an operation which should work considering the + implementation and the satisfied preconditions. For example, the + failure to create an empty object (no parameters): most probably + failed internally because of an allocation error. +* Almost any error in terminal elements: CLI and plugins. +|Almost none: should be executed in production. + +|_WARN_ +|A logic error which still allows the execution to continue. + +_WARN_-level logging statements are for any error or weird action that +is directly or indirectly caused by the user. For example, not having +enough memory is considered beyond the user's control, so we always +log memory errors with an _ERROR_ level (not _FATAL_ because we usually +don't abort in this condition). + +Almost all the library's errors are logged as warnings because they are +caused by the user. +| +* Not honoring a public function's preconditions (NULL parameters, + index out of bounds, etc.). +* Adding an invalid event class to a stream class which is already + part of a trace. ++ +For example, the caller tries to set a property of a frozen stream +class. +|Almost none: can be executed in production. + +|_INFO_ +|Any useful information which a non-developer user would understand. +| +* Successful loading of a plugin (with name, version, etc.). +* Successful connection to or disconnection from another system. +* An _optional_ subsystem cannot be loaded. +|Very little: can be executed in production if +_INFO_ level information is desired. + +|_DEBUG_ +|Something that only Babeltrace developers would be interested into. +| +* High-level function entry/exit. +* Object creation, destruction, copying, and freezing. +* The result of some computation/validation which does not + occur in a tight loop. +|Noticeable, but not as much as the _VERBOSE_ level: not executed in +production. + +|_VERBOSE_ +|Low-level debugging context information. More appropriate for tracing +in general. +| +* Reference count change. +* Status of each iteration of a loop. +* State machine's state change. +* Data structure lookup/modification. +* List of ELF sections found in a plugin. +* Get or set an object's property. +* Object comparison's intermediate results. +|Huge: not executed in production. +|=== + +Make sure not to use a _WARN_ (or higher) log level when the condition +leading to the logging statement can occur under normal circumstances. +For example, a public function to get some object or property from an +object by name or key that fails to find the value is not a warning: the +user could legitimately use this function to check if the name/key +exists in the object. In this case, use the _VERBOSE_ level (or do not +log at all). If a numeric index is out of bounds, however, this +qualifies for a _WARN_ level: such API functions have documented +preconditions that the index must be in bounds (the user can always +check with a count or size function). + + +[[message]] +=== Write an appropriate message + +Follow those rules when you write a logging statement's message: + +* Use an english sentence which starts with a capital letter. Start the + sentence with the appropriate verb tense depending on the context. For + example: ++ +-- +** Beginning of operation (present continuous): _Creating ..._, + _Copying ..._, _Serializing ..._, _Freezing ..._, _Destroying ..._ +** End of operation (simple past): _Created ..._, _Successfully created ..._, + _Failed to create ..._, _Set ..._ (simple past of _to set_ which is + also _set_) +-- ++ +For warning and error messages, you can start the message with _Cannot_ +or _Failed to_ followed by a verb if it's appropriate. + +* Do not include the log level in the message itself. For example, + do not start the message with _Error while_ or _Warning:_. + +* Do not put newlines, tabs, or other special characters in the + message, unless you want to log a string with such characters. Note + that multiline log messages can be hard to parse, analyze, and filter, + however, so prefer multiple `BT_LOG*()` statements over a single + statement with newlines. + +* **If there are fields that your logging statement must record**, + follow the message with `:` followed by a space, then with the list of + fields (more about this below). If there are no fields, end the + sentence with a period. + +The statement's fields _must_ be a comma-separated list of ++__name__=__value__+ tokens. Keep +__name__+ as simple as possible +(lowercase if possible). If +__value__+ is a non-alphanumeric string, +put it between double quotes. Always use the `PRId64` and `PRIu64` +specifiers when logging `int64_t` and `uint64_t` values. + +Example: + + "Cannot add event class to stream class: stream-class-addr=%p, " + "stream-class-name=\"%s\", stream-class-id=%" PRId64 ", " + "event-class-addr=%p, event-class-name=\"%s\", event-class-id=%" PRId64 + +By following a standard format for the statement fields, it is easier +to use tools like https://www.elastic.co/products/logstash[Logstash] +to split fields and analyze logs. + +Prefer the following suffixes in field names: + +[options="header,autowidth"] +|=== +|Field name suffix |Description |Format specifier + +|`-addr` |Memory address |`%p` +|`-fd` |File descriptor |`%d` +|`-fp` |File stream (`FILE *`) |`%p` +|`-id` |Object's ID |`%" PRId64 "` or `%" PRIu64 "` +|`-name` |Object's name |`\"%s\"` +|=== + + +=== Output + +The log is printed to the standard error stream. A log line contains the +time, the process and thread IDs, the <>, the +<>, the source's function name, file name and +line number, and the <>. + +When Babeltrace supports terminal color codes (depends on the +`BABELTRACE_TERM_COLOR` environment variable's value and what the +standard output and error streams are plugged into), _INFO_-level lines +are blue, _WARN_-level lines are yellow, and _ERROR_-level and +_FATAL_-level lines are red. + +Log line example: + + 05-11 00:58:03.691 23402 23402 D VALUES bt_value_destroy@values.c:498 Destroying value: addr=0xb9c3eb0 + +You can easily filter the log with `grep` or `ag`. For example, to +keep only the _WARN_-level log messages that the `VALUES` module +generates: + + $ export BABELTRACE_LOGGING_GLOBAL_LEVEL=VERBOSE + $ ./test_ctf_writer_complete 2>&1 | ag 'W VALUES' diff --git a/Makefile.am b/Makefile.am index 2bd6bf04..98fd7d44 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,9 +21,14 @@ SUBDIRS += \ extras dist_doc_DATA = ChangeLog LICENSE mit-license.txt gpl-2.0.txt \ - std-ext-lib.txt README + std-ext-lib.txt README CONTRIBUTING.adoc dist_noinst_DATA = CodingStyle pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = babeltrace.pc babeltrace-ctf.pc + +CONTRIBUTING.html: $(srcdir)/CONTRIBUTING.adoc + asciidoc --verbose -a source-highlighter=pygments $< + +CLEANFILES = CONTRIBUTING.html diff --git a/configure.ac b/configure.ac index 262de9ad..749e2d23 100644 --- a/configure.ac +++ b/configure.ac @@ -653,7 +653,7 @@ AC_CONFIG_FILES([ doc/api/Doxyfile doc/bindings/Makefile doc/bindings/python/Makefile - doc/images/Makefile + doc/contributing-images/Makefile lib/Makefile lib/prio_heap/Makefile lib/plugin/Makefile diff --git a/doc/Makefile.am b/doc/Makefile.am index 07785a81..67a1fb87 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = bindings images +SUBDIRS = bindings contributing-images if ENABLE_API_DOC SUBDIRS += api @@ -6,9 +6,4 @@ endif dist_man_MANS = babeltrace.1 babeltrace-log.1 -EXTRA_DIST = development.txt logging-guide.adoc ref-counting.md API.txt lttng-live.txt - -logging-guide.html: logging-guide.adoc - asciidoc --verbose -a source-highlighter=pygments logging-guide.adoc - -CLEANFILES = logging-guide.html +EXTRA_DIST = API.txt lttng-live.txt diff --git a/doc/contributing-images/Makefile.am b/doc/contributing-images/Makefile.am new file mode 100644 index 00000000..b5121ca8 --- /dev/null +++ b/doc/contributing-images/Makefile.am @@ -0,0 +1,4 @@ +EXTRA_DIST = bt-ref01.png bt-ref02.png bt-ref03.png bt-ref04.png \ + bt-ref05.png bt-ref06.png bt-ref07.png bt-ref08.png \ + bt-ref09.png bt-ref10.png bt-ref11.png bt-ref12.png \ + bt-ref13.png diff --git a/doc/contributing-images/bt-ref01.png b/doc/contributing-images/bt-ref01.png new file mode 100644 index 00000000..a97d207a Binary files /dev/null and b/doc/contributing-images/bt-ref01.png differ diff --git a/doc/contributing-images/bt-ref02.png b/doc/contributing-images/bt-ref02.png new file mode 100644 index 00000000..601f1b74 Binary files /dev/null and b/doc/contributing-images/bt-ref02.png differ diff --git a/doc/contributing-images/bt-ref03.png b/doc/contributing-images/bt-ref03.png new file mode 100644 index 00000000..7de5513f Binary files /dev/null and b/doc/contributing-images/bt-ref03.png differ diff --git a/doc/contributing-images/bt-ref04.png b/doc/contributing-images/bt-ref04.png new file mode 100644 index 00000000..6520c97b Binary files /dev/null and b/doc/contributing-images/bt-ref04.png differ diff --git a/doc/contributing-images/bt-ref05.png b/doc/contributing-images/bt-ref05.png new file mode 100644 index 00000000..11a97b16 Binary files /dev/null and b/doc/contributing-images/bt-ref05.png differ diff --git a/doc/contributing-images/bt-ref06.png b/doc/contributing-images/bt-ref06.png new file mode 100644 index 00000000..0e7c9a3d Binary files /dev/null and b/doc/contributing-images/bt-ref06.png differ diff --git a/doc/contributing-images/bt-ref07.png b/doc/contributing-images/bt-ref07.png new file mode 100644 index 00000000..2e48e42a Binary files /dev/null and b/doc/contributing-images/bt-ref07.png differ diff --git a/doc/contributing-images/bt-ref08.png b/doc/contributing-images/bt-ref08.png new file mode 100644 index 00000000..fb413248 Binary files /dev/null and b/doc/contributing-images/bt-ref08.png differ diff --git a/doc/contributing-images/bt-ref09.png b/doc/contributing-images/bt-ref09.png new file mode 100644 index 00000000..9bca543b Binary files /dev/null and b/doc/contributing-images/bt-ref09.png differ diff --git a/doc/contributing-images/bt-ref10.png b/doc/contributing-images/bt-ref10.png new file mode 100644 index 00000000..5c3e28ce Binary files /dev/null and b/doc/contributing-images/bt-ref10.png differ diff --git a/doc/contributing-images/bt-ref11.png b/doc/contributing-images/bt-ref11.png new file mode 100644 index 00000000..91bdf331 Binary files /dev/null and b/doc/contributing-images/bt-ref11.png differ diff --git a/doc/contributing-images/bt-ref12.png b/doc/contributing-images/bt-ref12.png new file mode 100644 index 00000000..77d5e25a Binary files /dev/null and b/doc/contributing-images/bt-ref12.png differ diff --git a/doc/contributing-images/bt-ref13.png b/doc/contributing-images/bt-ref13.png new file mode 100644 index 00000000..eb7d1137 Binary files /dev/null and b/doc/contributing-images/bt-ref13.png differ diff --git a/doc/development.txt b/doc/development.txt deleted file mode 100644 index 7b6c9e35..00000000 --- a/doc/development.txt +++ /dev/null @@ -1,21 +0,0 @@ -This document describes some principles that should be respected when -developing in Babeltrace. - -Memory usage : - -Since Babeltrace exports a library, we need to make sure that all allocated -memory is freed, we do not want any memory leaks. -Since Babeltrace uses the glib, it is necessary to assist a little valgrind -when trying to identify memory leaks. - -libpopt has issues with inconsistency between versions. Namely, libpopt -0.16 allocates memory for the string returned by poptgetArg(), but not -libpopt 0.13. Therefore, we are providing a warning suppression file -that covers this case in the extras/ directory of the source code. - -So the proper invocation of Babeltrace with Valgrind is : - -G_SLICE=always-malloc G_DEBUG=gc-friendly \ - valgrind --leak-check=full \ - --suppressions=path_to_babeltrace_src/extras/valgrind/popt.supp \ - babeltrace diff --git a/doc/images/Makefile.am b/doc/images/Makefile.am deleted file mode 100644 index a1e6af20..00000000 --- a/doc/images/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -EXTRA_DIST = bt-ref01.svg bt-ref02.svg bt-ref03.svg bt-ref04.svg \ - bt-ref05.svg bt-ref06.svg bt-ref07.svg bt-ref08.svg \ - bt-ref09.svg bt-ref10.svg bt-ref11.svg bt-ref12.svg \ - bt-ref13.svg diff --git a/doc/images/bt-ref01.svg b/doc/images/bt-ref01.svg deleted file mode 100644 index 7291f2c0..00000000 --- a/doc/images/bt-ref01.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
TC1
[Not supported by viewer]
SC1
[Not supported by viewer]
SC2
[Not supported by viewer]
EC1
[Not supported by viewer]
EC2
[Not supported by viewer]
EC3
[Not supported by viewer]
1
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
User A
[Not supported by viewer]
\ No newline at end of file diff --git a/doc/images/bt-ref02.svg b/doc/images/bt-ref02.svg deleted file mode 100644 index da1eb735..00000000 --- a/doc/images/bt-ref02.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
TC1
[Not supported by viewer]
SC1
[Not supported by viewer]
SC2
[Not supported by viewer]
EC1
[Not supported by viewer]
EC2
[Not supported by viewer]
EC3
[Not supported by viewer]
2
[Not supported by viewer]
1
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
User A
[Not supported by viewer]
\ No newline at end of file diff --git a/doc/images/bt-ref03.svg b/doc/images/bt-ref03.svg deleted file mode 100644 index 12f8a14c..00000000 --- a/doc/images/bt-ref03.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
TC1
[Not supported by viewer]
SC1
[Not supported by viewer]
SC2
[Not supported by viewer]
EC1
[Not supported by viewer]
EC2
[Not supported by viewer]
EC3
[Not supported by viewer]
2
[Not supported by viewer]
2
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
1
[Not supported by viewer]
User A
[Not supported by viewer]
\ No newline at end of file diff --git a/doc/images/bt-ref04.svg b/doc/images/bt-ref04.svg deleted file mode 100644 index 10ada1d1..00000000 --- a/doc/images/bt-ref04.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
TC1
[Not supported by viewer]
SC1
[Not supported by viewer]
SC2
[Not supported by viewer]
EC1
[Not supported by viewer]
EC2
[Not supported by viewer]
EC3
[Not supported by viewer]
2
[Not supported by viewer]
1
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
1
[Not supported by viewer]
User A
[Not supported by viewer]
\ No newline at end of file diff --git a/doc/images/bt-ref05.svg b/doc/images/bt-ref05.svg deleted file mode 100644 index d14a01c5..00000000 --- a/doc/images/bt-ref05.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
TC1
[Not supported by viewer]
SC1
[Not supported by viewer]
SC2
[Not supported by viewer]
EC1
[Not supported by viewer]
EC2
[Not supported by viewer]
EC3
[Not supported by viewer]
1
[Not supported by viewer]
1
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
1
[Not supported by viewer]
User A
[Not supported by viewer]
\ No newline at end of file diff --git a/doc/images/bt-ref06.svg b/doc/images/bt-ref06.svg deleted file mode 100644 index 4f8328f7..00000000 --- a/doc/images/bt-ref06.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
TC1
[Not supported by viewer]
SC1
[Not supported by viewer]
SC2
[Not supported by viewer]
EC1
[Not supported by viewer]
EC2
[Not supported by viewer]
EC3
[Not supported by viewer]
2
[Not supported by viewer]
1
[Not supported by viewer]
1
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
1
[Not supported by viewer]
User A
[Not supported by viewer]
User B
[Not supported by viewer]
\ No newline at end of file diff --git a/doc/images/bt-ref07.svg b/doc/images/bt-ref07.svg deleted file mode 100644 index e6674a1c..00000000 --- a/doc/images/bt-ref07.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
TC1
[Not supported by viewer]
SC1
[Not supported by viewer]
SC2
[Not supported by viewer]
EC1
[Not supported by viewer]
EC2
[Not supported by viewer]
EC3
[Not supported by viewer]
2
[Not supported by viewer]
1
[Not supported by viewer]
2
[Not supported by viewer]
0
[Not supported by viewer]
1
[Not supported by viewer]
1
[Not supported by viewer]
User A
[Not supported by viewer]
User B
[Not supported by viewer]
User C
[Not supported by viewer]
\ No newline at end of file diff --git a/doc/images/bt-ref08.svg b/doc/images/bt-ref08.svg deleted file mode 100644 index e813cee6..00000000 --- a/doc/images/bt-ref08.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
TC1
[Not supported by viewer]
SC1
[Not supported by viewer]
SC2
[Not supported by viewer]
EC1
[Not supported by viewer]
EC2
[Not supported by viewer]
EC3
[Not supported by viewer]
1
[Not supported by viewer]
0
[Not supported by viewer]
2
[Not supported by viewer]
0
[Not supported by viewer]
1
[Not supported by viewer]
0
[Not supported by viewer]
User B
[Not supported by viewer]
User C
[Not supported by viewer]
\ No newline at end of file diff --git a/doc/images/bt-ref09.svg b/doc/images/bt-ref09.svg deleted file mode 100644 index 98a7ad4a..00000000 --- a/doc/images/bt-ref09.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
TC1
[Not supported by viewer]
SC1
[Not supported by viewer]
SC2
[Not supported by viewer]
EC1
[Not supported by viewer]
EC2
[Not supported by viewer]
EC3
[Not supported by viewer]
1
[Not supported by viewer]
0
[Not supported by viewer]
1
[Not supported by viewer]
0
[Not supported by viewer]
1
[Not supported by viewer]
0
[Not supported by viewer]
User C
[Not supported by viewer]
\ No newline at end of file diff --git a/doc/images/bt-ref10.svg b/doc/images/bt-ref10.svg deleted file mode 100644 index 5bad3555..00000000 --- a/doc/images/bt-ref10.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
TC1
[Not supported by viewer]
SC1
[Not supported by viewer]
SC2
[Not supported by viewer]
EC1
[Not supported by viewer]
EC2
[Not supported by viewer]
EC3
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
\ No newline at end of file diff --git a/doc/images/bt-ref11.svg b/doc/images/bt-ref11.svg deleted file mode 100644 index e10a6e95..00000000 --- a/doc/images/bt-ref11.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
TC1
[Not supported by viewer]
SC1
[Not supported by viewer]
SC2
[Not supported by viewer]
EC1
[Not supported by viewer]
EC2
[Not supported by viewer]
EC3
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
\ No newline at end of file diff --git a/doc/images/bt-ref12.svg b/doc/images/bt-ref12.svg deleted file mode 100644 index b6f6b89a..00000000 --- a/doc/images/bt-ref12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
TC1
[Not supported by viewer]
SC1
[Not supported by viewer]
SC2
[Not supported by viewer]
EC1
[Not supported by viewer]
EC2
[Not supported by viewer]
EC3
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
\ No newline at end of file diff --git a/doc/images/bt-ref13.svg b/doc/images/bt-ref13.svg deleted file mode 100644 index de88b298..00000000 --- a/doc/images/bt-ref13.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
TC1
[Not supported by viewer]
SC1
[Not supported by viewer]
SC2
[Not supported by viewer]
EC1
[Not supported by viewer]
EC2
[Not supported by viewer]
EC3
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
0
[Not supported by viewer]
\ No newline at end of file diff --git a/doc/logging-guide.adoc b/doc/logging-guide.adoc deleted file mode 100644 index cfd099f9..00000000 --- a/doc/logging-guide.adoc +++ /dev/null @@ -1,674 +0,0 @@ -= Babeltrace logging guide -Philippe Proulx -31 May 2017 -:toc: -:toclevels: 5 - -This guide explains to the Babeltrace developer how to insert logging -statements in Babeltrace's CLI, library, and plugins. - - -== Motive - -Logging is a great instrument for a developer to be able to collect -information about a running software. - -Babeltrace is a complex software with many layers. When a Babeltrace -graph fails to run, what caused the failure? It could be caused by any -component, any notification iterator, and any deeply nested validation -of a CTR IR object, for example. With the appropriate logging statements -manually placed in the source code, we can find the cause of a bug -faster. - -While <> when placing _INFO_ to _FATAL_ -logging statements, you should deliberately instrument your Babeltrace -module with _DEBUG_ and _VERBOSE_ logging statements to help future -you and other developers understand what's happening at run-time. - - -== API - -The Babeltrace logging API is internal: it is not exposed to the users -of the library, only to their developers. The only thing that a library -user can control is the current log level of the library itself with -`bt_logging_set_global_level()` and the initial library's log level with -the `BABELTRACE_LOGGING_GLOBAL_LEVEL` environment variable. - -This API is based on https://github.com/wonder-mice/zf_log[zf_log], a -lightweight, yet featureful, MIT-licensed core logging library for C and -$$C++$$. The zf_log source files were modified to have the `BT_` and -`bt_` prefixes, and other small changes. - -The logging functions are implemented in the logging convenience -library (`logging` directory). - - -=== Headers - -The logging API headers are: - -``:: - Public header which a library user can use to control and read - libbabeltrace's current log level. - -``:: - Internal, generic logging API which you can use in any Babeltrace - subproject. This is the translation of `zf_log.h`. - -``:: - Specific internal header to use within the library. This header - defines `BT_LOG_OUTPUT_LEVEL` to a custom, library-wide hidden - symbol which is the library's current log level before including - ``. - -Do not include `` or -`` in a header which contains logging -statements: this header could be included in source files which define a -different <>, for example. - - - -=== Log levels === - -The API offers the following log levels: - -* _VERBOSE_ -* _DEBUG_ -* _INFO_ -* _WARN_ -* _ERROR_ -* _FATAL_ - -See <> below. - -There are two important log level variables: - -[[build-time-log-level]]Build-time, minimal log level:: - The minimal log level, or build-time log level, is set at build - time and determines the minimal log level which can be executed. - This applies to all the subprojects and modules (CLI, library, - plugins, etc.). -+ -All the logging statements with a level below this level are **not built -at all**. All the logging statements with a level equal to or greater -than this level _can_ be executed, depending on the run-time log level -(see below). -+ -You can set this level at configuration time with the -`BABELTRACE_MINIMAL_LOG_LEVEL` environment variable, for example: -+ --- ----- -$ BABELTRACE_MINIMAL_LOG_LEVEL=WARN ./configure ----- --- -+ -The default build-time log level is `DEBUG`. For optimal performance, -set it to `NONE`, which effectively disables all logging in all the -Babeltrace subprojects. -+ -The library's public API provides `bt_logging_get_minimal_level()` to -get the configured minimal log level. - -[[run-time-log-level]]Run-time, dynamic log level:: - The dynamic log level is set at run-time and determines the current, - active log level. All the logging statements with a level below this - level are not executed, but they evaluate the condition. All the - logging statements with a level equal to or greater than this level - are executed, provided that their level is also enabled at build - time (see above). -+ -In `zf_log`, there is a concept of a global run-time log level which -uses the `_bt_log_global_output_lvl` symbol. In practice, we never use -this symbol, and always make sure that `BT_LOG_OUTPUT_LEVEL` is defined -to a module-wise or subproject-wise hidden symbol before including -``. In the library, -`` does this job: just include -this header which defines `BT_LOG_OUTPUT_LEVEL` to the appropriate -symbol before it includes ``. In plugins, -for example, there is one log level per component class, which makes -log filtering easier during execution. -+ -In libbabeltrace, the user can set the current run-time log level with -the `bt_logging_set_global_level()` function, for example: -+ --- -[source,c] ----- -bt_logging_set_global_level(BT_LOGGING_LEVEL_INFO); ----- --- -+ -The library's initial run-time log level is defined by the -`BABELTRACE_LOGGING_GLOBAL_LEVEL` environment variable (`VERBOSE`, `DEBUG`, -`INFO`, `WARN`, `ERROR`, `FATAL`, or `NONE`), or set to _NONE_ if this -environment variable is undefined. -+ -Other subprojects have their own way of setting their run-time log -level. For example, the CLI uses the `BABELTRACE_CLI_LOG_LEVEL` -environment variable, and the `text.pretty` sink component class -initializes its log level thanks to the -`BABELTRACE_PLUGIN_TEXT_PRETTY_SINK_LOG_LEVEL` environment variable -(also _NONE_ by default). -+ -Make sure that there is a documented way to initialize or modify the -log level of your subproject or module, and that it's set to _NONE_ -by default. - - -[[logging-statements]] -=== Logging statement macros - -The Babeltrace logging statement macros work just like `printf()` and -contain their log level in their name: - -`BT_LOGV("format string", ...)`:: - Standard verbose logging statement. - -`BT_LOGD("format string", ...)`:: - Standard debug logging statement. - -`BT_LOGI("format string", ...)`:: - Standard info logging statement. - -`BT_LOGW("format string", ...)`:: - Standard warning logging statement. - -`BT_LOGE("format string", ...)`:: - Standard error logging statement. - -`BT_LOGF("format string", ...)`:: - Standard fatal logging statement. - -`BT_LOGV_MEM(data_ptr, data_size, "format string", ...)`:: - Memory verbose logging statement. - -`BT_LOGD_MEM(data_ptr, data_size, "format string", ...)`:: - Memory debug logging statement. - -`BT_LOGI_MEM(data_ptr, data_size, "format string", ...)`:: - Memory info logging statement. - -`BT_LOGW_MEM(data_ptr, data_size, "format string", ...)`:: - Memory warning logging statement. - -`BT_LOGE_MEM(data_ptr, data_size, "format string", ...)`:: - Memory error logging statement. - -`BT_LOGF_MEM(data_ptr, data_size, "format string", ...)`:: - Memory fatal logging statement. - -`BT_LOGV_STR("preformatted string")`:: - Preformatted string verbose logging statement. - -`BT_LOGD_STR("preformatted string")`:: - Preformatted string debug logging statement. - -`BT_LOGI_STR("preformatted string")`:: - Preformatted string info logging statement. - -`BT_LOGW_STR("preformatted string")`:: - Preformatted string warning logging statement. - -`BT_LOGE_STR("preformatted string")`:: - Preformatted string error logging statement. - -`BT_LOGF_STR("preformatted string")`:: - Preformatted string fatal logging statement. - - -=== Conditional logging - -`BT_LOG_IF(cond, statement)`:: - Execute `statement` only if `cond` is true. -+ -Example: -+ --- -[source,c] ----- -BT_LOG_IF(i < count / 2, BT_LOGD("Log this: i=%d", i)); ----- --- - -To check the <>: - -[source,c] ----- -#if BT_LOG_ENABLED_DEBUG -... -#endif ----- - -This tests if the _DEBUG_ level was enabled at build-time. This -means that the current, dynamic log level _could_ be _DEBUG_, but it -could also be higher. The rule of thumb is to use only logging -statements at the same level in a `BT_LOG_ENABLED_*` conditional block. - -The available definitions for build-time conditions are: - -* `BT_LOG_ENABLED_VERBOSE` -* `BT_LOG_ENABLED_DEBUG` -* `BT_LOG_ENABLED_INFO` -* `BT_LOG_ENABLED_WARN` -* `BT_LOG_ENABLED_ERROR` -* `BT_LOG_ENABLED_FATAL` - -To check the current, <>: - -[source,c] ----- -if (BT_LOG_ON_DEBUG) { - ... -} ----- - -This tests if the _DEBUG_ log level is dynamically turned on -(implies that it's also enabled at build-time). This check could have a -noticeable impact on performance. - -The available definitions for run-time conditions are: - -* `BT_LOG_ON_VERBOSE` -* `BT_LOG_ON_DEBUG` -* `BT_LOG_ON_INFO` -* `BT_LOG_ON_WARN` -* `BT_LOG_ON_ERROR` -* `BT_LOG_ON_FATAL` - -Those macros check the subproject-specific or module-specific log level -symbol (defined by `BT_LOG_OUTPUT_LEVEL`). - -Never, ever write code which would be executed only to compute the -fields of a logging statement outside a conditional logging scope, -for example: - -[source,c] ----- -int number = get_number_of_event_classes_with_property_x(...); -BT_LOGD("Bla bla: number=%d", number); ----- - -Do this instead: - -[source,c] ----- -if (BT_LOG_ON_DEBUG) { - int number = get_number_of_event_classes_with_property_x(...); - BT_LOGD("Bla bla: number=%d", number); -} ----- - -Or even this: - -[source,c] ----- -BT_LOGD("Bla bla: number=%d", get_number_of_event_classes_with_property_x(...)); ----- - - -[[how-to]] -=== Instrument a module - -Follow those steps to make your module loggable: - -. In your module's root directory, create a `logging.c` file with - this content: -+ --- -[source,c] ----- -/* - * Copyright (c) 2017 EfficiOS Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL my_module_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(my_module_log_level, "BABELTRACE_MY_MODULE_LOG_LEVEL"); ----- --- -+ -Replace `my_module_log_level` with the name of the symbol which holds -the log level for your module (should be unique amongst all the log level -symbols of the project). Replace `BABELTRACE_MY_MODULE_LOG_LEVEL` with -the name of the environment variable from which to initialize your -module's log level at construction time. `BT_LOG_INIT_LOG_LEVEL()` -defines both the hidden log level symbol and a constructor which reads -the environment variable and sets the log level symbol accordingly. - -. In your module's root directory, create a `logging.h` file with - this content: -+ --- -[source,c] ----- -#ifndef MY_MODULE_LOGGING_H -#define MY_MODULE_LOGGING_H - -/* - * Copyright (c) 2017 EfficiOS Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL my_module_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(my_module_log_level); - -#endif /* MY_MODULE_LOGGING_H */ ----- --- -+ -Again, replace `my_module_log_level` with the name of your module's -log level symbol. - -. Include the `logging.c` and `logging.h` in the `_SOURCES` variable - of your module's object in your module's root `Makefile.am`. - -To instrument a C source file (`.c`): - -. At the top of the file, before the first `#include` line (if any), - define your file's tag name and include the local `logging.h`: -+ --- -[source,c] ----- -#define BT_LOG_TAG "MY-MODULE-MY-FILE" -#include "logging.h" ----- --- -+ -A logging tag name is a namespace for a specific source file. Its name -is part of every log message generated by the statements of the file. A -logging tag name _must_ be only uppercase letters, digits, and the -hyphen (`-`) character. See <> for -a list of standard tags. - -. Use the <> in the file's - functions to instrument it. - -To instrument a C header file (`.h`), if you have `static inline` -functions in it: - -. Do not include any logging header. -. Use the <> in the file's - functions to instrument it, making each of them conditional to the - existence of the macro you're using: -+ --- -[source,c] ----- -static inline -void some_function(void) -{ - /* ... */ - -#ifdef BT_LOGV - BT_LOGV(...); -#endif - - /* ... */ - -#ifdef BT_LOGW_STR - BT_LOGW_STR(...); -#endif - - /* ... */ -} ----- --- -+ -The C source files which include this header file determine if logging -is enabled or not for them (if the source file is instrumented itself), -and the tag of the included header's logging statement is the same as -the C source file. - - -[[tags]] -=== Choose a tag - -For plugins, the format of the tag name for a given source file _must_ -be: - -[verse] -PLUGIN-__PNAME__-__CCNAME__-__CCTYPE__[-__FILE__] - -__PNAME__:: - Plugin's name. - -__CCNAME__:: - Component class's name. - -__CCTYPE__:: - Component class's type (`SRC`, `FLT`, or `SINK`). - -__FILE__:: - Additional information to specify the source file name or module. - -Examples: - -* `PLUGIN-CTF-LTTNG-LIVE-SRC` -* `PLUGIN-CTF-LTTNG-LIVE-SRC-VIEWER` -* `PLUGIN-UTILS-MUXER-FLT` - - -[[level]] -== Choose a log level - -Choosing the appropriate level for your logging statement is very -important. - -[options="header,autowidth",cols="default,default,asciidoc,default"] -|=== -|Log level |Description |Use cases |Impact on performance - -|_FATAL_ -|The program, library, or plugin cannot continue to work in this -condition: it must be terminated immediately. - -A _FATAL_-level logging statement should always be followed by -`abort()`. -| -* Unexpected return values from system calls. -* Logic error in internal code, for example an unexpected value in a - `switch` statement. -|Almost none: should be executed in production. - -|_ERROR_ -|An important error which is somewhat not fatal, that is, the program, -library, or plugin can continue to work after this, but you judge that -it should be reported to the user. - -Usually, the program cannot recover from such an error, but it can at -least exit cleanly. -| -* Memory allocation errors. -* Failed to perform an operation which should work considering the - implementation and the satisfied preconditions. For example, the - failure to create an empty object (no parameters): most probably - failed internally because of an allocation error. -* Almost any error in terminal elements: CLI and plugins. -|Almost none: should be executed in production. - -|_WARN_ -|A logic error which still allows the execution to continue. - -_WARN_-level logging statements are for any error or weird action that -is directly or indirectly caused by the user. For example, not having -enough memory is considered beyond the user's control, so we always -log memory errors with an _ERROR_ level (not _FATAL_ because we usually -don't abort in this condition). - -Almost all the library's errors are logged as warnings because they are -caused by the user. -| -* Not honoring a public function's preconditions (NULL parameters, - index out of bounds, etc.). -* Adding an invalid event class to a stream class which is already - part of a trace. -+ -For example, the caller tries to set a property of a frozen stream -class. -|Almost none: can be executed in production. - -|_INFO_ -|Any useful information which a non-developer user would understand. -| -* Successful loading of a plugin (with name, version, etc.). -* Successful connection to or disconnection from another system. -* An _optional_ subsystem cannot be loaded. -|Very little: can be executed in production if -_INFO_ level information is desired. - -|_DEBUG_ -|Something that only Babeltrace developers would be interested into. -| -* High-level function entry/exit. -* Object creation, destruction, copying, and freezing. -* The result of some computation/validation. -|Noticeable, but not as much as the _VERBOSE_ level: not executed in -production. - -|_VERBOSE_ -|Low-level debugging context information. More appropriate for tracing -in general. -| -* Reference count change. -* Status of each iteration of a loop. -* State machine's state change. -* Data structure lookup/modification. -* List of ELF sections found in a plugin. -* Get or set an object's property. -* Object comparison's intermediate results. -|Huge: not executed in production. -|=== - -Make sure not to use a _WARN_ (or higher) log level when the condition -leading to the logging statement can occur under normal circumstances. -For example, a public function to get some object or property from an -object by name or key that fails to find the value is not a warning: the -user could legitimately use this function to check if the name/key -exists in the object. In this case, use the _VERBOSE_ level (or do not -log at all). If a numeric index is out of bounds, however, this -qualifies for a _WARN_ level: such API functions have documented -preconditions that the index must be in bounds (the user can always -check with a count or size function). - - -[[message]] -== Write an appropriate message - -Follow those rules when you write a logging statement's message: - -* Use an english sentence which starts with a capital letter. Start the - sentence with the appropriate verb tense depending on the context. For - example: -+ --- -** Beginning of operation (present continuous): _Creating ..._, - _Copying ..._, _Serializing ..._, _Freezing ..._, _Destroying ..._ -** End of operation (simple past): _Created ..._, _Successfully created ..._, - _Failed to create ..._, _Set ..._ (simple past of _to set_ which is - also _set_) --- -+ -For warning and error messages, you can start the message with _Cannot_ -or _Failed to_ followed by a verb if it's appropriate. - -* Do not include the log level in the message itself. For example, - do not start the message with _Error while_ or _Warning:_. - -* Do not put newlines, tabs, or other special characters in the - message, unless you want to log a string with such characters. Note - that multiline log messages can be hard to parse, analyze, and filter, - however, so prefer multiple `BT_LOG*()` statements over a single - statement with newlines. - -* **If there are fields that your logging statement must record**, - follow the message with `:` followed by a space, then with the list of - fields (more about this below). If there are no fields, end the - sentence with a period. - -The statement's fields _must_ be a comma-separated list of -+__name__=__value__+ tokens. Keep +__name__+ as simple as possible -(lowercase if possible). If +__value__+ is a non-alphanumeric string, -put it between double quotes. Always use the `PRId64` and `PRIu64` -specifiers when logging `int64_t` and `uint64_t` values. - -Example: - - "Cannot add event class to stream class: stream-class-addr=%p, " - "stream-class-name=\"%s\", stream-class-id=%" PRId64 ", " - "event-class-addr=%p, event-class-name=\"%s\", event-class-id=%" PRId64 - -By following a standard format for the statement fields, it is easier -to use tools like https://www.elastic.co/products/logstash[Logstash] -to split fields and analyze logs. - -Prefer the following suffixes in field names: - -[options="header,autowidth"] -|=== -|Field name suffix |Description |Format specifier - -|`-addr` |Memory address |`%p` -|`-fd` |File descriptor |`%d` -|`-fp` |File stream (`FILE *`) |`%p` -|`-id` |Object's ID |`%" PRId64 "` or `%" PRIu64 "` -|`-name` |Object's name |`\"%s\"` -|=== - - -== Output - -The log is printed to the standard error stream. A log line contains the -time, the process and thread IDs, the <>, the tag name, -the source's function name, file name and line number, and the -<>. - -Example: - - 05-11 00:58:03.691 23402 23402 D VALUES bt_value_destroy@values.c:498 Destroying value: addr=0xb9c3eb0 - -You can easily filter the log with `grep` or `ag`. For example, to -keep only the _WARN_-level log messages that the `VALUES` module -generates: - - $ export BABELTRACE_LOGGING_GLOBAL_LEVEL=VERBOSE - $ ./test_ctf_writer_complete 2>&1 | ag 'W VALUES' diff --git a/doc/ref-counting.md b/doc/ref-counting.md deleted file mode 100644 index 32b86085..00000000 --- a/doc/ref-counting.md +++ /dev/null @@ -1,195 +0,0 @@ -# Babeltrace Object Reference Counting and Lifetime - -This document covers the rationale behind the design of Babeltrace's object -lifetime management. - -Starting from Babeltrace 2.x, all publicly exposed objects inherit a common -base: bt_object. This base provides a number of facilities to all objects, chief -amongst which are lifetime management functions. - -The reference count of all public objects is controlled by invoking -the `bt_get()` and `bt_put()` functions which respectively -increment and decrement an object's reference count. - -As far as lifetime management in concerned, Babeltrace makes a clear -distinction between regular objects, which have a single parent, and root -objects, which don't. - -## The Problem - -Let us consider a problematic case to illustrate the need for this -distinction. - -A user of the CTF Writer library declares a Trace, which *has* a -Stream Class (the declaration of a stream) and that Stream Class, in -turn, *has* an Event Class (the declaration of an event). - -Nothing prevents this user from releasing his reference on any one of -these objects in any order. However, all objects in the "Trace <-> -Stream <-> Event" hierarchy can be retrieved from any other. - -For instance, the user could discard his reference on both the Event -Class and the Stream Class, only keeping a reference on the Trace. -From this Trace reference, Stream Classes can be enumerated, providing -the user with a new reference to the Stream Class he discarded -earlier. Event Classes can also be enumerated from Stream Classes, -providing the user with references to the individual Event Classes. - -Conversely, the user could also hold a reference to an Event Class and -retrieve its parent Stream Class. The Trace, in turn, can then be -retrieved from the Stream Class. - -This example illustrates what could be interpreted as a circular -reference dependency existing between these objects. Of course, if the -objects in such a scenario were to hold references to each other (in -both directions), we would be in presence of a circular ownership -resulting in a leak of both objects as their reference counts would -never reach zero. - -Nonetheless, the API must offer the guarantee that holding a node to any node of -the graph keeps all other reachable nodes alive. - -## The Solution - -The scheme employed in Babeltrace to break this cycle consists in the -"children" holding *Reverse Component References* to their parents. That -is, in the context of CTF-IR, that Event Classes hold a reference to -their Stream Class and Stream Classes hold a reference to their Trace. - -On the other hand, parents hold *Claiming Aggregation References* to -their children. A claiming aggregation reference means that the -object being referenced should not be deleted as long as the reference -still exists. In this respect, it can be said that parents truly hold the -ownership of their children, since they control their lifetime. Conversely, -the reference counting mechanism is leveraged by children to notify parents -that no other child indirectly exposes the parent. - -When a parented object's reference count reaches zero, it invokes -`bt_put()` on its parent and does **not** free itself. However, from that -point, the object depends on its parent to signal the moment when it -can be safely reclaimed. - -The invocation of `bt_put()` by the last children holding a reference to its -parent might trigger a cascade of `bt_put()` from child to parent. Eventually, -a **root** object is reached. At that point, if this orphaned object's -reference count reaches zero, the object will invoke the `destroy()` method -defined by every one of its children as part of their base `struct bt_object`. -The key point here is that the cascade of `destroy()` will necessarily originate -from the root and propagate in pre-order to the children. These children will -propagate the destruction to their own children before reclaiming their own -memory. This ensures that a node's pointer to its parent is *always* valid -since the parent has the responsibility of tearing-down their children before -cleaning themselves-up. - -Assuming a reference to an object is *acquired* by calling `bt_get()` while its -reference count is zero, the object will, in turn, acquire a reference on its -parent using `bt_get()`. At that point, the child can be thought of as having -converted its weak reference to its parent into a regular reference. That is -why this reference is referred to as a *claiming* aggregation reference. - -## Caveats - -This scheme imposes a number of strict rules defining the relation -between objects: - - * Objects may only have one parent, - * Objects, beside the root, are only retrievable from their direct parent or - children. - -## Walking through an example - -The initial situation is rather simple. **User A** is holding a reference to a -trace, **TC1**. As per the rules previously enounced, Stream Classes **SC1** and -**SC2** don't hold a reference to **TC1** since their own reference counts are -zero. The same holds true for **EC1**, **EC2** and **EC3** with respect to -**SC1** and **SC2**. - -![](images/bt-ref01.svg) - -In this second step, we can see that User A has acquired a reference on **SC2** -through the Trace, **TC1**. - -The Stream Class' reference count transitions from zero to one, triggering the -acquisition of a strong reference on **TC1** from **SC2**. - -Hence, at this point, the Trace's ownership is shared by **User A** and -**SC2**. - -![](images/bt-ref02.svg) - -Next, **User A** acquires a reference on the **EC3** Event Class through its -parent Stream Class, **SC2**. Again, the transition of an object's reference -count from 0 to 1 triggers the acquisition of a reference on its parent. - -Note that SC2's reference count was incremented to 2. The Trace's reference -count remains unchanged. - -![](images/bt-ref03.svg) - -**User A** decides to drop its reference on **SC2**. **SC2**'s reference count -returns back to 1, everything else remaining unchanged. - -![](images/bt-ref04.svg) - -**User A** can then decide to drop its reference on the Trace. This results in -a reversal of the initial situation: **User A** now owns an event, **EC3**, -which is keeping everything else alive and reachable. - -![](images/bt-ref05.svg) - -If another object, **User B**, enters the picture and acquires a reference on -the **SC1** Stream Class, we see that **SC1**'s reference count -transitioned from 0 to 1, triggering the acquisition of a reference on **TC1**. - -![](images/bt-ref06.svg) - -**User B** hands off a reference to **EC1**, acquired through **SC1**, to -another object, **User C**. The acquisition of a reference on **EC1**, which -transitions from 0 to 1, triggers the acquisition of a reference on its parent, -**SC1**. - -![](images/bt-ref07.svg) - -At some point, **User A** releases its reference on **EC3**. Since **EC3**'s -reference count transitions to zero, it releases its reference on **SC2**. -**SC2**'s reference count, in turn, reaches zero and it releases its reference -to **TC1**. - -**TC1**'s reference count is now 1 and no further action is taken. - -![](images/bt-ref08.svg) - -**User B** releases its reference on **SC1**. **User C** becomes the sole owner -of the whole hierarchy through his ownership of **EC1**. - -![](images/bt-ref09.svg) - -Finally, **User C** releases his ownership of **EC1**, triggering the release of -the whole hierarchy. We will walk through the reclamation of the whole graph. - -Mirroring what happened when **User A** released its last reference on **EC3**, -the release of **EC1** by **User C** causes its reference count to fall to zero. - -This transition to zero causes **EC1** to release its reference on **SC1**. -**SC1**'s reference count reaching zero causes it to release its reference on -**TC1**. - -![](images/bt-ref10.svg) - -Since the reference count of **TC1**, a root object, has reached zero, it -invokes the `destroy()` method on its children. This method is recursive and -causes the Stream Classes to call the `destroy()` method on their Event Classes. - -The Event Classes are reached and, having no children of their own, are -reclaimed. - -![](images/bt-ref11.svg) - -The Stream Classes having destroyed their children, are then reclaimed by the -Trace. - -![](images/bt-ref12.svg) - -Finally, the Stream Classes having been reclaimed, **TC1** is reclaimed. - -![](images/bt-ref13.svg)