This is an automated email from the ASF dual-hosted git repository. tqchen pushed a commit to branch debug-symbol in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 40b6023a3a0df1b9f007ddb8b9e2f92285c994fa Author: tqchen <[email protected]> AuthorDate: Sun Aug 24 14:50:24 2025 -0400 update --- ffi/CMakeLists.txt | 10 ++++++++-- ffi/cmake/Utils/Library.cmake | 8 ++++++++ ffi/include/tvm/ffi/c_api.h | 4 +++- ffi/include/tvm/ffi/error.h | 4 ++-- ffi/python/tvm_ffi/cython/base.pxi | 3 ++- ffi/python/tvm_ffi/cython/error.pxi | 2 +- ffi/src/ffi/error.cc | 2 +- ffi/src/ffi/extra/testing.cc | 17 +++++++++-------- ffi/src/ffi/traceback.cc | 13 +++++++------ ffi/src/ffi/traceback.h | 34 +++++++++++++++++++++------------- ffi/src/ffi/traceback_win.cc | 6 ++++-- include/tvm/runtime/logging.h | 2 +- src/tir/schedule/error.h | 2 +- 13 files changed, 68 insertions(+), 39 deletions(-) diff --git a/ffi/CMakeLists.txt b/ffi/CMakeLists.txt index 8e9ac09b6b..c85d0f181f 100644 --- a/ffi/CMakeLists.txt +++ b/ffi/CMakeLists.txt @@ -148,7 +148,7 @@ option(TVM_FFI_BUILD_TESTS "Adding test targets." OFF) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") if (TVM_FFI_USE_RELEASE_FLAGS) message(STATUS "Targeting global CXX flags to -O3 for optimized build") - target_compile_options(tvm_ffi_objs PRIVATE -O3 -g) + target_compile_options(tvm_ffi_objs PRIVATE -O3) target_compile_options(tvm_ffi_shared PRIVATE -O3) target_compile_options(tvm_ffi_static PRIVATE -O3) endif() @@ -158,7 +158,9 @@ endif() include(cmake/Utils/CxxWarning.cmake) include(cmake/Utils/Sanitizer.cmake) -tvm_ffi_add_cxx_warning(tvm_ffi_objs) +# remap the file name to the source directory so we can see the +# exact file name in traceback relative to the project source root +tvm_ffi_add_prefix_map(tvm_ffi_objs ${CMAKE_SOURCE_DIR}) ########## Adding cpp tests ########## @@ -169,6 +171,7 @@ if (TVM_FFI_BUILD_TESTS) message(STATUS "Enable Testing") include(cmake/Utils/AddGoogleTest.cmake) add_subdirectory(tests/cpp/) + tvm_ffi_add_cxx_warning(tvm_ffi_objs) endif() ########## Install the related for normal cmake library ########## @@ -208,9 +211,12 @@ if (TVM_FFI_BUILD_PYTHON_MODULE) ${CMAKE_CURRENT_SOURCE_DIR}/python/tvm_ffi/cython/object.pxi ${CMAKE_CURRENT_SOURCE_DIR}/python/tvm_ffi/cython/string.pxi ) + # set working directory to source so we can see the exact file name in traceback + # relatived to the project source root add_custom_command( OUTPUT ${core_cpp} COMMAND ${Python_EXECUTABLE} -m cython --cplus ${core_pyx} -o ${core_cpp} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Transpiling ${core_pyx} to ${core_cpp}" DEPENDS ${cython_sources} VERBATIM diff --git a/ffi/cmake/Utils/Library.cmake b/ffi/cmake/Utils/Library.cmake index a6f6341d43..ff9744a5a9 100644 --- a/ffi/cmake/Utils/Library.cmake +++ b/ffi/cmake/Utils/Library.cmake @@ -14,6 +14,14 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. + +function(tvm_ffi_add_prefix_map target_name prefix_path) + # Add prefix map so the path displayed becomes relative to prefix_path + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(${target_name} PRIVATE "-ffile-prefix-map=${prefix_path}/=") + endif() +endfunction() + function(tvm_ffi_add_apple_dsymutil target_name) # running dsymutil on macos to generate debugging symbols for backtraces if(APPLE AND TVM_FFI_USE_LIBBACKTRACE) diff --git a/ffi/include/tvm/ffi/c_api.h b/ffi/include/tvm/ffi/c_api.h index d54ef0ea49..b1107c4a0c 100644 --- a/ffi/include/tvm/ffi/c_api.h +++ b/ffi/include/tvm/ffi/c_api.h @@ -836,13 +836,15 @@ TVM_FFI_DLL const TVMFFITypeAttrColumn* TVMFFIGetTypeAttrColumn(const TVMFFIByte * \param filename The current file name. * \param lineno The current line number * \param func The current function + * \param cross_ffi_boundary Whether the traceback is crossing the ffi boundary + * or we should stop at the ffi boundary when detected * \return The traceback string * * \note filename/func can be nullptr, then these info are skipped, they are useful * for cases when debug symbols is not available. */ TVM_FFI_DLL const TVMFFIByteArray* TVMFFITraceback(const char* filename, int lineno, - const char* func); + const char* func, int cross_ffi_boundary); /*! * \brief Initialize the type info during runtime. diff --git a/ffi/include/tvm/ffi/error.h b/ffi/include/tvm/ffi/error.h index 5e5ceac667..ae06ca5a2c 100644 --- a/ffi/include/tvm/ffi/error.h +++ b/ffi/include/tvm/ffi/error.h @@ -216,7 +216,7 @@ class ErrorBuilder { */ #define TVM_FFI_THROW(ErrorKind) \ ::tvm::ffi::details::ErrorBuilder(#ErrorKind, \ - TVMFFITraceback(__FILE__, __LINE__, TVM_FFI_FUNC_SIG), \ + TVMFFITraceback(__FILE__, __LINE__, TVM_FFI_FUNC_SIG, 0), \ TVM_FFI_ALWAYS_LOG_BEFORE_THROW) \ .stream() @@ -229,7 +229,7 @@ class ErrorBuilder { */ #define TVM_FFI_LOG_AND_THROW(ErrorKind) \ ::tvm::ffi::details::ErrorBuilder(#ErrorKind, \ - TVMFFITraceback(__FILE__, __LINE__, TVM_FFI_FUNC_SIG), true) \ + TVMFFITraceback(__FILE__, __LINE__, TVM_FFI_FUNC_SIG, 0), true) \ .stream() // Glog style checks with TVM_FFI prefix diff --git a/ffi/python/tvm_ffi/cython/base.pxi b/ffi/python/tvm_ffi/cython/base.pxi index e61eaf322d..4caecc1f96 100644 --- a/ffi/python/tvm_ffi/cython/base.pxi +++ b/ffi/python/tvm_ffi/cython/base.pxi @@ -187,7 +187,8 @@ cdef extern from "tvm/ffi/c_api.h": int TVMFFITypeKeyToIndex(TVMFFIByteArray* type_key, int32_t* out_tindex) nogil int TVMFFIDataTypeFromString(TVMFFIByteArray* str, DLDataType* out) nogil int TVMFFIDataTypeToString(const DLDataType* dtype, TVMFFIAny* out) nogil - const TVMFFIByteArray* TVMFFITraceback(const char* filename, int lineno, const char* func) nogil; + const TVMFFIByteArray* TVMFFITraceback( + const char* filename, int lineno, const char* func, int cross_ffi_boundary) nogil; int TVMFFINDArrayFromDLPack(DLManagedTensor* src, int32_t require_alignment, int32_t require_contiguous, TVMFFIObjectHandle* out) nogil int TVMFFINDArrayFromDLPackVersioned(DLManagedTensorVersioned* src, diff --git a/ffi/python/tvm_ffi/cython/error.pxi b/ffi/python/tvm_ffi/cython/error.pxi index 968860390a..b7771000fd 100644 --- a/ffi/python/tvm_ffi/cython/error.pxi +++ b/ffi/python/tvm_ffi/cython/error.pxi @@ -98,7 +98,7 @@ cdef inline int set_last_ffi_error(error) except -1: kind = ERROR_TYPE_TO_NAME.get(type(error), "RuntimeError") message = error.__str__() py_traceback = _TRACEBACK_TO_STR(error.__traceback__) - c_traceback = bytearray_to_str(TVMFFITraceback("<unknown>", 0, "<unknown>")) + c_traceback = bytearray_to_str(TVMFFITraceback(NULL, 0, NULL, 0)) # error comes from an exception thrown from C++ side if hasattr(error, "__tvm_ffi_error__"): diff --git a/ffi/src/ffi/error.cc b/ffi/src/ffi/error.cc index 1ed0476203..faf5ea27e3 100644 --- a/ffi/src/ffi/error.cc +++ b/ffi/src/ffi/error.cc @@ -57,7 +57,7 @@ class SafeCallContext { void TVMFFIErrorSetRaisedFromCStr(const char* kind, const char* message) { // NOTE: run traceback here to simplify the depth of tracekback tvm::ffi::SafeCallContext::ThreadLocal()->SetRaisedByCstr(kind, message, - TVMFFITraceback(nullptr, 0, nullptr)); + TVMFFITraceback(nullptr, 0, nullptr, 0)); } void TVMFFIErrorSetRaised(TVMFFIObjectHandle error) { diff --git a/ffi/src/ffi/extra/testing.cc b/ffi/src/ffi/extra/testing.cc index e3efba6350..1a7bdb4e68 100644 --- a/ffi/src/ffi/extra/testing.cc +++ b/ffi/src/ffi/extra/testing.cc @@ -55,11 +55,16 @@ class TestObjectDerived : public TestObjectBase { TVM_FFI_DECLARE_FINAL_OBJECT_INFO(TestObjectDerived, TestObjectBase); }; -void TestRaiseError(String kind, String msg) { - throw ffi::Error(kind, msg, TVMFFITraceback(__FILE__, __LINE__, TVM_FFI_FUNC_SIG)); +TVM_FFI_NO_INLINE void TestRaiseError(String kind, String msg) { + // keep name and no liner for testing traceback + throw ffi::Error(kind, msg, TVMFFITraceback(__FILE__, __LINE__, TVM_FFI_FUNC_SIG, 0)); } -void TestApply(Function f, PackedArgs args, Any* ret) { f.CallPacked(args, ret); } +TVM_FFI_NO_INLINE void TestApply(PackedArgs args, Any* ret) { + // keep name and no liner for testing traceback + auto f = args[0].cast<Function>(); + f.CallPacked(args.Slice(1), ret); +} TVM_FFI_STATIC_INIT_BLOCK({ namespace refl = tvm::ffi::reflection; @@ -78,11 +83,7 @@ TVM_FFI_STATIC_INIT_BLOCK({ .def("testing.test_raise_error", TestRaiseError) .def_packed("testing.nop", [](PackedArgs args, Any* ret) { *ret = args[0]; }) .def_packed("testing.echo", [](PackedArgs args, Any* ret) { *ret = args[0]; }) - .def_packed("testing.apply", - [](PackedArgs args, Any* ret) { - auto f = args[0].cast<Function>(); - TestApply(f, args.Slice(1), ret); - }) + .def_packed("testing.apply", TestApply) .def("testing.run_check_signal", [](int nsec) { for (int i = 0; i < nsec; ++i) { diff --git a/ffi/src/ffi/traceback.cc b/ffi/src/ffi/traceback.cc index debe5c5b07..d232e05786 100644 --- a/ffi/src/ffi/traceback.cc +++ b/ffi/src/ffi/traceback.cc @@ -95,13 +95,11 @@ int BacktraceFullCallback(void* data, uintptr_t pc, const char* filename, int li backtrace_syminfo(_bt_state, pc, BacktraceSyminfoCallback, BacktraceErrorCallback, &symbol_str); } symbol = symbol_str.data(); - // TVMFFITraceback function itself does not count if (IsTracebackFunction(filename, symbol)) return 0; - if (stack_trace->ExceedTracebackLimit()) { return 1; } - if (ShouldStopTraceback(filename, symbol)) { + if (stack_trace->stop_at_boundary && DetectFFIBoundary(filename, symbol)) { return 1; } // skip extra frames @@ -119,13 +117,15 @@ int BacktraceFullCallback(void* data, uintptr_t pc, const char* filename, int li } // namespace ffi } // namespace tvm -const TVMFFIByteArray* TVMFFITraceback(const char* filename, int lineno, const char* func) { +const TVMFFIByteArray* TVMFFITraceback(const char* filename, int lineno, const char* func, + int cross_ffi_boundary) { // We collapse the traceback into a single function // to simplify the traceback detection handling (since we need to detect TVMFFITraceback) static thread_local std::string traceback_str; static thread_local TVMFFIByteArray traceback_array; // pass in current line as here so last line of traceback is always accurate tvm::ffi::TracebackStorage traceback; + traceback.stop_at_boundary = cross_ffi_boundary == 0; if (filename != nullptr && func != nullptr) { traceback.skip_frame_count = 1; traceback.Append(filename, func, lineno); @@ -148,7 +148,7 @@ void TVMFFISegFaultHandler(int sig) { // Technically we shouldn't do any allocation in a signal handler, but // Backtrace may allocate. What's the worst it could do? We're already // crashing. - const TVMFFIByteArray* traceback = TVMFFITraceback(nullptr, 0, nullptr); + const TVMFFIByteArray* traceback = TVMFFITraceback(nullptr, 0, nullptr, 1); std::cerr << "!!!!!!! Segfault encountered !!!!!!!\n" << std::string(traceback->data, traceback->size) << std::endl; // Re-raise signal with default handler @@ -167,7 +167,8 @@ __attribute__((constructor)) void TVMFFIInstallSignalHandler(void) { #endif // TVM_FFI_BACKTRACE_ON_SEGFAULT #else // fallback implementation simply print out the last trace -const TVMFFIByteArray* TVMFFITraceback(const char* filename, int lineno, const char* func) { +const TVMFFIByteArray* TVMFFITraceback(const char* filename, int lineno, const char* func, + int cross_ffi_boundary) { static thread_local std::string traceback_str; static thread_local TVMFFIByteArray traceback_array; std::ostringstream traceback_stream; diff --git a/ffi/src/ffi/traceback.h b/ffi/src/ffi/traceback.h index 7a8ac1e7b8..43768401a6 100644 --- a/ffi/src/ffi/traceback.h +++ b/ffi/src/ffi/traceback.h @@ -64,29 +64,37 @@ bool IsTracebackFunction(const char*, const char* symbol) { * \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 (filename) { // Stack frames for TVM FFI - if (strstr(filename, "include/tvm/ffi/error.h")) { + if (strncmp(filename, "include/tvm/ffi/error.h", 23) == 0) { return true; } - if (strstr(filename, "include/tvm/ffi/function_details.h")) { + if (strncmp(filename, "include/tvm/ffi/function_details.h", 34) == 0) { return true; } - if (strstr(filename, "include/tvm/ffi/function.h")) { + if (strncmp(filename, "include/tvm/ffi/function.h", 26) == 0) { return true; } - if (strstr(filename, "include/tvm/ffi/any.h")) { + if (strncmp(filename, "include/tvm/ffi/any.h", 21) == 0) { return true; } // C++ stdlib frames - if (strstr(filename, "include/c++/")) { + if (strncmp(filename, "include/c++/", 12) == 0) { return true; } } if (symbol) { // C++ stdlib frames - if (strstr(symbol, "__libc_")) { + if (strncmp(symbol, "__libc_", 7) == 0) { return true; } } @@ -94,7 +102,7 @@ inline bool ShouldExcludeFrame(const char* filename, const char* symbol) { // 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 (strstr(symbol, "ffi_call_")) { + if (strncmp(symbol, "ffi_call_", 9) == 0) { return true; } return false; @@ -107,15 +115,15 @@ inline bool ShouldExcludeFrame(const char* filename, const char* symbol) { * \return true if the frame should stop the traceback. * \note We stop traceback at the FFI boundary. */ -inline bool ShouldStopTraceback(const char* filename, const char* symbol) { +inline bool DetectFFIBoundary(const char* filename, const char* symbol) { if (symbol != nullptr) { - if (strncmp(symbol, "TVMFFIFunctionCall", 14) == 0) { + if (strncmp(symbol, "TVMFFIFunctionCall", 18) == 0) { return true; } // Python interpreter stack frames // we stop traceback 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", 9) == 0) { + if (strncmp(symbol, "_Py", 3) == 0 || strncmp(symbol, "PyObject", 8) == 0) { return true; } } @@ -131,6 +139,8 @@ struct TracebackStorage { size_t max_frame_size = GetTracebackLimit(); /*! \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 @@ -146,9 +156,7 @@ struct TracebackStorage { } std::ostringstream trackeback_stream; trackeback_stream << " File \"" << filename << "\""; - if (lineno != 0) { - trackeback_stream << ", line " << lineno; - } + trackeback_stream << ", line " << lineno; trackeback_stream << ", in " << func << '\n'; lines.push_back(trackeback_stream.str()); } diff --git a/ffi/src/ffi/traceback_win.cc b/ffi/src/ffi/traceback_win.cc index 1b7d2ee71c..0a5ff4426a 100644 --- a/ffi/src/ffi/traceback_win.cc +++ b/ffi/src/ffi/traceback_win.cc @@ -36,12 +36,14 @@ #include "./traceback.h" -const TVMFFIByteArray* TVMFFITraceback(const char* filename, int lineno, const char* func) { +const TVMFFIByteArray* TVMFFITraceback(const char* filename, int lineno, const char* func, + int cross_ffi_boundary) { static thread_local std::string traceback_str; static thread_local TVMFFIByteArray traceback_array; // pass in current line as here so last line of traceback is always accurate tvm::ffi::TracebackStorage traceback; + traceback.stop_at_boundary = cross_ffi_boundary == 0; if (filename != nullptr && func != nullptr) { traceback.skip_frame_count = 1; traceback.Append(filename, func, lineno); @@ -114,7 +116,7 @@ const TVMFFIByteArray* TVMFFITraceback(const char* filename, int lineno, const c if (IsTracebackFunction(filename, symbol)) { continue; } - if (ShouldStopTraceback(filename, symbol)) { + if (traceback.stop_at_boundary && DetectFFIBoundary(filename, symbol)) { break; } // skip extra frames diff --git a/include/tvm/runtime/logging.h b/include/tvm/runtime/logging.h index da715848e0..e9482a9907 100644 --- a/include/tvm/runtime/logging.h +++ b/include/tvm/runtime/logging.h @@ -206,7 +206,7 @@ class InternalError : public Error { */ InternalError(std::string file, int lineno, std::string message) : Error(DetectKind(message), DetectMessage(message), - TVMFFITraceback(file.c_str(), lineno, "")) {} + TVMFFITraceback(file.c_str(), lineno, "", 0)) {} private: // try to detect the kind of error from the message when the error type diff --git a/src/tir/schedule/error.h b/src/tir/schedule/error.h index 0c185bea34..8ddffce3ce 100644 --- a/src/tir/schedule/error.h +++ b/src/tir/schedule/error.h @@ -31,7 +31,7 @@ class ScheduleError : public tvm::runtime::Error { public: /*! \brief Base constructor */ ScheduleError() - : tvm::runtime::Error("ScheduleError", "", TVMFFITraceback(nullptr, 0, nullptr)) {} + : tvm::runtime::Error("ScheduleError", "", TVMFFITraceback(nullptr, 0, nullptr, 0)) {} /*! \brief The error occurred in this IRModule */ virtual IRModule mod() const = 0; /*! \brief The locations of interest that we want to point out */
