blob: 346d51c79e46485c02eff346be02427bb27178e0 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*!
* \file backtrace.h
* \brief Common headers for backtrace.
* \note We use the term "backtrace" to be consistent with python naming convention.
*/
#ifndef TVM_FFI_TRACEBACK_H_
#define TVM_FFI_TRACEBACK_H_
#include <tvm/ffi/base_details.h>
#include <cstring>
#include <sstream>
#include <string>
#include <vector>
namespace tvm {
namespace ffi {
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996) // std::getenv is unsafe
#endif
inline int32_t GetBacktraceLimit() {
if (const char* env = std::getenv("TVM_TRACEBACK_LIMIT")) {
return std::stoi(env);
}
return 512;
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
/*!
* \brief List frame patterns that should be excluded as they contain less information
*/
inline bool ShouldExcludeFrame(const char* filename, const char* symbol) {
if (symbol != nullptr) {
if (strncmp(symbol, "tvm::ffi::Function", 18) == 0) {
return true;
}
if (strncmp(symbol, "tvm::ffi::details::", 19) == 0) {
return true;
}
if (strncmp(symbol, "TVMFFIBacktrace", 15) == 0) {
return true;
}
if (strncmp(symbol, "TVMFFIErrorSetRaisedFromCStr", 28) == 0) {
return true;
}
if (strncmp(symbol, "TVMFFIErrorSetRaisedFromCStrParts", 33) == 0) {
return true;
}
// C++ stdlib frames
if (strncmp(symbol, "__libc_", 7) == 0) {
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 (strncmp(symbol, "ffi_call_", 9) == 0) {
return true;
}
}
if (filename) {
// Stack frames for TVM FFI
if (strstr(filename, "include/tvm/ffi/error.h") != nullptr) {
return true;
}
if (strstr(filename, "include/tvm/ffi/function_details.h") != nullptr) {
return true;
}
if (strstr(filename, "include/tvm/ffi/function.h") != nullptr) {
return true;
}
if (strstr(filename, "include/tvm/ffi/any.h") != nullptr) {
return true;
}
// C++ stdlib frames
if (strstr(filename, "include/c++/") != nullptr) {
return true;
}
}
return false;
}
/**
* \brief List frames that should stop the backtrace.
* \param filename The filename of the frame.
* \param symbol The symbol name of the frame.
* \return true if the frame should stop the backtrace.
* \note We stop backtrace at the FFI boundary.
*/
inline bool DetectFFIBoundary(const char* filename, const char* symbol) {
if (symbol != nullptr) {
if (strncmp(symbol, "TVMFFIFunctionCall", 18) == 0) {
return true;
}
// python ABI functions
if (strncmp(symbol, "slot_tp_call", 12) == 0) {
return true;
}
if (strncmp(symbol, "object_is_not_callable", 11) == 0) {
return true;
}
// Python interpreter stack frames
// we stop backtrace at the Python interpreter stack frames
// since these frame will be handled from by the python side.
if (strncmp(symbol, "_Py", 3) == 0 || strncmp(symbol, "PyObject", 8) == 0) {
return true;
}
}
return false;
}
/*!
* \brief storage to store backtrace
*/
struct BacktraceStorage {
/*! \brief The stream to store the backtrace. */
std::ostringstream backtrace_stream_;
/*! \brief The number of lines in the backtrace. */
size_t line_count_ = 0;
/*! \brief Maximum size of the backtrace. */
size_t max_frame_size = GetBacktraceLimit();
/*! \brief Number of frames to skip. */
size_t skip_frame_count = 0;
/*! \brief Whether to stop at the ffi boundary. */
bool stop_at_boundary = true;
void Append(const char* filename, const char* func, int lineno) {
// skip frames with empty filename
if (filename == nullptr) {
if (func != nullptr) {
if (strncmp(func, "0x0", 3) == 0) {
return;
}
if (strncmp(func, "<unknown>", 9) == 0) {
return;
}
filename = "<unknown>";
} else {
return;
}
}
backtrace_stream_ << " File \"" << filename << "\"";
backtrace_stream_ << ", line " << lineno;
backtrace_stream_ << ", in " << func << '\n';
line_count_++;
}
bool ExceedBacktraceLimit() const { return line_count_ >= max_frame_size; }
// get backtrace in the order of most recent call last
std::string GetBacktrace() const { return backtrace_stream_.str(); }
};
} // namespace ffi
} // namespace tvm
#endif // TVM_FFI_TRACEBACK_H_