this repo has no description
at trunk 257 lines 7.7 kB view raw
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) 2#include <signal.h> 3#include <sys/resource.h> 4#include <unistd.h> 5 6#include <cerrno> 7#include <csignal> 8 9#include "builtins.h" 10#include "file.h" 11#include "int-builtins.h" 12#include "modules.h" 13#include "runtime.h" 14#include "sys-module.h" 15 16namespace py { 17 18struct FaultHandler { 19 int signum; 20 const char* msg; 21 bool enabled; 22 struct sigaction previous_handler; 23}; 24 25static struct { 26 int fd; 27 bool all_threads; 28 bool enabled; 29 Runtime* runtime; 30} fatal_error = {-1}; 31 32static FaultHandler handler_sigabrt = {SIGABRT, "Aborted"}; 33static FaultHandler handler_sigbus = {SIGBUS, "Bus error"}; 34static FaultHandler handler_sigfpe = {SIGFPE, "Floating point exception"}; 35static FaultHandler handler_sigill = {SIGILL, "Illegal instruction"}; 36static FaultHandler handler_sigsegv = {SIGSEGV, "Segmentation fault"}; 37 38static void disableFatalHandler(FaultHandler* handler) { 39 if (!handler->enabled) return; 40 handler->enabled = false; 41 int result = 42 ::sigaction(handler->signum, &handler->previous_handler, nullptr); 43 DCHECK(result == 0, "sigaction returned unexpected error"); 44} 45 46static RawObject getFileno(Thread* thread, const Object& file) { 47 Runtime* runtime = thread->runtime(); 48 if (file.isNoneType()) { 49 return SmallInt::fromWord(File::kStderr); 50 } 51 if (runtime->isInstanceOfInt(*file)) { 52 RawInt fd = intUnderlying(*file); 53 if (fd.isNegative() || fd.isLargeInt()) { 54 return thread->raiseWithFmt(LayoutId::kValueError, 55 "file is not a valid file descriptor"); 56 } 57 return fd.isSmallInt() ? fd : convertBoolToInt(fd); 58 } 59 60 HandleScope scope(thread); 61 Object fileno(&scope, thread->invokeMethod1(file, ID(fileno))); 62 if (fileno.isError()) { 63 if (fileno.isErrorNotFound()) { 64 return thread->raiseWithFmt(LayoutId::kAttributeError, 65 "'%T' object has no attribute 'fileno'", 66 &file); 67 } 68 return *fileno; 69 } 70 71 if (!runtime->isInstanceOfInt(*fileno)) { 72 return thread->raiseWithFmt(LayoutId::kRuntimeError, 73 "file.fileno() is not a valid file descriptor"); 74 } 75 Int fd(&scope, intUnderlying(*fileno)); 76 if (fd.isNegative() || fd.isLargeInt()) { 77 return thread->raiseWithFmt(LayoutId::kRuntimeError, 78 "file.fileno() is not a valid file descriptor"); 79 } 80 81 Object flush_result(&scope, thread->invokeMethod1(file, ID(flush))); 82 if (flush_result.isErrorException()) { 83 thread->clearPendingException(); 84 } 85 return fd.isSmallInt() ? *fd : convertBoolToInt(*fd); 86} 87 88static void writeCStr(int fd, const char* str) { 89 File::write(fd, str, std::strlen(str)); 90} 91 92static void handleFatalError(FaultHandler* handler) { 93 if (!fatal_error.enabled) return; 94 95 int saved_errno = errno; 96 disableFatalHandler(handler); 97 98 int fd = fatal_error.fd; 99 writeCStr(fd, "Fatal Python error: "); 100 writeCStr(fd, handler->msg); 101 writeCStr(fd, "\n\n"); 102 // TODO(T66337218): Print tracebacks for all threads when there is more than 103 // one and`all_threads` is true. 104 fatal_error.runtime->printTraceback(Thread::current(), fd); 105 106 errno = saved_errno; 107 std::raise(handler->signum); 108} 109 110static void handleSigabrt(int signum) { 111 DCHECK(signum == SIGABRT, "expected SIGABRT (%d), got %d", SIGABRT, signum); 112 handleFatalError(&handler_sigabrt); 113} 114 115static void handleSigbus(int signum) { 116 DCHECK(signum == SIGBUS, "expected SIGBUS (%d), got %d", SIGBUS, signum); 117 handleFatalError(&handler_sigbus); 118} 119 120static void handleSigfpe(int signum) { 121 DCHECK(signum == SIGFPE, "expected SIGFPE (%d), got %d", SIGFPE, signum); 122 handleFatalError(&handler_sigfpe); 123} 124 125static void handleSigill(int signum) { 126 DCHECK(signum == SIGILL, "expected SIGILL (%d), got %d", SIGILL, signum); 127 handleFatalError(&handler_sigill); 128} 129 130static void handleSigsegv(int signum) { 131 DCHECK(signum == SIGSEGV, "expected SIGSEGV (%d), got %d", SIGSEGV, signum); 132 handleFatalError(&handler_sigsegv); 133} 134 135// Disable creation of core dump 136static void suppressCrashReport() { 137 struct rlimit rl; 138 if (::getrlimit(RLIMIT_CORE, &rl) == 0) { 139 rl.rlim_cur = 0; 140 ::setrlimit(RLIMIT_CORE, &rl); 141 } 142} 143 144RawObject FUNC(faulthandler, _read_null)(Thread*, Arguments) { 145 suppressCrashReport(); 146 *static_cast<volatile word*>(nullptr); 147 return NoneType::object(); 148} 149 150RawObject FUNC(faulthandler, _sigabrt)(Thread*, Arguments) { 151 suppressCrashReport(); 152 std::abort(); 153 return NoneType::object(); 154} 155 156RawObject FUNC(faulthandler, _sigfpe)(Thread*, Arguments) { 157 suppressCrashReport(); 158 std::raise(SIGFPE); 159 return NoneType::object(); 160} 161 162RawObject FUNC(faulthandler, _sigsegv)(Thread*, Arguments) { 163 suppressCrashReport(); 164 std::raise(SIGSEGV); 165 return NoneType::object(); 166} 167 168RawObject FUNC(faulthandler, disable)(Thread*, Arguments) { 169 if (!fatal_error.enabled) { 170 return Bool::falseObj(); 171 } 172 173 fatal_error.enabled = false; 174 disableFatalHandler(&handler_sigabrt); 175 disableFatalHandler(&handler_sigbus); 176 disableFatalHandler(&handler_sigill); 177 disableFatalHandler(&handler_sigfpe); 178 disableFatalHandler(&handler_sigsegv); 179 return Bool::trueObj(); 180} 181 182RawObject FUNC(faulthandler, dump_traceback)(Thread* thread, Arguments args) { 183 HandleScope scope(thread); 184 Object file(&scope, args.get(0)); 185 Object all_threads(&scope, args.get(1)); 186 187 Runtime* runtime = thread->runtime(); 188 if (!runtime->isInstanceOfInt(*all_threads)) { 189 return thread->raiseRequiresType(all_threads, ID(int)); 190 } 191 192 Object fileno(&scope, getFileno(thread, file)); 193 if (fileno.isError()) return *fileno; 194 SmallInt fd(&scope, *fileno); 195 // TODO(T66337218): Dump all threads when there is more than one and 196 // `all_threads` is True. 197 runtime->printTraceback(thread, fd.value()); 198 199 return runtime->handlePendingSignals(thread); 200} 201 202static bool enableHandler(FaultHandler* handler, void (*handler_func)(int)) { 203 struct sigaction action; 204 action.sa_handler = handler_func; 205 sigemptyset(&action.sa_mask); 206 // For GC-safety, we execute all signal handlers on an alternate stack. 207 action.sa_flags = SA_NODEFER | SA_ONSTACK; 208 209 if (::sigaction(handler->signum, &action, &handler->previous_handler) != 0) { 210 return false; 211 } 212 213 handler->enabled = true; 214 return true; 215} 216 217RawObject FUNC(faulthandler, enable)(Thread* thread, Arguments args) { 218 HandleScope scope(thread); 219 Object file(&scope, args.get(0)); 220 Object all_threads(&scope, args.get(1)); 221 Runtime* runtime = thread->runtime(); 222 if (!runtime->isInstanceOfInt(*all_threads)) { 223 return thread->raiseRequiresType(all_threads, ID(int)); 224 } 225 226 Object fileno(&scope, getFileno(thread, file)); 227 if (fileno.isError()) return *fileno; 228 fatal_error.fd = SmallInt::cast(*fileno).value(); 229 fatal_error.all_threads = !intUnderlying(*all_threads).isZero(); 230 fatal_error.runtime = runtime; 231 if (fatal_error.enabled) { 232 return NoneType::object(); 233 } 234 235 fatal_error.enabled = true; 236 237 if (enableHandler(&handler_sigabrt, handleSigabrt) && 238 enableHandler(&handler_sigbus, handleSigbus) && 239 enableHandler(&handler_sigfpe, handleSigfpe) && 240 enableHandler(&handler_sigill, handleSigill) && 241 enableHandler(&handler_sigsegv, handleSigsegv)) { 242 return NoneType::object(); 243 } 244 245 int saved_errno = errno; 246 const char* msg = std::strerror(saved_errno); 247 MutableTuple val(&scope, runtime->newMutableTuple(2)); 248 val.atPut(0, SmallInt::fromWord(saved_errno)); 249 val.atPut(1, runtime->newStrFromCStr(msg)); 250 return thread->raise(LayoutId::kRuntimeError, val.becomeImmutable()); 251} 252 253RawObject FUNC(faulthandler, is_enabled)(Thread*, Arguments) { 254 return Bool::fromBool(fatal_error.enabled); 255} 256 257} // namespace py