janitor

janitor
git clone https://git.prime8.dev/janitor.git
Log | Files | Refs | README | LICENSE

log.cc (4507B)


      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 /*
     20  * Stack trace related stuff is from kaldi.
     21  * Refer to
     22  * https://github.com/kaldi-asr/kaldi/blob/master/src/base/kaldi-error.cc
     23  */
     24 
     25 #include "kaldi-native-fbank/csrc/log.h"
     26 
     27 #ifdef KNF_HAVE_EXECINFO_H
     28 #include <execinfo.h>  // To get stack trace in error messages.
     29 #ifdef KNF_HAVE_CXXABI_H
     30 #include <cxxabi.h>  // For name demangling.
     31 // Useful to decode the stack trace, but only used if we have execinfo.h
     32 #endif  // KNF_HAVE_CXXABI_H
     33 #endif  // KNF_HAVE_EXECINFO_H
     34 
     35 #include <stdlib.h>
     36 
     37 #include <ctime>
     38 #include <iomanip>
     39 #include <string>
     40 
     41 namespace knf {
     42 
     43 std::string GetDateTimeStr() {
     44   std::ostringstream os;
     45   std::time_t t = std::time(nullptr);
     46   std::tm tm = *std::localtime(&t);
     47   os << std::put_time(&tm, "%F %T");  // yyyy-mm-dd hh:mm:ss
     48   return os.str();
     49 }
     50 
     51 static bool LocateSymbolRange(const std::string &trace_name, std::size_t *begin,
     52                               std::size_t *end) {
     53   // Find the first '_' with leading ' ' or '('.
     54   *begin = std::string::npos;
     55   for (std::size_t i = 1; i < trace_name.size(); ++i) {
     56     if (trace_name[i] != '_') {
     57       continue;
     58     }
     59     if (trace_name[i - 1] == ' ' || trace_name[i - 1] == '(') {
     60       *begin = i;
     61       break;
     62     }
     63   }
     64   if (*begin == std::string::npos) {
     65     return false;
     66   }
     67   *end = trace_name.find_first_of(" +", *begin);
     68   return *end != std::string::npos;
     69 }
     70 
     71 #ifdef KNF_HAVE_EXECINFO_H
     72 static std::string Demangle(const std::string &trace_name) {
     73 #ifndef KNF_HAVE_CXXABI_H
     74   return trace_name;
     75 #else   // KNF_HAVE_CXXABI_H
     76   // Try demangle the symbol. We are trying to support the following formats
     77   // produced by different platforms:
     78   //
     79   // Linux:
     80   //   ./kaldi-error-test(_ZN5kaldi13UnitTestErrorEv+0xb) [0x804965d]
     81   //
     82   // Mac:
     83   //   0 server 0x000000010f67614d _ZNK5kaldi13MessageLogger10LogMessageEv + 813
     84   //
     85   // We want to extract the name e.g., '_ZN5kaldi13UnitTestErrorEv' and
     86   // demangle it info a readable name like kaldi::UnitTextError.
     87   std::size_t begin, end;
     88   if (!LocateSymbolRange(trace_name, &begin, &end)) {
     89     return trace_name;
     90   }
     91   std::string symbol = trace_name.substr(begin, end - begin);
     92   int status;
     93   char *demangled_name = abi::__cxa_demangle(symbol.c_str(), 0, 0, &status);
     94   if (status == 0 && demangled_name != nullptr) {
     95     symbol = demangled_name;
     96     free(demangled_name);
     97   }
     98   return trace_name.substr(0, begin) + symbol +
     99          trace_name.substr(end, std::string::npos);
    100 #endif  // KNF_HAVE_CXXABI_H
    101 }
    102 #endif  // KNF_HAVE_EXECINFO_H
    103 
    104 std::string GetStackTrace() {
    105   std::string ans;
    106 #ifdef KNF_HAVE_EXECINFO_H
    107   constexpr const std::size_t kMaxTraceSize = 50;
    108   constexpr const std::size_t kMaxTracePrint = 50;  // Must be even.
    109                                                     // Buffer for the trace.
    110   void *trace[kMaxTraceSize];
    111   // Get the trace.
    112   std::size_t size = backtrace(trace, kMaxTraceSize);
    113   // Get the trace symbols.
    114   char **trace_symbol = backtrace_symbols(trace, size);
    115   if (trace_symbol == nullptr) return ans;
    116 
    117   // Compose a human-readable backtrace string.
    118   ans += "[ Stack-Trace: ]\n";
    119   if (size <= kMaxTracePrint) {
    120     for (std::size_t i = 0; i < size; ++i) {
    121       ans += Demangle(trace_symbol[i]) + "\n";
    122     }
    123   } else {  // Print out first+last (e.g.) 5.
    124     for (std::size_t i = 0; i < kMaxTracePrint / 2; ++i) {
    125       ans += Demangle(trace_symbol[i]) + "\n";
    126     }
    127     ans += ".\n.\n.\n";
    128     for (std::size_t i = size - kMaxTracePrint / 2; i < size; ++i) {
    129       ans += Demangle(trace_symbol[i]) + "\n";
    130     }
    131     if (size == kMaxTraceSize)
    132       ans += ".\n.\n.\n";  // Stack was too long, probably a bug.
    133   }
    134 
    135   // We must free the array of pointers allocated by backtrace_symbols(),
    136   // but not the strings themselves.
    137   free(trace_symbol);
    138 #endif  // KNF_HAVE_EXECINFO_H
    139   return ans;
    140 }
    141 
    142 }  // namespace knf