[flang-commits] [flang] [flang]Add new intrinsic function backtrace and complete the TODO of abort (PR #117603)

via flang-commits flang-commits at lists.llvm.org
Mon Nov 25 10:23:53 PST 2024


https://github.com/dty2 created https://github.com/llvm/llvm-project/pull/117603

Hey guys, I found that Flang's built-in ABORT function is incomplete when I was using it. Compared with gfortran's ABORT (which can both abort and print out a backtrace), flang's ABORT implementation lacks the function of printing out a backtrace. This feature is essential for debugging and understanding the call stack at the failure point.

To solve this problem, I completed the "// TODO:" of the abort function, and then implemented an additional built-in function BACKTRACE for flang. After a brief reading of the relevant source code, I used backtrace and backtrace_symbols in "execinfo.h" to quickly implement this. But since I used the above two functions directly, my implementation is slightly different from gfortran's implementation (in the output, the function call stack before main is additionally output, and the function line number is missing). In addition, since I used the above two functions, I did not need to add -g to embed debug information into the ELF file, but needed -rdynamic to ensure that the symbols are added to the dynamic symbol table (so that the function name will be printed out).

Here is a comparison of the output between gfortran 's backtrace and my implementation:
gfortran's implemention output:
```
#0  0x557eb71f4184 in testfun2_
        at /home/hunter/plct/fortran/test.f90:5
#1  0x557eb71f4165 in testfun1_
        at /home/hunter/plct/fortran/test.f90:13
#2  0x557eb71f4192 in test_backtrace
        at /home/hunter/plct/fortran/test.f90:17
#3  0x557eb71f41ce in main
        at /home/hunter/plct/fortran/test.f90:18
```
my impelmention output:
```
Backtrace:
#0 ./test(_FortranABacktrace+0x32) [0x574f07efcf92]
#1 ./test(testfun2_+0x14) [0x574f07efc7b4]
#2 ./test(testfun1_+0xd) [0x574f07efc7cd]
#3 ./test(_QQmain+0x9) [0x574f07efc7e9]
#4 ./test(main+0x12) [0x574f07efc802]
#5 /usr/lib/libc.so.6(+0x25e08) [0x76954694fe08]
#6 /usr/lib/libc.so.6(__libc_start_main+0x8c) [0x76954694fecc]
#7 ./test(_start+0x25) [0x574f07efc6c5]
```
test program is:
```
function testfun2() result(err)
  implicit none
  integer :: err
  err = 1
  call backtrace
end function testfun2

subroutine testfun1()
  implicit none
  integer :: err
  integer :: testfun2

  err = testfun2()
end subroutine testfun1

program test_backtrace
  call testfun1()
end program test_backtrace
```
I am well aware of the importance of line numbers, so I am now working on implementing line numbers (by parsing DWARF information) and supporting cross-platform (Windows) support.

>From 2d5d3107444d244cf5fa2c75cb69000e3c6ca081 Mon Sep 17 00:00:00 2001
From: hunter <284050500 at qq.com>
Date: Tue, 26 Nov 2024 02:18:10 +0800
Subject: [PATCH] [flang]Add new intrinsic function backtrace and complete the
 TODO of abort

---
 .../flang/Optimizer/Builder/IntrinsicCall.h   |  1 +
 .../flang/Optimizer/Builder/Runtime/Stop.h    |  3 +++
 flang/include/flang/Runtime/stop.h            |  1 +
 flang/lib/Evaluate/intrinsics.cpp             |  1 +
 flang/lib/Optimizer/Builder/IntrinsicCall.cpp |  7 +++++++
 flang/lib/Optimizer/Builder/Runtime/Stop.cpp  |  7 +++++++
 flang/runtime/stop.cpp                        | 21 ++++++++++++++++++-
 flang/test/Lower/Intrinsics/backtrace.f90     | 10 +++++++++
 8 files changed, 50 insertions(+), 1 deletion(-)
 create mode 100644 flang/test/Lower/Intrinsics/backtrace.f90

diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
index e83d1a42e34133..7f4b9ebf1d1c21 100644
--- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
+++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
@@ -196,6 +196,7 @@ struct IntrinsicLibrary {
   fir::ExtendedValue genAssociated(mlir::Type,
                                    llvm::ArrayRef<fir::ExtendedValue>);
   mlir::Value genAtand(mlir::Type, llvm::ArrayRef<mlir::Value>);
+  void genBacktrace(llvm::ArrayRef<fir::ExtendedValue>);
   fir::ExtendedValue genBesselJn(mlir::Type,
                                  llvm::ArrayRef<fir::ExtendedValue>);
   fir::ExtendedValue genBesselYn(mlir::Type,
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Stop.h b/flang/include/flang/Optimizer/Builder/Runtime/Stop.h
index 6f764badf6f3a8..be73cffff021e3 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/Stop.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/Stop.h
@@ -30,6 +30,9 @@ void genExit(fir::FirOpBuilder &, mlir::Location, mlir::Value status);
 /// Generate call to ABORT intrinsic runtime routine.
 void genAbort(fir::FirOpBuilder &, mlir::Location);
 
+/// Generate call to BACKTRACE intrinsic runtime routine.
+void genBacktrace(fir::FirOpBuilder &builder, mlir::Location loc);
+
 /// Generate call to crash the program with an error message when detecting
 /// an invalid situation at runtime.
 void genReportFatalUserError(fir::FirOpBuilder &, mlir::Location,
diff --git a/flang/include/flang/Runtime/stop.h b/flang/include/flang/Runtime/stop.h
index f7c4ffe7403e8e..d442f72bfe1fa4 100644
--- a/flang/include/flang/Runtime/stop.h
+++ b/flang/include/flang/Runtime/stop.h
@@ -29,6 +29,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 RTNAME(Backtrace)(NO_ARGUMENTS);
 
 // Crash with an error message when the program dynamically violates a Fortran
 // constraint.
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 1e27c0ae4216c5..599a7d0124b800 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -1333,6 +1333,7 @@ static const IntrinsicInterface intrinsicSubroutine[]{
             {"stat", AnyInt, Rank::scalar, Optionality::optional,
                 common::Intent::Out}},
         {}, Rank::elemental, IntrinsicClass::atomicSubroutine},
+    {"backtrace", {}, {}, Rank::elemental, IntrinsicClass::pureSubroutine},
     {"co_broadcast",
         {{"a", AnyData, Rank::anyOrAssumedRank, Optionality::required,
              common::Intent::InOut},
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index a2b327f45c6939..c748c6583a5ce9 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -150,6 +150,7 @@ static constexpr IntrinsicHandler handlers[]{
     {"atan2pi", &I::genAtanpi},
     {"atand", &I::genAtand},
     {"atanpi", &I::genAtanpi},
+    {"backtrace", &I::genBacktrace},
     {"bessel_jn",
      &I::genBesselJn,
      {{{"n1", asValue}, {"n2", asValue}, {"x", asValue}}},
@@ -2681,6 +2682,12 @@ IntrinsicLibrary::genBesselJn(mlir::Type resultType,
   }
 }
 
+// Backtrace
+void IntrinsicLibrary::genBacktrace(llvm::ArrayRef<fir::ExtendedValue> args) {
+  assert(args.size() == 0);
+  fir::runtime::genBacktrace(builder, loc);
+}
+
 // BESSEL_YN
 fir::ExtendedValue
 IntrinsicLibrary::genBesselYn(mlir::Type resultType,
diff --git a/flang/lib/Optimizer/Builder/Runtime/Stop.cpp b/flang/lib/Optimizer/Builder/Runtime/Stop.cpp
index 411181cc6dd1ca..541e5f3b5d11a8 100644
--- a/flang/lib/Optimizer/Builder/Runtime/Stop.cpp
+++ b/flang/lib/Optimizer/Builder/Runtime/Stop.cpp
@@ -28,6 +28,13 @@ void fir::runtime::genAbort(fir::FirOpBuilder &builder, mlir::Location loc) {
   builder.create<fir::CallOp>(loc, abortFunc, std::nullopt);
 }
 
+void fir::runtime::genBacktrace(fir::FirOpBuilder &builder,
+                                mlir::Location loc) {
+  mlir::func::FuncOp backtraceFunc =
+      fir::runtime::getRuntimeFunc<mkRTKey(Backtrace)>(loc, builder);
+  builder.create<fir::CallOp>(loc, backtraceFunc, std::nullopt);
+}
+
 void fir::runtime::genReportFatalUserError(fir::FirOpBuilder &builder,
                                            mlir::Location loc,
                                            llvm::StringRef message) {
diff --git a/flang/runtime/stop.cpp b/flang/runtime/stop.cpp
index cfb36b40840200..57209dc37befa7 100644
--- a/flang/runtime/stop.cpp
+++ b/flang/runtime/stop.cpp
@@ -15,6 +15,7 @@
 #include <cfenv>
 #include <cstdio>
 #include <cstdlib>
+#include <execinfo.h>
 
 extern "C" {
 
@@ -152,11 +153,29 @@ void RTNAME(PauseStatementText)(const char *code, std::size_t length) {
   std::exit(status);
 }
 
+static void PrintBacktrace() {
+  // TODO: Need to parse DWARF information to print function line numbers
+  const int MAX_CALL_STACK = 999;
+  void *buffer[MAX_CALL_STACK];
+  int nptrs = backtrace(buffer, MAX_CALL_STACK);
+  char **symbols = backtrace_symbols(buffer, nptrs);
+  if (symbols == nullptr) {
+    Fortran::runtime::Terminator{}.Crash("no symbols");
+    std::exit(EXIT_FAILURE);
+  }
+  for (int i = 0; i < nptrs; i++) {
+    Fortran::runtime::Terminator{}.PrintCrashArgs("#%d %s\n", i, symbols[i]);
+  }
+  free(symbols);
+}
+
 [[noreturn]] void RTNAME(Abort)() {
-  // TODO: Add backtrace call, unless with `-fno-backtrace`.
+  PrintBacktrace();
   std::abort();
 }
 
+void RTNAME(Backtrace)() { PrintBacktrace(); }
+
 [[noreturn]] void RTNAME(ReportFatalUserError)(
     const char *message, const char *source, int line) {
   Fortran::runtime::Terminator{source, line}.Crash(message);
diff --git a/flang/test/Lower/Intrinsics/backtrace.f90 b/flang/test/Lower/Intrinsics/backtrace.f90
new file mode 100644
index 00000000000000..9d5e7b4965baff
--- /dev/null
+++ b/flang/test/Lower/Intrinsics/backtrace.f90
@@ -0,0 +1,10 @@
+! RUN: bbc -emit-fir %s -o - | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPbacktrace_test() {
+! CHECK:         %[[VAL_0:.*]] = fir.call @_FortranABacktrace() {{.*}}: () -> none
+! CHECK:         return
+! CHECK:       }
+
+subroutine backtrace_test()
+  call backtrace
+end subroutine



More information about the flang-commits mailing list