kaldi-error.cc
Go to the documentation of this file.
1 // base/kaldi-error.cc
2 
3 // Copyright 2019 LAIX (Yi Sun)
4 // Copyright 2019 SmartAction LLC (kkm)
5 // Copyright 2016 Brno University of Technology (author: Karel Vesely)
6 // Copyright 2009-2011 Microsoft Corporation; Lukas Burget; Ondrej Glembek
7 
8 // See ../../COPYING for clarification regarding multiple authors
9 //
10 // Licensed under the Apache License, Version 2.0 (the "License");
11 // you may not use this file except in compliance with the License.
12 // You may obtain a copy of the License at
13 //
14 // http://www.apache.org/licenses/LICENSE-2.0
15 //
16 // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
18 // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
19 // MERCHANTABLITY OR NON-INFRINGEMENT.
20 // See the Apache 2 License for the specific language governing permissions and
21 // limitations under the License.
22 
23 #ifdef HAVE_EXECINFO_H
24 #include <execinfo.h> // To get stack trace in error messages.
25 // If this #include fails there is an error in the Makefile, it does not
26 // support your platform well. Make sure HAVE_EXECINFO_H is undefined,
27 // and the code will compile.
28 #ifdef HAVE_CXXABI_H
29 #include <cxxabi.h> // For name demangling.
30 // Useful to decode the stack trace, but only used if we have execinfo.h
31 #endif // HAVE_CXXABI_H
32 #endif // HAVE_EXECINFO_H
33 
34 #include "base/kaldi-common.h"
35 #include "base/kaldi-error.h"
36 
37 // KALDI_GIT_HEAD is useless currently in full repo
38 #if !defined(KALDI_VERSION)
39 #include "base/version.h"
40 #endif
41 
42 namespace kaldi {
43 
44 /***** GLOBAL VARIABLES FOR LOGGING *****/
45 
47 static std::string program_name;
48 static LogHandler log_handler = NULL;
49 
50 void SetProgramName(const char *basename) {
51  // Using the 'static std::string' for the program name is mostly harmless,
52  // because (a) Kaldi logging is undefined before main(), and (b) no stdc++
53  // string implementation has been found in the wild that would not be just
54  // an empty string when zero-initialized but not yet constructed.
55  program_name = basename;
56 }
57 
58 /***** HELPER FUNCTIONS *****/
59 
60 // Trim filename to at most 1 trailing directory long. Given a filename like
61 // "/a/b/c/d/e/f.cc", return "e/f.cc". Support both '/' and '\' as the path
62 // separator.
63 static const char *GetShortFileName(const char *path) {
64  if (path == nullptr)
65  return "";
66 
67  const char *prev = path, *last = path;
68  while ((path = std::strpbrk(path, "\\/")) != nullptr) {
69  ++path;
70  prev = last;
71  last = path;
72  }
73  return prev;
74 }
75 
76 /***** STACK TRACE *****/
77 
78 namespace internal {
79 bool LocateSymbolRange(const std::string &trace_name, size_t *begin,
80  size_t *end) {
81  // Find the first '_' with leading ' ' or '('.
82  *begin = std::string::npos;
83  for (size_t i = 1; i < trace_name.size(); i++) {
84  if (trace_name[i] != '_') {
85  continue;
86  }
87  if (trace_name[i - 1] == ' ' || trace_name[i - 1] == '(') {
88  *begin = i;
89  break;
90  }
91  }
92  if (*begin == std::string::npos) {
93  return false;
94  }
95  *end = trace_name.find_first_of(" +", *begin);
96  return *end != std::string::npos;
97 }
98 } // namespace internal
99 
100 #ifdef HAVE_EXECINFO_H
101 static std::string Demangle(std::string trace_name) {
102 #ifndef HAVE_CXXABI_H
103  return trace_name;
104 #else // HAVE_CXXABI_H
105  // Try demangle the symbol. We are trying to support the following formats
106  // produced by different platforms:
107  //
108  // Linux:
109  // ./kaldi-error-test(_ZN5kaldi13UnitTestErrorEv+0xb) [0x804965d]
110  //
111  // Mac:
112  // 0 server 0x000000010f67614d _ZNK5kaldi13MessageLogger10LogMessageEv + 813
113  //
114  // We want to extract the name e.g., '_ZN5kaldi13UnitTestErrorEv' and
115  // demangle it info a readable name like kaldi::UnitTextError.
116  size_t begin, end;
117  if (!internal::LocateSymbolRange(trace_name, &begin, &end)) {
118  return trace_name;
119  }
120  std::string symbol = trace_name.substr(begin, end - begin);
121  int status;
122  char *demangled_name = abi::__cxa_demangle(symbol.c_str(), 0, 0, &status);
123  if (status == 0 && demangled_name != nullptr) {
124  symbol = demangled_name;
125  free(demangled_name);
126  }
127  return trace_name.substr(0, begin) + symbol +
128  trace_name.substr(end, std::string::npos);
129 #endif // HAVE_CXXABI_H
130 }
131 #endif // HAVE_EXECINFO_H
132 
133 static std::string KaldiGetStackTrace() {
134  std::string ans;
135 #ifdef HAVE_EXECINFO_H
136  const size_t KALDI_MAX_TRACE_SIZE = 50;
137  const size_t KALDI_MAX_TRACE_PRINT = 50; // Must be even.
138  // Buffer for the trace.
139  void *trace[KALDI_MAX_TRACE_SIZE];
140  // Get the trace.
141  size_t size = backtrace(trace, KALDI_MAX_TRACE_SIZE);
142  // Get the trace symbols.
143  char **trace_symbol = backtrace_symbols(trace, size);
144  if (trace_symbol == NULL)
145  return ans;
146 
147  // Compose a human-readable backtrace string.
148  ans += "[ Stack-Trace: ]\n";
149  if (size <= KALDI_MAX_TRACE_PRINT) {
150  for (size_t i = 0; i < size; i++) {
151  ans += Demangle(trace_symbol[i]) + "\n";
152  }
153  } else { // Print out first+last (e.g.) 5.
154  for (size_t i = 0; i < KALDI_MAX_TRACE_PRINT / 2; i++) {
155  ans += Demangle(trace_symbol[i]) + "\n";
156  }
157  ans += ".\n.\n.\n";
158  for (size_t i = size - KALDI_MAX_TRACE_PRINT / 2; i < size; i++) {
159  ans += Demangle(trace_symbol[i]) + "\n";
160  }
161  if (size == KALDI_MAX_TRACE_SIZE)
162  ans += ".\n.\n.\n"; // Stack was too long, probably a bug.
163  }
164 
165  // We must free the array of pointers allocated by backtrace_symbols(),
166  // but not the strings themselves.
167  free(trace_symbol);
168 #endif // HAVE_EXECINFO_H
169  return ans;
170 }
171 
172 /***** KALDI LOGGING *****/
173 
175  const char *func, const char *file, int32 line) {
176  // Obviously, we assume the strings survive the destruction of this object.
177  envelope_.severity = severity;
178  envelope_.func = func;
179  envelope_.file = GetShortFileName(file); // Points inside 'file'.
180  envelope_.line = line;
181 }
182 
184  // Send to the logging handler if provided.
185  if (log_handler != NULL) {
186  log_handler(envelope_, GetMessage().c_str());
187  return;
188  }
189 
190  // Otherwise, use the default Kaldi logging.
191  // Build the log-message header.
192  std::stringstream full_message;
193  if (envelope_.severity > LogMessageEnvelope::kInfo) {
194  full_message << "VLOG[" << envelope_.severity << "] (";
195  } else {
196  switch (envelope_.severity) {
198  full_message << "LOG (";
199  break;
201  full_message << "WARNING (";
202  break;
204  full_message << "ASSERTION_FAILED (";
205  break;
207  default: // If not the ERROR, it still an error!
208  full_message << "ERROR (";
209  break;
210  }
211  }
212  // Add other info from the envelope and the message text.
213  full_message << program_name.c_str() << "[" KALDI_VERSION "]" << ':'
214  << envelope_.func << "():" << envelope_.file << ':'
215  << envelope_.line << ") " << GetMessage().c_str();
216 
217  // Add stack trace for errors and assertion failures, if available.
218  if (envelope_.severity < LogMessageEnvelope::kWarning) {
219  const std::string &stack_trace = KaldiGetStackTrace();
220  if (!stack_trace.empty()) {
221  full_message << "\n\n" << stack_trace;
222  }
223  }
224 
225  // Print the complete message to stderr.
226  full_message << "\n";
227  std::cerr << full_message.str();
228 }
229 
230 /***** KALDI ASSERTS *****/
231 
232 void KaldiAssertFailure_(const char *func, const char *file, int32 line,
233  const char *cond_str) {
236  << "Assertion failed: (" << cond_str << ")";
237  fflush(NULL); // Flush all pending buffers, abort() may not flush stderr.
238  std::abort();
239 }
240 
241 /***** THIRD-PARTY LOG-HANDLER *****/
242 
244  LogHandler old_handler = log_handler;
245  log_handler = handler;
246  return old_handler;
247 }
248 
249 } // namespace kaldi
This code computes Goodness of Pronunciation (GOP) and extracts phone-level pronunciation feature for...
Definition: chain.dox:20
void LogMessage() const
Definition: kaldi-error.cc:183
MessageLogger(LogMessageEnvelope::Severity severity, const char *func, const char *file, int32 line)
The constructor stores the message&#39;s "envelope", a set of data which.
Definition: kaldi-error.cc:174
static std::string program_name
Definition: kaldi-error.cc:47
void SetProgramName(const char *basename)
Called by ParseOptions to set base name (no directory) of the executing program.
Definition: kaldi-error.cc:50
void KaldiAssertFailure_(const char *func, const char *file, int32 line, const char *cond_str)
Definition: kaldi-error.cc:232
static std::string KaldiGetStackTrace()
Definition: kaldi-error.cc:133
kaldi::int32 int32
void(* LogHandler)(const LogMessageEnvelope &envelope, const char *message)
Type of third-party logging function.
Definition: kaldi-error.h:212
Informational message.
Definition: kaldi-error.h:78
static LogHandler log_handler
Definition: kaldi-error.cc:48
double Log(double x)
Definition: kaldi-math.h:100
Severity
Message severity.
Definition: kaldi-error.h:74
LogHandler SetLogHandler(LogHandler handler)
Set logging handler.
Definition: kaldi-error.cc:243
#define KALDI_VERSION
Definition: version.h:3
Fatal error. KaldiFatalError will be thrown.
Definition: kaldi-error.h:76
int32 g_kaldi_verbose_level
This is set by util/parse-options.
Definition: kaldi-error.cc:46
static const char * GetShortFileName(const char *path)
Definition: kaldi-error.cc:63
Assertion failure. abort() will be called.
Definition: kaldi-error.h:75
bool LocateSymbolRange(const std::string &trace_name, size_t *begin, size_t *end)
Definition: kaldi-error.cc:79
Indicates a recoverable but abnormal condition.
Definition: kaldi-error.h:77