Kaldi logging and error-reporting

Overview

All output that consists of logging messages, warnings or errors is directed to the standard error in Kaldi programs. This is so that our programs can be used in of pipes without such messages becoming "mixed up" with the program's output. The most common way to produce logging, warning or error output is through the macros KALDI_LOG, KALDI_WARN and KALDI_ERR. Invoking the KALDI_ERR macro will normally terminate the program (unless the exception is caught). An example code fragment that illustrates all three of these is as follows:

KALDI_LOG << "On iteration " << iter
<< ", objective function change was " << delta;
if (delta < 0.0) {
KALDI_WARN << "Negative objf change " << delta;
if (delta < -0.1)
KALDI_ERR << "Convergence failure in EM";
}

Notice that these examples don't include a newline character (this gets added automatically). A typical example of the messages that get produced by this is:

  WARNING (copy-feats:Next():util/kaldi-table-inl.h:381) Invalid archive file format

For log messages that are not quite important enough (or are too verbose) to appear in normal logs, you can use KALDI_VLOG: for example,

KALDI_VLOG(2) << "This message is not important enough to use KALDI_LOG for.";

This will get printed if the argument to the –verbose option is greater than or equal to the number in parentheses, e.g. the message above would get printed if the user called a program with –verbose=2 or greater. See Implicit command-line arguments for more context on this.

Some parts of the code print logging messages to the standard error directly; this is discouraged.

Assertions in Kaldi

Assertions should ideally be done using the macro KALDI_ASSERT. This prints more informative output than a normal assertion using assert(); KALDI_ASSERT prints a stack trace. KALDI_ASSERT is also more reconfigurable.

A typical assertion is:

KALDI_ASSERT(i < M.NumRows());

A trick that we sometimes to get more informative assert-failure message is to follow the assert condition with "&& [some string]", for example:

KALDI_ASSERT(ApproxEqual(delta, objf_change) && "Probable coding error in optimization");

If compiled normally asserts will get checked, but not if compiled with NDEBUG defined. For inner-loop assertions that use a lot of CPU, we use the following pattern:

#ifdef KALDI_PARANOID
#endif

The macro KALDI_PARANOID is on by default in the current build setup.

Exceptions thrown by KALDI_ERR

When the KALDI_ERR macro is invoked, it prints the error message to the standard error, and then throws an exception of type std::runtime_error. The string held by this exception contains the error message, and also a stack trace (if supported by the OS). The normal behavior of current Kaldi programs is to catch the exception in a try...catch block in main(), which prints the exception's string to the standard error, and exits. This typically results in the error message being printed twice.

In some cases, the error message will be caught by Kaldi code and not re-thrown. This occurs in Holder code (see Holders as helpers to Table classes) that is called by Table code (see The Table concept). Here, exceptions thrown by Read functions of Kaldi objects are caught and get propagated to the Table code as a boolean return value (e.g. see KaldiObjectHolder::Read() ). Depending on the options, such as the "p" (permissive) option to the Table code, and depending how the Table code is called, this may or may not result in another exception.

The only other type of error other than std::runtime_error that should get thrown in Kaldi code under normal circumstances is probably std::alloc_error.

Some parts of Kaldi code currently throw std::runtime_error directly, or call assert() directly, but this is to be changed to the more standard KALDI_ERR and KALDI_ASSERT macros.

Compile-time assertions in Kaldi

It is also possible to make assertions that are checked at compile time (they will produce a compilation error if they fail). This is enabled by some macros defined in kaldi-utils.h. It is especially useful to make sure that templates have been instantiated with the right kind of types. Examples of compile-time assertions are:

KALDI_COMPILE_TIME_ASSERT(kSomeConstant < 0);
...
template<class T> class foo {
};
...
template<class T> class bar {
}