diff --git a/src/base/kaldi-error-test.cc b/src/base/kaldi-error-test.cc index 462ad956907..31440edf3f9 100644 --- a/src/base/kaldi-error-test.cc +++ b/src/base/kaldi-error-test.cc @@ -17,19 +17,14 @@ // See the Apache 2 License for the specific language governing permissions and // limitations under the License. - #include "base/kaldi-common.h" // testing that we get the stack trace. namespace kaldi { -void MyFunction2() { - KALDI_ERR << "Ignore this error"; -} +void MyFunction2() { KALDI_ERR << "Ignore this error"; } -void MyFunction1() { - MyFunction2(); -} +void MyFunction1() { MyFunction2(); } void UnitTestError() { { @@ -38,16 +33,50 @@ void UnitTestError() { } } +void VerifySymbolRange(const std::string &trace, const bool want_found, + const std::string &want_symbol) { + size_t begin, end; + const bool found = internal::LocateSymbolRange(trace, &begin, &end); + if (found != want_found) { + KALDI_ERR << "Found mismatch, got " << found << " want " << want_found; + } + if (!found) { + return; + } + const std::string symbol = trace.substr(begin, end - begin); + if (symbol != want_symbol) { + KALDI_ERR << "Symbol mismatch, got " << symbol << " want " << want_symbol; + } +} + +void TestLocateSymbolRange() { + VerifySymbolRange("", false, ""); + VerifySymbolRange( + R"TRACE(./kaldi-error-test(_ZN5kaldi13UnitTestErrorEv+0xb) [0x804965d])TRACE", + true, "_ZN5kaldi13UnitTestErrorEv"); + // It is ok thread_start is not found because it is a C symbol. + VerifySymbolRange( + R"TRACE(31 libsystem_pthread.dylib 0x00007fff6fe4e40d thread_start + 13)TRACE", + false, ""); + VerifySymbolRange( + R"TRACE(0 server 0x000000010f67614d _ZNK5kaldi13MessageLogger10LogMessageEv + 813)TRACE", + true, "_ZNK5kaldi13MessageLogger10LogMessageEv"); + VerifySymbolRange( + R"TRACE(29 libsystem_pthread.dylib 0x00007fff6fe4f2eb _pthread_body + 126)TRACE", + true, "_pthread_body"); +} -} // end namespace kaldi. +} // namespace kaldi int main() { + kaldi::TestLocateSymbolRange(); + kaldi::SetProgramName("/foo/bar/kaldi-error-test"); try { kaldi::UnitTestError(); - KALDI_ASSERT(0); // should not happen. + KALDI_ASSERT(0); // should not happen. exit(1); - } catch(kaldi::KaldiFatalError &e) { + } catch (kaldi::KaldiFatalError &e) { std::cout << "The error we generated was: '" << e.KaldiMessage() << "'\n"; } } diff --git a/src/base/kaldi-error.cc b/src/base/kaldi-error.cc index 9705936466c..2dbc7318209 100644 --- a/src/base/kaldi-error.cc +++ b/src/base/kaldi-error.cc @@ -1,5 +1,6 @@ // base/kaldi-error.cc +// Copyright 2019 LAIX (Yi Sun) // Copyright 2019 SmartAction LLC (kkm) // Copyright 2016 Brno University of Technology (author: Karel Vesely) // Copyright 2009-2011 Microsoft Corporation; Lukas Burget; Ondrej Glembek @@ -20,15 +21,15 @@ // limitations under the License. #ifdef HAVE_EXECINFO_H -#include // To get stack trace in error messages. +#include // To get stack trace in error messages. // If this #include fails there is an error in the Makefile, it does not // support your platform well. Make sure HAVE_EXECINFO_H is undefined, // and the code will compile. #ifdef HAVE_CXXABI_H -#include // For name demangling. +#include // For name demangling. // Useful to decode the stack trace, but only used if we have execinfo.h -#endif // HAVE_CXXABI_H -#endif // HAVE_EXECINFO_H +#endif // HAVE_CXXABI_H +#endif // HAVE_EXECINFO_H #include "base/kaldi-common.h" #include "base/kaldi-error.h" @@ -36,7 +37,6 @@ namespace kaldi { - /***** GLOBAL VARIABLES FOR LOGGING *****/ int32 g_kaldi_verbose_level = 0; @@ -51,7 +51,6 @@ void SetProgramName(const char *basename) { program_name = basename; } - /***** HELPER FUNCTIONS *****/ // Trim filename to at most 1 trailing directory long. Given a filename like @@ -70,40 +69,68 @@ static const char *GetShortFileName(const char *path) { return prev; } - /***** STACK TRACE *****/ +namespace internal { +bool LocateSymbolRange(const std::string &trace_name, size_t *begin, + size_t *end) { + // Find the first '_' with leading ' ' or '('. + *begin = std::string::npos; + for (size_t i = 1; i < trace_name.size(); i++) { + if (trace_name[i] != '_') { + continue; + } + if (trace_name[i - 1] == ' ' || trace_name[i - 1] == '(') { + *begin = i; + break; + } + } + if (*begin == std::string::npos) { + return false; + } + *end = trace_name.find_first_of(" +", *begin); + return *end != std::string::npos; +} +} // namespace internal + #ifdef HAVE_EXECINFO_H static std::string Demangle(std::string trace_name) { -#ifdef HAVE_CXXABI_H - // At input the string looks like: +#ifndef HAVE_CXXABI_H + return trace_name; +#else // HAVE_CXXABI_H + // Try demangle the symbol. We are trying to support the following formats + // produced by different platforms: + // + // Linux: // ./kaldi-error-test(_ZN5kaldi13UnitTestErrorEv+0xb) [0x804965d] - // We want to extract the name e.g. '_ZN5kaldi13UnitTestErrorEv" - // and demangle it. - - // Try to locate '(' and '+', take the string in between. - size_t begin(trace_name.find("(")), - end(trace_name.rfind("+")); - if (begin != std::string::npos && end != std::string::npos && begin < end) { - trace_name = trace_name.substr(begin + 1, end - (begin + 1)); + // + // Mac: + // 0 server 0x000000010f67614d _ZNK5kaldi13MessageLogger10LogMessageEv + 813 + // + // We want to extract the name e.g., '_ZN5kaldi13UnitTestErrorEv' and + // demangle it info a readable name like kaldi::UnitTextError. + size_t begin, end; + if (!internal::LocateSymbolRange(trace_name, &begin, &end)) { + return trace_name; } - // Try to demangle function name. + std::string symbol = trace_name.substr(begin, end - begin); int status; - char *demangled_name = abi::__cxa_demangle(trace_name.c_str(), 0, 0, &status); - if (status == 0 && demangled_name != NULL) { - trace_name = demangled_name; + char *demangled_name = abi::__cxa_demangle(symbol.c_str(), 0, 0, &status); + if (status == 0 && demangled_name != nullptr) { + symbol = demangled_name; free(demangled_name); } -#endif // HAVE_CXXABI_H - return trace_name; + return trace_name.substr(0, begin) + symbol + + trace_name.substr(end, std::string::npos); +#endif // HAVE_CXXABI_H } -#endif // HAVE_EXECINFO_H +#endif // HAVE_EXECINFO_H static std::string KaldiGetStackTrace() { std::string ans; #ifdef HAVE_EXECINFO_H const size_t KALDI_MAX_TRACE_SIZE = 50; - const size_t KALDI_MAX_TRACE_PRINT = 20; // Must be even. + const size_t KALDI_MAX_TRACE_PRINT = 50; // Must be even. // Buffer for the trace. void *trace[KALDI_MAX_TRACE_SIZE]; // Get the trace. @@ -119,26 +146,25 @@ static std::string KaldiGetStackTrace() { for (size_t i = 0; i < size; i++) { ans += Demangle(trace_symbol[i]) + "\n"; } - } else { // Print out first+last (e.g.) 5. - for (size_t i = 0; i < KALDI_MAX_TRACE_PRINT/2; i++) { + } else { // Print out first+last (e.g.) 5. + for (size_t i = 0; i < KALDI_MAX_TRACE_PRINT / 2; i++) { ans += Demangle(trace_symbol[i]) + "\n"; } ans += ".\n.\n.\n"; - for (size_t i = size - KALDI_MAX_TRACE_PRINT/2; i < size; i++) { + for (size_t i = size - KALDI_MAX_TRACE_PRINT / 2; i < size; i++) { ans += Demangle(trace_symbol[i]) + "\n"; } if (size == KALDI_MAX_TRACE_SIZE) - ans += ".\n.\n.\n"; // Stack was too long, probably a bug. + ans += ".\n.\n.\n"; // Stack was too long, probably a bug. } // We must free the array of pointers allocated by backtrace_symbols(), // but not the strings themselves. free(trace_symbol); -#endif // HAVE_EXECINFO_H +#endif // HAVE_EXECINFO_H return ans; } - /***** KALDI LOGGING *****/ MessageLogger::MessageLogger(LogMessageEnvelope::Severity severity, @@ -146,7 +172,7 @@ MessageLogger::MessageLogger(LogMessageEnvelope::Severity severity, // Obviously, we assume the strings survive the destruction of this object. envelope_.severity = severity; envelope_.func = func; - envelope_.file = GetShortFileName(file); // Points inside 'file'. + envelope_.file = GetShortFileName(file); // Points inside 'file'. envelope_.line = line; } @@ -164,29 +190,29 @@ void MessageLogger::LogMessage() const { full_message << "VLOG[" << envelope_.severity << "] ("; } else { switch (envelope_.severity) { - case LogMessageEnvelope::kInfo : + case LogMessageEnvelope::kInfo: full_message << "LOG ("; break; - case LogMessageEnvelope::kWarning : + case LogMessageEnvelope::kWarning: full_message << "WARNING ("; break; - case LogMessageEnvelope::kAssertFailed : + case LogMessageEnvelope::kAssertFailed: full_message << "ASSERTION_FAILED ("; break; - case LogMessageEnvelope::kError : - default: // If not the ERROR, it still an error! + case LogMessageEnvelope::kError: + default: // If not the ERROR, it still an error! full_message << "ERROR ("; break; } } // Add other info from the envelope and the message text. full_message << program_name.c_str() << "[" KALDI_VERSION "]" << ':' - << envelope_.func << "():" << envelope_.file << ':' + << envelope_.func << "():" << envelope_.file << ':' << envelope_.line << ") " << GetMessage().c_str(); // Add stack trace for errors and assertion failures, if available. if (envelope_.severity < LogMessageEnvelope::kWarning) { - const std::string& stack_trace = KaldiGetStackTrace(); + const std::string &stack_trace = KaldiGetStackTrace(); if (!stack_trace.empty()) { full_message << "\n\n" << stack_trace; } @@ -197,19 +223,17 @@ void MessageLogger::LogMessage() const { std::cerr << full_message.str(); } - /***** KALDI ASSERTS *****/ -void KaldiAssertFailure_(const char *func, const char *file, - int32 line, const char *cond_str) { +void KaldiAssertFailure_(const char *func, const char *file, int32 line, + const char *cond_str) { MessageLogger::Log() = - MessageLogger (LogMessageEnvelope::kAssertFailed, func, file, line) + MessageLogger(LogMessageEnvelope::kAssertFailed, func, file, line) << "Assertion failed: (" << cond_str << ")"; - fflush(NULL); // Flush all pending buffers, abort() may not flush stderr. + fflush(NULL); // Flush all pending buffers, abort() may not flush stderr. std::abort(); } - /***** THIRD-PARTY LOG-HANDLER *****/ LogHandler SetLogHandler(LogHandler handler) { @@ -218,4 +242,4 @@ LogHandler SetLogHandler(LogHandler handler) { return old_handler; } -} // namespace kaldi +} // namespace kaldi diff --git a/src/base/kaldi-error.h b/src/base/kaldi-error.h index c90a18b15f1..a9904a752cd 100644 --- a/src/base/kaldi-error.h +++ b/src/base/kaldi-error.h @@ -1,5 +1,6 @@ // base/kaldi-error.h +// Copyright 2019 LAIX (Yi Sun) // Copyright 2019 SmartAction LLC (kkm) // Copyright 2016 Brno University of Technology (author: Karel Vesely) // Copyright 2009-2011 Microsoft Corporation; Ondrej Glembek; Lukas Burget; @@ -62,7 +63,6 @@ inline int32 GetVerboseLevel() { return g_kaldi_verbose_level; } /// command-line programs set the verbose level automatically from ParseOptions. inline void SetVerboseLevel(int32 i) { g_kaldi_verbose_level = i; } - /***** KALDI LOGGING *****/ /// Log message severity and source location info. @@ -72,26 +72,25 @@ struct LogMessageEnvelope { /// SetVerboseLevel() has been called to set logging level to at least the /// corresponding value. enum Severity { - kAssertFailed = -3, //!< Assertion failure. abort() will be called. - kError = -2, //!< Fatal error. KaldiFatalError will be thrown. - kWarning = -1, //!< Indicates a recoverable but abnormal condition. - kInfo = 0, //!< Informational message. + kAssertFailed = -3, //!< Assertion failure. abort() will be called. + kError = -2, //!< Fatal error. KaldiFatalError will be thrown. + kWarning = -1, //!< Indicates a recoverable but abnormal condition. + kInfo = 0, //!< Informational message. }; - int severity; //!< A Severity value, or positive verbosity level. - const char *func; //!< Name of the function invoking the logging. - const char *file; //!< Source file name with up to 1 leading directory. - int32 line; // - MessageLogger &operator<<(const T &val) { + template MessageLogger &operator<<(const T &val) { ss_ << val; return *this; } // When assigned a MessageLogger, log its contents. struct Log final { - void operator=(const MessageLogger& logger) { - logger.LogMessage(); - } + void operator=(const MessageLogger &logger) { logger.LogMessage(); } }; // When assigned a MessageLogger, log its contents and then throw // a KaldiFatalError. struct LogAndThrow final { - [[ noreturn ]] void operator=(const MessageLogger& logger) { + [[noreturn]] void operator=(const MessageLogger &logger) { logger.LogMessage(); throw KaldiFatalError(logger.GetMessage()); } @@ -150,29 +144,25 @@ class MessageLogger { }; // Logging macros. -#define KALDI_ERR \ - ::kaldi::MessageLogger::LogAndThrow() = \ - ::kaldi::MessageLogger(::kaldi::LogMessageEnvelope::kError, \ - __func__, __FILE__, __LINE__) -#define KALDI_WARN \ - ::kaldi::MessageLogger::Log() = \ - ::kaldi::MessageLogger(::kaldi::LogMessageEnvelope::kWarning, \ - __func__, __FILE__, __LINE__) -#define KALDI_LOG \ - ::kaldi::MessageLogger::Log() = \ - ::kaldi::MessageLogger(::kaldi::LogMessageEnvelope::kInfo, \ - __func__, __FILE__, __LINE__) -#define KALDI_VLOG(v) \ - if ((v) <= ::kaldi::GetVerboseLevel()) \ - ::kaldi::MessageLogger::Log() = \ - ::kaldi::MessageLogger((::kaldi::LogMessageEnvelope::Severity)(v), \ +#define KALDI_ERR \ + ::kaldi::MessageLogger::LogAndThrow() = ::kaldi::MessageLogger( \ + ::kaldi::LogMessageEnvelope::kError, __func__, __FILE__, __LINE__) +#define KALDI_WARN \ + ::kaldi::MessageLogger::Log() = ::kaldi::MessageLogger( \ + ::kaldi::LogMessageEnvelope::kWarning, __func__, __FILE__, __LINE__) +#define KALDI_LOG \ + ::kaldi::MessageLogger::Log() = ::kaldi::MessageLogger( \ + ::kaldi::LogMessageEnvelope::kInfo, __func__, __FILE__, __LINE__) +#define KALDI_VLOG(v) \ + if ((v) <= ::kaldi::GetVerboseLevel()) \ + ::kaldi::MessageLogger::Log() = \ + ::kaldi::MessageLogger((::kaldi::LogMessageEnvelope::Severity)(v), \ __func__, __FILE__, __LINE__) - /***** KALDI ASSERTS *****/ -[[ noreturn ]] void KaldiAssertFailure_(const char *func, const char *file, - int32 line, const char *cond_str); +[[noreturn]] void KaldiAssertFailure_(const char *func, const char *file, + int32 line, const char *cond_str); // Note on KALDI_ASSERT and KALDI_PARANOID_ASSERT: // @@ -192,21 +182,30 @@ class MessageLogger { // KALDI_ASSERT_IS_INTEGER_TYPE and KALDI_ASSERT_IS_FLOATING_TYPE, also defined // there. #ifndef NDEBUG -#define KALDI_ASSERT(cond) do { if (cond) (void)0; else \ - ::kaldi::KaldiAssertFailure_(__func__, __FILE__, __LINE__, #cond); } while(0) +#define KALDI_ASSERT(cond) \ + do { \ + if (cond) \ + (void)0; \ + else \ + ::kaldi::KaldiAssertFailure_(__func__, __FILE__, __LINE__, #cond); \ + } while (0) #else #define KALDI_ASSERT(cond) (void)0 #endif // Some more expensive asserts only checked if this defined. #ifdef KALDI_PARANOID -#define KALDI_PARANOID_ASSERT(cond) do { if (cond) (void)0; else \ - ::kaldi::KaldiAssertFailure_(__func__, __FILE__, __LINE__, #cond); } while(0) +#define KALDI_PARANOID_ASSERT(cond) \ + do { \ + if (cond) \ + (void)0; \ + else \ + ::kaldi::KaldiAssertFailure_(__func__, __FILE__, __LINE__, #cond); \ + } while (0) #else #define KALDI_PARANOID_ASSERT(cond) (void)0 #endif - /***** THIRD-PARTY LOG-HANDLER *****/ /// Type of third-party logging function. @@ -222,6 +221,11 @@ LogHandler SetLogHandler(LogHandler); /// @} end "addtogroup error_group" -} // namespace kaldi +// Functions within internal is exported for testing only, do not use. +namespace internal { +bool LocateSymbolRange(const std::string &trace_name, size_t *begin, + size_t *end); +} // namespace internal +} // namespace kaldi -#endif // KALDI_BASE_KALDI_ERROR_H_ +#endif // KALDI_BASE_KALDI_ERROR_H_