xref: /llvm-project/flang/runtime/stop.cpp (revision 3da7de34a2bcfeef73747a9796652f6bff225de3)
1 //===-- runtime/stop.cpp --------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "flang/Runtime/stop.h"
10 #include "config.h"
11 #include "environment.h"
12 #include "file.h"
13 #include "io-error.h"
14 #include "terminator.h"
15 #include "unit.h"
16 #include <cfenv>
17 #include <cstdio>
18 #include <cstdlib>
19 
20 #ifdef HAVE_BACKTRACE
21 #include BACKTRACE_HEADER
22 #endif
23 
24 extern "C" {
25 
26 static void DescribeIEEESignaledExceptions() {
27 #ifdef fetestexcept // a macro in some environments; omit std::
28   auto excepts{fetestexcept(FE_ALL_EXCEPT)};
29 #else
30   auto excepts{std::fetestexcept(FE_ALL_EXCEPT)};
31 #endif
32   if (excepts) {
33     std::fputs("IEEE arithmetic exceptions signaled:", stderr);
34 #ifdef FE_DIVBYZERO
35     if (excepts & FE_DIVBYZERO) {
36       std::fputs(" DIVBYZERO", stderr);
37     }
38 #endif
39 #ifdef FE_INEXACT
40     if (excepts & FE_INEXACT) {
41       std::fputs(" INEXACT", stderr);
42     }
43 #endif
44 #ifdef FE_INVALID
45     if (excepts & FE_INVALID) {
46       std::fputs(" INVALID", stderr);
47     }
48 #endif
49 #ifdef FE_OVERFLOW
50     if (excepts & FE_OVERFLOW) {
51       std::fputs(" OVERFLOW", stderr);
52     }
53 #endif
54 #ifdef FE_UNDERFLOW
55     if (excepts & FE_UNDERFLOW) {
56       std::fputs(" UNDERFLOW", stderr);
57     }
58 #endif
59     std::fputc('\n', stderr);
60   }
61 }
62 
63 static void CloseAllExternalUnits(const char *why) {
64   Fortran::runtime::io::IoErrorHandler handler{why};
65   Fortran::runtime::io::ExternalFileUnit::CloseAll(handler);
66 }
67 
68 [[noreturn]] void RTNAME(StopStatement)(
69     int code, bool isErrorStop, bool quiet) {
70   CloseAllExternalUnits("STOP statement");
71   if (Fortran::runtime::executionEnvironment.noStopMessage && code == 0) {
72     quiet = true;
73   }
74   if (!quiet) {
75     std::fprintf(stderr, "Fortran %s", isErrorStop ? "ERROR STOP" : "STOP");
76     if (code != EXIT_SUCCESS) {
77       std::fprintf(stderr, ": code %d\n", code);
78     }
79     std::fputc('\n', stderr);
80     DescribeIEEESignaledExceptions();
81   }
82   std::exit(code);
83 }
84 
85 [[noreturn]] void RTNAME(StopStatementText)(
86     const char *code, std::size_t length, bool isErrorStop, bool quiet) {
87   CloseAllExternalUnits("STOP statement");
88   if (!quiet) {
89     if (Fortran::runtime::executionEnvironment.noStopMessage && !isErrorStop) {
90       std::fprintf(stderr, "%.*s\n", static_cast<int>(length), code);
91     } else {
92       std::fprintf(stderr, "Fortran %s: %.*s\n",
93           isErrorStop ? "ERROR STOP" : "STOP", static_cast<int>(length), code);
94     }
95     DescribeIEEESignaledExceptions();
96   }
97   if (isErrorStop) {
98     std::exit(EXIT_FAILURE);
99   } else {
100     std::exit(EXIT_SUCCESS);
101   }
102 }
103 
104 static bool StartPause() {
105   if (Fortran::runtime::io::IsATerminal(0)) {
106     Fortran::runtime::io::IoErrorHandler handler{"PAUSE statement"};
107     Fortran::runtime::io::ExternalFileUnit::FlushAll(handler);
108     return true;
109   }
110   return false;
111 }
112 
113 static void EndPause() {
114   std::fflush(nullptr);
115   if (std::fgetc(stdin) == EOF) {
116     CloseAllExternalUnits("PAUSE statement");
117     std::exit(EXIT_SUCCESS);
118   }
119 }
120 
121 void RTNAME(PauseStatement)() {
122   if (StartPause()) {
123     std::fputs("Fortran PAUSE: hit RETURN to continue:", stderr);
124     EndPause();
125   }
126 }
127 
128 void RTNAME(PauseStatementInt)(int code) {
129   if (StartPause()) {
130     std::fprintf(stderr, "Fortran PAUSE %d: hit RETURN to continue:", code);
131     EndPause();
132   }
133 }
134 
135 void RTNAME(PauseStatementText)(const char *code, std::size_t length) {
136   if (StartPause()) {
137     std::fprintf(stderr,
138         "Fortran PAUSE %.*s: hit RETURN to continue:", static_cast<int>(length),
139         code);
140     EndPause();
141   }
142 }
143 
144 [[noreturn]] void RTNAME(FailImageStatement)() {
145   Fortran::runtime::NotifyOtherImagesOfFailImageStatement();
146   CloseAllExternalUnits("FAIL IMAGE statement");
147   std::exit(EXIT_FAILURE);
148 }
149 
150 [[noreturn]] void RTNAME(ProgramEndStatement)() {
151   CloseAllExternalUnits("END statement");
152   std::exit(EXIT_SUCCESS);
153 }
154 
155 [[noreturn]] void RTNAME(Exit)(int status) {
156   CloseAllExternalUnits("CALL EXIT()");
157   std::exit(status);
158 }
159 
160 static RT_NOINLINE_ATTR 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{(int)backtrace(buffer, MAX_CALL_STACK)};
166 
167   if (char **symbols{backtrace_symbols(buffer, nptrs)}) {
168     // Skip the PrintBacktrace() frame, as it is just a utility.
169     // It makes sense to start printing the backtrace
170     // from Abort() or backtrace().
171     for (int i = 1; i < nptrs; i++) {
172       Fortran::runtime::Terminator{}.PrintCrashArgs(
173           "#%d %s\n", i - 1, symbols[i]);
174     }
175     free(symbols);
176   }
177 
178 #else
179 
180   // TODO: Need to implement the version for other platforms.
181   Fortran::runtime::Terminator{}.PrintCrashArgs("backtrace is not supported.");
182 
183 #endif
184 }
185 
186 [[noreturn]] RT_OPTNONE_ATTR void RTNAME(Abort)() {
187 #ifdef HAVE_BACKTRACE
188   PrintBacktrace();
189 #endif
190   std::abort();
191 }
192 
193 RT_OPTNONE_ATTR void FORTRAN_PROCEDURE_NAME(backtrace)() { PrintBacktrace(); }
194 
195 [[noreturn]] void RTNAME(ReportFatalUserError)(
196     const char *message, const char *source, int line) {
197   Fortran::runtime::Terminator{source, line}.Crash(message);
198 }
199 }
200