diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md index 94bca7a3972b6..d0b7999fbd067 100644 --- a/flang/docs/Intrinsics.md +++ b/flang/docs/Intrinsics.md @@ -705,6 +705,7 @@ MALLOC, FREE ### Library subroutine ``` +CALL BACKTRACE() CALL FDATE(TIME) CALL GETLOG(USRNAME) CALL GETENV(NAME [, VALUE, LENGTH, STATUS, TRIM_NAME, ERRMSG ]) @@ -769,7 +770,7 @@ This phase currently supports all the intrinsic procedures listed above but the | Intrinsic subroutines |MVBITS (elemental), CPU_TIME, DATE_AND_TIME, EVENT_QUERY, EXECUTE_COMMAND_LINE, GET_COMMAND, GET_COMMAND_ARGUMENT, GET_ENVIRONMENT_VARIABLE, MOVE_ALLOC, RANDOM_INIT, RANDOM_NUMBER, RANDOM_SEED, SIGNAL, SLEEP, SYSTEM, SYSTEM_CLOCK | | Atomic intrinsic subroutines | ATOMIC_ADD | | Collective intrinsic subroutines | CO_REDUCE | -| Library subroutines | FDATE, GETLOG, GETENV | +| Library subroutines | BACKTRACE, FDATE, GETLOG, GETENV | ### Intrinsic Function Folding diff --git a/flang/include/flang/Runtime/stop.h b/flang/include/flang/Runtime/stop.h index f7c4ffe7403e8..24ae2cbe01ec6 100644 --- a/flang/include/flang/Runtime/stop.h +++ b/flang/include/flang/Runtime/stop.h @@ -11,6 +11,7 @@ #include "flang/Runtime/c-or-cpp.h" #include "flang/Runtime/entry-names.h" +#include "flang/Runtime/extensions.h" #include FORTRAN_EXTERN_C_BEGIN @@ -29,6 +30,7 @@ NORETURN void RTNAME(ProgramEndStatement)(NO_ARGUMENTS); // Extensions NORETURN void RTNAME(Exit)(int status DEFAULT_VALUE(EXIT_SUCCESS)); NORETURN void RTNAME(Abort)(NO_ARGUMENTS); +void FORTRAN_PROCEDURE_NAME(backtrace)(NO_ARGUMENTS); // Crash with an error message when the program dynamically violates a Fortran // constraint. diff --git a/flang/runtime/CMakeLists.txt b/flang/runtime/CMakeLists.txt index cdd2de541c673..fbfaae9a88064 100644 --- a/flang/runtime/CMakeLists.txt +++ b/flang/runtime/CMakeLists.txt @@ -59,6 +59,11 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) ) endif() +# function checks +find_package(Backtrace) +set(HAVE_BACKTRACE ${Backtrace_FOUND}) +set(BACKTRACE_HEADER ${Backtrace_HEADER}) + include(CheckCXXSymbolExists) include(CheckCXXSourceCompiles) check_cxx_symbol_exists(strerror_r string.h HAVE_STRERROR_R) diff --git a/flang/runtime/config.h.cmake b/flang/runtime/config.h.cmake index 0a1d1394b9bc4..a2271be77b8c6 100644 --- a/flang/runtime/config.h.cmake +++ b/flang/runtime/config.h.cmake @@ -8,4 +8,9 @@ don't. */ #cmakedefine01 HAVE_DECL_STRERROR_S +/* Define to 1 if you have the `backtrace' function. */ +#cmakedefine HAVE_BACKTRACE ${HAVE_BACKTRACE} + +#define BACKTRACE_HEADER <${BACKTRACE_HEADER}> + #endif diff --git a/flang/runtime/stop.cpp b/flang/runtime/stop.cpp index cfb36b4084020..f8457e10566a2 100644 --- a/flang/runtime/stop.cpp +++ b/flang/runtime/stop.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "flang/Runtime/stop.h" +#include "config.h" #include "environment.h" #include "file.h" #include "io-error.h" @@ -16,6 +17,10 @@ #include #include +#ifdef HAVE_BACKTRACE +#include BACKTRACE_HEADER +#endif + extern "C" { static void DescribeIEEESignaledExceptions() { @@ -152,11 +157,37 @@ void RTNAME(PauseStatementText)(const char *code, std::size_t length) { std::exit(status); } +static void PrintBacktrace() { +#ifdef HAVE_BACKTRACE + // TODO: Need to parse DWARF information to print function line numbers + constexpr int MAX_CALL_STACK{999}; + void *buffer[MAX_CALL_STACK]; + int nptrs{backtrace(buffer, MAX_CALL_STACK)}; + + if (char **symbols{backtrace_symbols(buffer, nptrs)}) { + for (int i = 0; i < nptrs; i++) { + Fortran::runtime::Terminator{}.PrintCrashArgs("#%d %s\n", i, symbols[i]); + } + free(symbols); + } + +#else + + // TODO: Need to implement the version for other platforms. + Fortran::runtime::Terminator{}.PrintCrashArgs("backtrace is not supported."); + +#endif +} + [[noreturn]] void RTNAME(Abort)() { - // TODO: Add backtrace call, unless with `-fno-backtrace`. +#ifdef HAVE_BACKTRACE + PrintBacktrace(); +#endif std::abort(); } +void FORTRAN_PROCEDURE_NAME(backtrace)() { PrintBacktrace(); } + [[noreturn]] void RTNAME(ReportFatalUserError)( const char *message, const char *source, int line) { Fortran::runtime::Terminator{source, line}.Crash(message);