in src/runtime/logging.cc [95:207]
int BacktraceFullCallback(void* data, uintptr_t pc, const char* filename, int lineno,
const char* symbol) {
auto stack_trace = reinterpret_cast<BacktraceInfo*>(data);
std::unique_ptr<std::string> symbol_str = std::make_unique<std::string>("<unknown>");
if (symbol) {
*symbol_str = DemangleName(symbol);
} else {
// see if syminfo gives anything
backtrace_syminfo(_bt_state, pc, BacktraceSyminfoCallback, BacktraceErrorCallback,
symbol_str.get());
}
symbol = symbol_str->data();
// TVMFuncCall denotes the API boundary so we stop there. Exceptions
// should be caught there. This is before any frame suppressions,
// as it would otherwise be suppressed.
bool should_stop_collecting =
(*symbol_str == "TVMFuncCall" || stack_trace->lines.size() >= stack_trace->max_size);
if (should_stop_collecting) {
return 1;
}
// Exclude frames that contain little useful information for most
// debugging purposes
bool should_exclude = [&]() -> bool {
if (filename) {
// Stack frames for TVM FFI
if (strstr(filename, "include/tvm/runtime/packed_func.h") ||
strstr(filename, "include/tvm/runtime/registry.h") ||
strstr(filename, "src/runtime/c_runtime_api.cc")) {
return true;
}
// Stack frames for nested tree recursion.
// tir/ir/stmt_functor.cc and tir/ir/expr_functor.cc define
// Expr/Stmt Visitor/Mutator, which should be suppressed, but
// also Substitute which should not be suppressed. Therefore,
// they are suppressed based on the symbol name.
if (strstr(filename, "include/tvm/node/functor.h") || //
strstr(filename, "include/tvm/relax/expr_functor.h") || //
strstr(filename, "include/tvm/tir/stmt_functor.h") || //
strstr(filename, "include/tvm/tir/expr_functor.h") || //
strstr(filename, "include/tvm/node/reflection.h") || //
strstr(filename, "src/node/structural_equal.cc") || //
strstr(filename, "src/ir/transform.cc") || //
strstr(filename, "src/relax/ir/expr_functor.cc") || //
strstr(filename, "src/relax/ir/py_expr_functor.cc")) {
return true;
}
// Python interpreter stack frames
if (strstr(filename, "/python-") || strstr(filename, "/Python/ceval.c") ||
strstr(filename, "/Modules/_ctypes")) {
return true;
}
// C++ stdlib frames
if (strstr(filename, "include/c++/")) {
return true;
}
}
if (symbol) {
// C++ stdlib frames
if (strstr(symbol, "__libc_")) {
return true;
}
// Stack frames for nested tree visiting
if (strstr(symbol, "tvm::tir::StmtMutator::VisitStmt_") ||
strstr(symbol, "tvm::tir::ExprMutator::VisitExpr_") ||
strstr(symbol, "tvm::tir::IRTransformer::VisitExpr") ||
strstr(symbol, "tvm::tir::IRTransformer::VisitStmt") ||
strstr(symbol, "tvm::tir::IRTransformer::BaseVisitExpr") ||
strstr(symbol, "tvm::tir::IRTransformer::BaseVisitStmt")) {
return true;
}
// Python interpreter stack frames
if (strstr(symbol, "_Py") == symbol || strstr(symbol, "PyObject")) {
return true;
}
}
// libffi.so stack frames. These may also show up as numeric
// addresses with no symbol name. This could be improved in the
// future by using dladdr() to check whether an address is contained
// in libffi.so
if (filename == nullptr && strstr(symbol, "ffi_call_")) {
return true;
}
// Skip tvm::backtrace and tvm::LogFatal::~LogFatal at the beginning
// of the trace as they don't add anything useful to the backtrace.
if (stack_trace->lines.size() == 0 && (strstr(symbol, "tvm::runtime::Backtrace") ||
strstr(symbol, "tvm::runtime::detail::LogFatal"))) {
return true;
}
return false;
}();
if (should_exclude) {
return 0;
}
std::stringstream frame_str;
frame_str << *symbol_str;
if (filename) {
frame_str << std::endl << " at " << filename;
if (lineno != 0) {
frame_str << ":" << lineno;
}
}
stack_trace->lines.push_back(frame_str.str());
return 0;
}