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