log.cc 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /**
  2. * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang)
  3. *
  4. * See LICENSE for clarification regarding multiple authors
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. /*
  19. * Stack trace related stuff is from kaldi.
  20. * Refer to
  21. * https://github.com/kaldi-asr/kaldi/blob/master/src/base/kaldi-error.cc
  22. */
  23. #include "log.h"
  24. #ifdef KNF_HAVE_EXECINFO_H
  25. #include <execinfo.h> // To get stack trace in error messages.
  26. #ifdef KNF_HAVE_CXXABI_H
  27. #include <cxxabi.h> // For name demangling.
  28. // Useful to decode the stack trace, but only used if we have execinfo.h
  29. #endif // KNF_HAVE_CXXABI_H
  30. #endif // KNF_HAVE_EXECINFO_H
  31. #include <stdlib.h>
  32. #include <ctime>
  33. #include <iomanip>
  34. #include <string>
  35. namespace knf {
  36. std::string GetDateTimeStr() {
  37. std::ostringstream os;
  38. std::time_t t = std::time(nullptr);
  39. std::tm tm = *std::localtime(&t);
  40. os << std::put_time(&tm, "%F %T"); // yyyy-mm-dd hh:mm:ss
  41. return os.str();
  42. }
  43. static bool LocateSymbolRange(const std::string &trace_name, std::size_t *begin,
  44. std::size_t *end) {
  45. // Find the first '_' with leading ' ' or '('.
  46. *begin = std::string::npos;
  47. for (std::size_t i = 1; i < trace_name.size(); ++i) {
  48. if (trace_name[i] != '_') {
  49. continue;
  50. }
  51. if (trace_name[i - 1] == ' ' || trace_name[i - 1] == '(') {
  52. *begin = i;
  53. break;
  54. }
  55. }
  56. if (*begin == std::string::npos) {
  57. return false;
  58. }
  59. *end = trace_name.find_first_of(" +", *begin);
  60. return *end != std::string::npos;
  61. }
  62. #ifdef KNF_HAVE_EXECINFO_H
  63. static std::string Demangle(const std::string &trace_name) {
  64. #ifndef KNF_HAVE_CXXABI_H
  65. return trace_name;
  66. #else // KNF_HAVE_CXXABI_H
  67. // Try demangle the symbol. We are trying to support the following formats
  68. // produced by different platforms:
  69. //
  70. // Linux:
  71. // ./kaldi-error-test(_ZN5kaldi13UnitTestErrorEv+0xb) [0x804965d]
  72. //
  73. // Mac:
  74. // 0 server 0x000000010f67614d _ZNK5kaldi13MessageLogger10LogMessageEv + 813
  75. //
  76. // We want to extract the name e.g., '_ZN5kaldi13UnitTestErrorEv' and
  77. // demangle it info a readable name like kaldi::UnitTextError.
  78. std::size_t begin, end;
  79. if (!LocateSymbolRange(trace_name, &begin, &end)) {
  80. return trace_name;
  81. }
  82. std::string symbol = trace_name.substr(begin, end - begin);
  83. int status;
  84. char *demangled_name = abi::__cxa_demangle(symbol.c_str(), 0, 0, &status);
  85. if (status == 0 && demangled_name != nullptr) {
  86. symbol = demangled_name;
  87. free(demangled_name);
  88. }
  89. return trace_name.substr(0, begin) + symbol +
  90. trace_name.substr(end, std::string::npos);
  91. #endif // KNF_HAVE_CXXABI_H
  92. }
  93. #endif // KNF_HAVE_EXECINFO_H
  94. std::string GetStackTrace() {
  95. std::string ans;
  96. #ifdef KNF_HAVE_EXECINFO_H
  97. constexpr const std::size_t kMaxTraceSize = 50;
  98. constexpr const std::size_t kMaxTracePrint = 50; // Must be even.
  99. // Buffer for the trace.
  100. void *trace[kMaxTraceSize];
  101. // Get the trace.
  102. std::size_t size = backtrace(trace, kMaxTraceSize);
  103. // Get the trace symbols.
  104. char **trace_symbol = backtrace_symbols(trace, size);
  105. if (trace_symbol == nullptr) return ans;
  106. // Compose a human-readable backtrace string.
  107. ans += "[ Stack-Trace: ]\n";
  108. if (size <= kMaxTracePrint) {
  109. for (std::size_t i = 0; i < size; ++i) {
  110. ans += Demangle(trace_symbol[i]) + "\n";
  111. }
  112. } else { // Print out first+last (e.g.) 5.
  113. for (std::size_t i = 0; i < kMaxTracePrint / 2; ++i) {
  114. ans += Demangle(trace_symbol[i]) + "\n";
  115. }
  116. ans += ".\n.\n.\n";
  117. for (std::size_t i = size - kMaxTracePrint / 2; i < size; ++i) {
  118. ans += Demangle(trace_symbol[i]) + "\n";
  119. }
  120. if (size == kMaxTraceSize)
  121. ans += ".\n.\n.\n"; // Stack was too long, probably a bug.
  122. }
  123. // We must free the array of pointers allocated by backtrace_symbols(),
  124. // but not the strings themselves.
  125. free(trace_symbol);
  126. #endif // KNF_HAVE_EXECINFO_H
  127. return ans;
  128. }
  129. } // namespace knf