Difference between revisions of "Debugging functions"
(6 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
Mailutils API provides programmers with three ways of outputting additional debugging information: | Mailutils API provides programmers with three ways of outputting additional debugging information: | ||
− | # Via the [[mu_debug]] macro. | + | # Via the [[#mu_debug|<tt>mu_debug</tt>]] macro. |
− | # Via the [[mu_debug_log]] functions. | + | # Via the [[#mu_debug_log|<tt>mu_debug_log</tt>]] functions. |
− | # By writing directly to [[mu_strerr]]. | + | # By writing directly to the [[#mu_strerr|<tt>mu_strerr</tt>]] stream. |
The two former are the most often used, and the latter is a rather low-level method suitable when the desired output cannot be achieved by other means. | The two former are the most often used, and the latter is a rather low-level method suitable when the desired output cannot be achieved by other means. | ||
Line 14: | Line 14: | ||
A ''level'' controls what information is desired in the given category. | A ''level'' controls what information is desired in the given category. | ||
− | An easy to use [[Debug level|symbolic notation]] is provided which allows users to select | + | An easy to use [[Debug level|symbolic notation]] is provided which allows users to select the |
− | desired categories and associated | + | desired categories and associated levels from the command line and |
configuration files. In this article we will look at the internal aspects of these notions, from the point of view of a programmer. | configuration files. In this article we will look at the internal aspects of these notions, from the point of view of a programmer. | ||
Line 23: | Line 23: | ||
function: | function: | ||
− | < | + | <syntaxhighlight lang="C"> |
size_t mu_debug_register_category (char *name); | size_t mu_debug_register_category (char *name); | ||
− | </ | + | </syntaxhighlight> |
The ''name'' argument supplies the symbolic name for this category, under which it will be available for users. The function returns a ''category index'' which can subsequently be used in ''mu_debug*'' calls (see below). | The ''name'' argument supplies the symbolic name for this category, under which it will be available for users. The function returns a ''category index'' which can subsequently be used in ''mu_debug*'' calls (see below). | ||
Line 49: | Line 49: | ||
for a given category: | for a given category: | ||
− | < | + | <syntaxhighlight lang="C"> |
int mu_debug_level_p (int category, int level); | int mu_debug_level_p (int category, int level); | ||
− | </ | + | </syntaxhighlight> |
+ | <div id="mu_debug"></div> | ||
== The <tt>mu_debug</tt> macro == | == The <tt>mu_debug</tt> macro == | ||
The ''mu_debug'' macro is the basic mechanism for implementing debugging output in programs: | The ''mu_debug'' macro is the basic mechanism for implementing debugging output in programs: | ||
− | mu_debug(''category'',''level'', (''args'')) | + | mu_debug(''category'', ''level'', (''args'')) |
− | + | The first two arguments specify the category and level, and the third one must be an argument list suitable for the ''mu_debug_log'' invocation. | |
{{Note|''args'' must always be enclosed in an extra pair of parentheses.}} | {{Note|''args'' must always be enclosed in an extra pair of parentheses.}} | ||
Line 69: | Line 70: | ||
will format the argument list given as its third argument if the ''mailbox.=trace1'' [[Debug level|level]] is set. | will format the argument list given as its third argument if the ''mailbox.=trace1'' [[Debug level|level]] is set. | ||
+ | <div id="mu_debug_log"></div> | ||
==The <tt>mu_debug_log</tt> function family== | ==The <tt>mu_debug_log</tt> function family== | ||
The [[mu_debug_log]] functions are the mechanism used by the <tt>mu_debug</tt> macro to do the actual output: | The [[mu_debug_log]] functions are the mechanism used by the <tt>mu_debug</tt> macro to do the actual output: | ||
− | < | + | <syntaxhighlight lang="C"> |
void mu_debug_log (const char *fmt, ...); | void mu_debug_log (const char *fmt, ...); | ||
void mu_debug_log_begin (const char *fmt, ...); | void mu_debug_log_begin (const char *fmt, ...); | ||
void mu_debug_log_cont (const char *fmt, ...); | void mu_debug_log_cont (const char *fmt, ...); | ||
void mu_debug_log_end (const char *fmt, ...); | void mu_debug_log_end (const char *fmt, ...); | ||
− | </ | + | </syntaxhighlight> |
− | All functions are variadic, with their argument '' | + | All functions are variadic, with their argument ''fmt'' argument being a usual ''printf'' format specification<ref>[http://pubs.opengroup.org/onlinepubs/009695399/functions/printf.html The printf function]</ref>, and the rest of arguments supplying the actual data to output. |
The <tt>mu_debug_log</tt> function formats its arguments, and then sends the formatted data '''terminated with a newline''' character to the ''mu_strerr'' stream. Therefore, the ''fmt'' argument to this function must not contain terminating <tt>\n</tt>. | The <tt>mu_debug_log</tt> function formats its arguments, and then sends the formatted data '''terminated with a newline''' character to the ''mu_strerr'' stream. Therefore, the ''fmt'' argument to this function must not contain terminating <tt>\n</tt>. | ||
− | If you need to output | + | If you need to output debugging information in several chunks but want these chunks to form a single line on output, do as follows: |
− | # Call <tt>mu_debug_log_begin</tt>. This function prepares the log stream for subsequent continuous output, formats its data and sends them down stream as the first chunk. | + | # Call <tt>mu_debug_log_begin</tt>. This function prepares the log stream for subsequent continuous output, formats its data and sends them down the stream as the first chunk. |
# Call <tt>mu_debug_log_cont</tt> as much times as needed (perhaps within a loop) to format more parts of the output sequence. Neither <tt>mu_debug_log_begin</tt> nor <tt>mu_debug_log_cont</tt> add newlines to their output, so until now the output is not flushed. | # Call <tt>mu_debug_log_cont</tt> as much times as needed (perhaps within a loop) to format more parts of the output sequence. Neither <tt>mu_debug_log_begin</tt> nor <tt>mu_debug_log_cont</tt> add newlines to their output, so until now the output is not flushed. | ||
# Send the lest portion of data using the <tt>mu_debug_log_end</tt> call. After formatting its data, the function sends a terminating newline, thereby flushing the formatted output to the log stream. | # Send the lest portion of data using the <tt>mu_debug_log_end</tt> call. After formatting its data, the function sends a terminating newline, thereby flushing the formatted output to the log stream. | ||
− | You can also terminate the | + | You can also terminate the line with a call to <tt>mu_debug_log_nl()</tt> function, if there is no more data left for output: |
− | < | + | <syntaxhighlight lang="C"> |
void mu_debug_log_nl (void); | void mu_debug_log_nl (void); | ||
− | </ | + | </syntaxhighlight> |
The following code fragment illustrates this approach. | The following code fragment illustrates this approach. | ||
− | < | + | <syntaxhighlight lang="C"> |
if (mu_debug_level_p (MU_DEBCAT_FILTER, MU_DEBUG_TRACE1)) | if (mu_debug_level_p (MU_DEBCAT_FILTER, MU_DEBUG_TRACE1)) | ||
{ | { | ||
Line 108: | Line 110: | ||
mu_debug_log_nl (); | mu_debug_log_nl (); | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
This code checks if the [[Debug level|level]] ''filter.=trace1'' is set, and if so formats | This code checks if the [[Debug level|level]] ''filter.=trace1'' is set, and if so formats | ||
the array <tt>char **argv</tt> into the log output stream. | the array <tt>char **argv</tt> into the log output stream. | ||
− | == | + | <div id="mu_strerr"></div> |
+ | == The ''mu_strerr'' stream == | ||
The stream <tt>mu_strerr</tt> is a Mailutils counterpart of the traditional <tt>stderr</tt> stream, except that it provides much more possibilities. All writes to this stream are directed to whatever log transport stream was specified in the Mailutils configuration file. | The stream <tt>mu_strerr</tt> is a Mailutils counterpart of the traditional <tt>stderr</tt> stream, except that it provides much more possibilities. All writes to this stream are directed to whatever log transport stream was specified in the Mailutils configuration file. | ||
− | The stream is line-buffered, so the output gets flushed after each <tt>\n</tt> character written to it. Upon writing to the transport stream, a '' | + | The stream is line-buffered, so the output gets flushed after each <tt>\n</tt> character written to it. Upon writing to the transport stream, a ''diagnostic prefix'' is output before each lineful of data. The prefix consists of: |
# The value of <tt>mu_log_tag</tt> variable, followed by a colon and space character; | # The value of <tt>mu_log_tag</tt> variable, followed by a colon and space character; | ||
Line 124: | Line 127: | ||
<tt>Mu_strerr</tt> is an instance of [[log stream]], and supports all ioctls and format directives supported by it. For example, to set [[error location|location]] you can either use | <tt>Mu_strerr</tt> is an instance of [[log stream]], and supports all ioctls and format directives supported by it. For example, to set [[error location|location]] you can either use | ||
− | the [[MU_IOCTL_LOGSTREAM]] ioctl, or (to set it | + | the [[MU_IOCTL_LOGSTREAM]] ioctl, or (to set it temporarily, for this line of output only), the |
<tt>f</tt> and <tt>l</tt> [[log escape]]s, as shown in the example code below: | <tt>f</tt> and <tt>l</tt> [[log escape]]s, as shown in the example code below: | ||
− | < | + | <syntaxhighlight lang="C"> |
mu_stream_printf (mu_strerr, "\033f<%d>%s\033l<%d>%s", strlen (file), file, line, | mu_stream_printf (mu_strerr, "\033f<%d>%s\033l<%d>%s", strlen (file), file, line, | ||
"bad input") | "bad input") | ||
− | </ | + | </syntaxhighlight> |
Line 137: | Line 140: | ||
[[Category:Debugging]] | [[Category:Debugging]] | ||
+ | [[Category:C API]] |
Latest revision as of 13:28, 16 October 2023
Mailutils API provides programmers with three ways of outputting additional debugging information:
- Via the mu_debug macro.
- Via the mu_debug_log functions.
- By writing directly to the mu_strerr stream.
The two former are the most often used, and the latter is a rather low-level method suitable when the desired output cannot be achieved by other means.
Categories and Levels
Although debugging output is often regarded as being useful for developers only, it is quite often helpful for regular users. Therefore it is important to give user a possibility to choose what debugging information he needs and how verbose he wants it to be.
Mailutils achieves this using a concept of debugging categories and levels. A category comprises all debugging information related to a certain part of the functionality of a program. For example, the category acl means everything related to access control lists. A level controls what information is desired in the given category.
An easy to use symbolic notation is provided which allows users to select the desired categories and associated levels from the command line and configuration files. In this article we will look at the internal aspects of these notions, from the point of view of a programmer.
Categories are represented by integer numbers. There is a set of built-in categories, described in file libmailutils/diag/debcat[1] and the include/mailutils/sys/debcat.h header, which is generated from it. These categories can be referred to in program text by their macro names. For example, the category auth is available under the name MU_DEBCAT_AUTH, and so on. To access these names, include the file mailutils/debug.h.
Applications can also register their own category names. It is useful, for example, for loadable libraries. A new category is registered by calling the mu_debug_register_category function:
size_t mu_debug_register_category (char *name);
The name argument supplies the symbolic name for this category, under which it will be available for users. The function returns a category index which can subsequently be used in mu_debug* calls (see below).
Levels are implemented as integer numbers, which can be combined to form level bitmasks. There are 12 levels available:
- MU_DEBUG_ERROR
- Mild error conditions which are normally not reported, but passed back to the caller layers for handling.
- MU_DEBUG_TRACE0 through MU_DEBUG_TRACE9
- Ten levels of verbosity, from minimal to the maximum amount of output.
- MU_DEBUG_PROT
- Displays network protocol interaction, where applicable.
Two macros are available for creating level masks:
MU_DEBUG_LEVEL_MASK(n) creates a level mask for the level n. Such masks can be combined using binary 'OR', e.g.
MU_DEBUG_LEVEL_MASK(MU_DEBUG_TRACE6) | MU_DEBUG_LEVEL_MASK(MU_DEBUG_PROT)
MU_DEBUG_LEVEL_UPTO(n) creates a mask containing all levels from MU_DEBUG_ERROR up to and including n.
The function mu_debug_level_p is provided to check whether a given level is set for a given category:
int mu_debug_level_p (int category, int level);
The mu_debug macro
The mu_debug macro is the basic mechanism for implementing debugging output in programs:
mu_debug(category, level, (args))
The first two arguments specify the category and level, and the third one must be an argument list suitable for the mu_debug_log invocation.
The macro tests if the level is set for the given category, and if so, it invokes mu_debug_log with the argument list (args). Additionally. if the mu_debug_line_info global variable is set, it arranges for the current source location to be shown in the output. For example, the following invocation:
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1, ("mbox_scan (%s) returned %d", mud->name, result));
will format the argument list given as its third argument if the mailbox.=trace1 level is set.
The mu_debug_log function family
The mu_debug_log functions are the mechanism used by the mu_debug macro to do the actual output:
void mu_debug_log (const char *fmt, ...);
void mu_debug_log_begin (const char *fmt, ...);
void mu_debug_log_cont (const char *fmt, ...);
void mu_debug_log_end (const char *fmt, ...);
All functions are variadic, with their argument fmt argument being a usual printf format specification[2], and the rest of arguments supplying the actual data to output.
The mu_debug_log function formats its arguments, and then sends the formatted data terminated with a newline character to the mu_strerr stream. Therefore, the fmt argument to this function must not contain terminating \n.
If you need to output debugging information in several chunks but want these chunks to form a single line on output, do as follows:
- Call mu_debug_log_begin. This function prepares the log stream for subsequent continuous output, formats its data and sends them down the stream as the first chunk.
- Call mu_debug_log_cont as much times as needed (perhaps within a loop) to format more parts of the output sequence. Neither mu_debug_log_begin nor mu_debug_log_cont add newlines to their output, so until now the output is not flushed.
- Send the lest portion of data using the mu_debug_log_end call. After formatting its data, the function sends a terminating newline, thereby flushing the formatted output to the log stream.
You can also terminate the line with a call to mu_debug_log_nl() function, if there is no more data left for output:
void mu_debug_log_nl (void);
The following code fragment illustrates this approach.
if (mu_debug_level_p (MU_DEBCAT_FILTER, MU_DEBUG_TRACE1))
{
int i;
mu_debug_log_begin ("Command line was:");
for (i = 0; argv[i]; i++)
mu_debug_log_cont (" %s", argv[i]);
mu_debug_log_nl ();
}
This code checks if the level filter.=trace1 is set, and if so formats the array char **argv into the log output stream.
The mu_strerr stream
The stream mu_strerr is a Mailutils counterpart of the traditional stderr stream, except that it provides much more possibilities. All writes to this stream are directed to whatever log transport stream was specified in the Mailutils configuration file.
The stream is line-buffered, so the output gets flushed after each \n character written to it. Upon writing to the transport stream, a diagnostic prefix is output before each lineful of data. The prefix consists of:
- The value of mu_log_tag variable, followed by a colon and space character;
- Input stream location, if it is set;
- Optionally, if mu_log_print_severity is set, the severity designator, such as debug for debugging information, warning for warnings, etc.
Mu_strerr is an instance of log stream, and supports all ioctls and format directives supported by it. For example, to set location you can either use the MU_IOCTL_LOGSTREAM ioctl, or (to set it temporarily, for this line of output only), the f and l log escapes, as shown in the example code below:
mu_stream_printf (mu_strerr, "\033f<%d>%s\033l<%d>%s", strlen (file), file, line,
"bad input")
Notes
- ↑ Predefined debug categories (Git repository)
- ↑ The printf function