Skip to content

Commit 159e601

Browse files
authored
[flang]Add new intrinsic function backtrace and complete the TODO of abort (llvm#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.
1 parent b869f1b commit 159e601

File tree

8 files changed

+61
-1
lines changed

8 files changed

+61
-1
lines changed

flang/include/flang/Optimizer/Builder/IntrinsicCall.h

+1
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ struct IntrinsicLibrary {
196196
fir::ExtendedValue genAssociated(mlir::Type,
197197
llvm::ArrayRef<fir::ExtendedValue>);
198198
mlir::Value genAtand(mlir::Type, llvm::ArrayRef<mlir::Value>);
199+
void genBacktrace(llvm::ArrayRef<fir::ExtendedValue>);
199200
fir::ExtendedValue genBesselJn(mlir::Type,
200201
llvm::ArrayRef<fir::ExtendedValue>);
201202
fir::ExtendedValue genBesselYn(mlir::Type,

flang/include/flang/Optimizer/Builder/Runtime/Stop.h

+3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ void genExit(fir::FirOpBuilder &, mlir::Location, mlir::Value status);
3030
/// Generate call to ABORT intrinsic runtime routine.
3131
void genAbort(fir::FirOpBuilder &, mlir::Location);
3232

33+
/// Generate call to BACKTRACE intrinsic runtime routine.
34+
void genBacktrace(fir::FirOpBuilder &builder, mlir::Location loc);
35+
3336
/// Generate call to crash the program with an error message when detecting
3437
/// an invalid situation at runtime.
3538
void genReportFatalUserError(fir::FirOpBuilder &, mlir::Location,

flang/include/flang/Runtime/stop.h

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ NORETURN void RTNAME(ProgramEndStatement)(NO_ARGUMENTS);
2929
// Extensions
3030
NORETURN void RTNAME(Exit)(int status DEFAULT_VALUE(EXIT_SUCCESS));
3131
NORETURN void RTNAME(Abort)(NO_ARGUMENTS);
32+
void RTNAME(Backtrace)(NO_ARGUMENTS);
3233

3334
// Crash with an error message when the program dynamically violates a Fortran
3435
// constraint.

flang/lib/Evaluate/intrinsics.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,7 @@ static const IntrinsicInterface intrinsicSubroutine[]{
13361336
{"stat", AnyInt, Rank::scalar, Optionality::optional,
13371337
common::Intent::Out}},
13381338
{}, Rank::elemental, IntrinsicClass::atomicSubroutine},
1339+
{"backtrace", {}, {}, Rank::elemental, IntrinsicClass::pureSubroutine},
13391340
{"co_broadcast",
13401341
{{"a", AnyData, Rank::anyOrAssumedRank, Optionality::required,
13411342
common::Intent::InOut},

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ static constexpr IntrinsicHandler handlers[]{
150150
{"atan2pi", &I::genAtanpi},
151151
{"atand", &I::genAtand},
152152
{"atanpi", &I::genAtanpi},
153+
{"backtrace", &I::genBacktrace},
153154
{"bessel_jn",
154155
&I::genBesselJn,
155156
{{{"n1", asValue}, {"n2", asValue}, {"x", asValue}}},
@@ -2682,6 +2683,12 @@ IntrinsicLibrary::genBesselJn(mlir::Type resultType,
26822683
}
26832684
}
26842685

2686+
// Backtrace
2687+
void IntrinsicLibrary::genBacktrace(llvm::ArrayRef<fir::ExtendedValue> args) {
2688+
assert(args.size() == 0);
2689+
fir::runtime::genBacktrace(builder, loc);
2690+
}
2691+
26852692
// BESSEL_YN
26862693
fir::ExtendedValue
26872694
IntrinsicLibrary::genBesselYn(mlir::Type resultType,

flang/lib/Optimizer/Builder/Runtime/Stop.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ void fir::runtime::genAbort(fir::FirOpBuilder &builder, mlir::Location loc) {
2828
builder.create<fir::CallOp>(loc, abortFunc, std::nullopt);
2929
}
3030

31+
void fir::runtime::genBacktrace(fir::FirOpBuilder &builder,
32+
mlir::Location loc) {
33+
mlir::func::FuncOp backtraceFunc =
34+
fir::runtime::getRuntimeFunc<mkRTKey(Backtrace)>(loc, builder);
35+
builder.create<fir::CallOp>(loc, backtraceFunc, std::nullopt);
36+
}
37+
3138
void fir::runtime::genReportFatalUserError(fir::FirOpBuilder &builder,
3239
mlir::Location loc,
3340
llvm::StringRef message) {

flang/runtime/stop.cpp

+31-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
#include <cstdio>
1717
#include <cstdlib>
1818

19+
#include "llvm/Config/config.h"
20+
#ifdef HAVE_BACKTRACE
21+
#include BACKTRACE_HEADER
22+
#endif
23+
1924
extern "C" {
2025

2126
static void DescribeIEEESignaledExceptions() {
@@ -152,11 +157,36 @@ void RTNAME(PauseStatementText)(const char *code, std::size_t length) {
152157
std::exit(status);
153158
}
154159

160+
static void PrintBacktrace() {
161+
#ifdef HAVE_BACKTRACE
162+
// TODO: Need to parse DWARF information to print function line numbers
163+
constexpr int MAX_CALL_STACK{999};
164+
void *buffer[MAX_CALL_STACK];
165+
int nptrs{backtrace(buffer, MAX_CALL_STACK)};
166+
167+
if (char **symbols{backtrace_symbols(buffer, nptrs)}) {
168+
for (int i = 0; i < nptrs; i++) {
169+
Fortran::runtime::Terminator{}.PrintCrashArgs("#%d %s\n", i, symbols[i]);
170+
}
171+
free(symbols);
172+
}
173+
174+
#else
175+
176+
// TODO: Need to implement the version for other platforms.
177+
Fortran::runtime::Terminator{}.PrintCrashArgs(
178+
"Handle the case when a backtrace is not available");
179+
180+
#endif
181+
}
182+
155183
[[noreturn]] void RTNAME(Abort)() {
156-
// TODO: Add backtrace call, unless with `-fno-backtrace`.
184+
PrintBacktrace();
157185
std::abort();
158186
}
159187

188+
void RTNAME(Backtrace)() { PrintBacktrace(); }
189+
160190
[[noreturn]] void RTNAME(ReportFatalUserError)(
161191
const char *message, const char *source, int line) {
162192
Fortran::runtime::Terminator{source, line}.Crash(message);
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
! RUN: bbc -emit-fir %s -o - | FileCheck %s
2+
3+
! CHECK-LABEL: func.func @_QPbacktrace_test() {
4+
! CHECK: %[[VAL_0:.*]] = fir.call @_FortranABacktrace() {{.*}}: () -> none
5+
! CHECK: return
6+
! CHECK: }
7+
8+
subroutine backtrace_test()
9+
call backtrace
10+
end subroutine

0 commit comments

Comments
 (0)