| /* |
| * 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_ |