kaldi-error.cc 6.78 KB
Newer Older
1 2
// base/kaldi-error.cc

3
// Copyright 2009-2011  Microsoft Corporation;  Lukas Burget;  Ondrej Glembek
4

5 6
// See ../../COPYING for clarification regarding multiple authors
//
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.

#ifdef HAVE_EXECINFO_H
#include <execinfo.h>  // 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 <cxxabi.h>  // 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

#include "base/kaldi-common.h"
#include "base/kaldi-error.h"

namespace kaldi {
int32 g_kaldi_verbose_level = 0;  // Just initialize this global variable.
36
const char *g_program_name = NULL;
37 38 39 40

// If the program name was set (g_program_name != ""), the function
// GetProgramName returns the program name (without the path) followed by a
// colon, e.g. "gmm-align:".  Otherwise it returns the empty string "".
41 42 43
const char *GetProgramName() {
  if (g_program_name == NULL) return "";
  else return g_program_name;
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
}

// Given a filename like "/a/b/c/d/e/f.cc",  GetShortFileName
// returns "e/f.cc".  Does not currently work if backslash is
// the filename separator.
const char *GetShortFileName(const char *filename) {
  const char *last_slash = strrchr(filename, '/');
  if (!last_slash) { return filename; }
  else {
    while (last_slash > filename && last_slash[-1] != '/')
      last_slash--;
    return last_slash;
  }
}


#if defined(HAVE_CXXABI_H) && defined(HAVE_EXECINFO_H)
// The function name looks like a macro: it's a macro if we don't have ccxxabi.h
inline void KALDI_APPEND_POSSIBLY_DEMANGLED_STRING(std::string &ans,  
                                                   const char *to_append) {
  // at input the string "to_append" looks like:
  //   ./kaldi-error-test(_ZN5kaldi13UnitTestErrorEv+0xb) [0x804965d]
  // We want to extract the name e.g. '_ZN5kaldi13UnitTestErrorEv",
  // demangle it and return it.
  int32 status;
  const char *paren = strchr(to_append, '(');
  const char *plus = (paren ? strchr(paren, '+') : NULL);
  if (!plus) {  // did not find the '(' or did not find the '+'
    // This is a soft failure in case we did not get what we expected.
    ans += to_append;
    return;
  }
  std::string stripped(paren+1, plus-(paren+1));  // the bit between ( and +.

  char *demangled_name = abi::__cxa_demangle(stripped.c_str(), 0, 0, &status);

  // if status != 0 it is an error (demangling failure),  but not all names seem
  // to demangle, so we don't check it.

  if (demangled_name != NULL) {
    ans += demangled_name;
    free(demangled_name);
  } else {
    ans += to_append;  // add the original string.
  }
}
#else  // defined(HAVE_CXXABI_H) && defined(HAVE_EXECINFO_H)
#define KALDI_APPEND_POSSIBLY_DEMANGLED_STRING(ans, to_append) ans += to_append
#endif  // defined(HAVE_CXXABI_H) && defined(HAVE_EXECINFO_H)

#ifdef HAVE_EXECINFO_H
std::string KaldiGetStackTrace() {
#define KALDI_MAX_TRACE_SIZE 50
#define KALDI_MAX_TRACE_PRINT 10  // must be even.
  std::string ans;
  void *array[KALDI_MAX_TRACE_SIZE];
  size_t size = backtrace(array, KALDI_MAX_TRACE_SIZE);
  char **strings = backtrace_symbols(array, size);
  if (size <= KALDI_MAX_TRACE_PRINT) {
    for (size_t i = 0; i < size; i++) {
      KALDI_APPEND_POSSIBLY_DEMANGLED_STRING(ans, strings[i]);
      ans += "\n";
    }
  } else {  // print out first+last (e.g.) 5.
    for (size_t i = 0; i < KALDI_MAX_TRACE_PRINT/2; i++) {
      KALDI_APPEND_POSSIBLY_DEMANGLED_STRING(ans, strings[i]);
      ans += "\n";
    }
    ans += ".\n.\n.\n";
    for (size_t i = size - KALDI_MAX_TRACE_PRINT/2; i < size; i++) {
      KALDI_APPEND_POSSIBLY_DEMANGLED_STRING(ans, strings[i]);
      ans += "\n";
    }
    if (size == KALDI_MAX_TRACE_SIZE)
      ans += ".\n.\n.\n";  // stack was too long, probably a bug.
  }
  free(strings);  // it's all in one big malloc()ed block.


#ifdef HAVE_CXXABI_H  // demangle the name, if possible.
#endif  // HAVE_CXXABI_H
  return ans;
}
#endif

void KaldiAssertFailure_(const char *func, const char *file,
                         int32 line, const char *cond_str) {
131 132 133 134
  std::ostringstream ss;
  ss << "KALDI_ASSERT: at " << GetProgramName() << func << ':'
     << GetShortFileName(file)
     << ':' << line << ", failed: " << cond_str << '\n';
135
#ifdef HAVE_EXECINFO_H
136
  ss << "Stack trace is:\n" << KaldiGetStackTrace();
137
#endif
138
  std::cerr << ss.str();
139
  std::cerr.flush();
140 141 142 143
  // We used to call abort() here, but switch to throwing an exception
  // (like KALDI_ERR) because it's easier to deal with in multi-threaded
  // code.
  throw std::runtime_error(ss.str());
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
}


KaldiWarnMessage::KaldiWarnMessage(const char *func, const char *file,
                                   int32 line) {
  this->stream() << "WARNING (" << GetProgramName() << func << "():"
                 << GetShortFileName(file) << ':' << line << ") ";
}


KaldiLogMessage::KaldiLogMessage(const char *func, const char *file,
                                 int32 line) {
  this->stream() << "LOG (" << GetProgramName() << func << "():"
                 << GetShortFileName(file) << ':' << line << ") ";
}


KaldiVlogMessage::KaldiVlogMessage(const char *func, const char *file,
                                   int32 line, int32 verbose) {
  this->stream() << "VLOG[" << verbose << "] (" << GetProgramName() << func
                 << "():" << GetShortFileName(file) << ':' << line << ") ";
}

KaldiErrorMessage::KaldiErrorMessage(const char *func, const char *file,
                                     int32 line) {
  this->stream() << "ERROR (" << GetProgramName() << func << "():"
                 << GetShortFileName(file) << ':' << line << ") ";
}

173
KaldiErrorMessage::~KaldiErrorMessage() KALDI_NOEXCEPT(false) {
174 175 176 177 178 179 180 181 182 183
  // (1) Print the message to stderr.
  std::cerr << ss.str() << '\n';
  // (2) Throw an exception with the message, plus traceback info if available.
  if (!std::uncaught_exception()) {
#ifdef HAVE_EXECINFO_H
    throw std::runtime_error(ss.str() + "\n\n[stack trace: ]\n" +
                             KaldiGetStackTrace() + "\n");
#else
    throw std::runtime_error(ss.str());
#endif
184 185
  } else {
    abort(); // This may be temporary...
186 187 188 189
  }
}

}  // end namespace kaldi