kaldi-error.cc 7.9 KB

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