From c21a86c9d2c6b4b89ec8ca5452a7b4ac925a18fb Mon Sep 17 00:00:00 2001 From: Relintai Date: Wed, 3 Nov 2021 15:42:16 +0100 Subject: [PATCH] Initial platform setup, and added godot's crash handler (will need to be heavily reworked!). --- SConstruct | 1 + core/SCsub | 1 + core/http/session_manager.cpp | 2 + core/os/crash_handler.cpp | 27 +++ core/os/crash_handler.h | 22 ++ core/safe_refcount.cpp | 4 +- core/string.cpp | 4 +- platform/SCsub | 17 ++ platform/linux/SCsub | 12 + platform/linux/crash_handler_linux.cpp | 303 +++++++++++++++++++++++++ platform/linux/crash_handler_linux.h | 45 ++++ platform/linux/detect.py | 27 +++ platform/platform_initializer.cpp | 33 +++ platform/platform_initializer.h | 19 ++ 14 files changed, 515 insertions(+), 2 deletions(-) create mode 100644 core/os/crash_handler.cpp create mode 100644 core/os/crash_handler.h create mode 100644 platform/SCsub create mode 100644 platform/linux/SCsub create mode 100644 platform/linux/crash_handler_linux.cpp create mode 100644 platform/linux/crash_handler_linux.h create mode 100644 platform/linux/detect.py create mode 100644 platform/platform_initializer.cpp create mode 100644 platform/platform_initializer.h diff --git a/SConstruct b/SConstruct index 5800ceb..730e2b5 100644 --- a/SConstruct +++ b/SConstruct @@ -271,6 +271,7 @@ if scons_ver >= (4, 0, 0): Export("env") SConscript("core/SCsub") +SConscript("platform/SCsub") if env_base["databases"]: for d in database_list: diff --git a/core/SCsub b/core/SCsub index 9d42574..b96a1e1 100644 --- a/core/SCsub +++ b/core/SCsub @@ -12,6 +12,7 @@ env.add_source_files(env.core_sources, "./http/*.cpp") env.add_source_files(env.core_sources, "./hash/*.cpp") env.add_source_files(env.core_sources, "./bry_http/*.cpp") env.add_source_files(env.core_sources, "./database/*.cpp") +env.add_source_files(env.core_sources, "./os/*.cpp") # Build it all as a library lib = env.add_library("core", env.core_sources) diff --git a/core/http/session_manager.cpp b/core/http/session_manager.cpp index 0d894f0..89cc7aa 100644 --- a/core/http/session_manager.cpp +++ b/core/http/session_manager.cpp @@ -150,6 +150,8 @@ void SessionManager::load_sessions() { Ref b = DatabaseManager::get_singleton()->ddb->get_query_builder(); + CRASH_COND(true); + b->select("id, session_id"); b->from(_table_name); //b->print(); diff --git a/core/os/crash_handler.cpp b/core/os/crash_handler.cpp new file mode 100644 index 0000000..38faebb --- /dev/null +++ b/core/os/crash_handler.cpp @@ -0,0 +1,27 @@ +#include "crash_handler.h" + +#include "core/error_macros.h" + +void CrashHandler::enable() { +} +void CrashHandler::disable() { +} + +CrashHandler *CrashHandler::get_singleton() { + return _self; +} + +CrashHandler::CrashHandler() { + enabled = false; + + _self = this; +} +CrashHandler::~CrashHandler() { + disable(); + + if (_self == this) { + _self = nullptr; + } +} + +CrashHandler *CrashHandler::_self = nullptr; diff --git a/core/os/crash_handler.h b/core/os/crash_handler.h new file mode 100644 index 0000000..2766ce4 --- /dev/null +++ b/core/os/crash_handler.h @@ -0,0 +1,22 @@ +#ifndef CRASH_HANDLER_H +#define CRASH_HANDLER_H + +class CrashHandler { +public: + virtual void enable(); + virtual void disable(); + + bool is_enabled() const { return enabled; }; + + static CrashHandler *get_singleton(); + + CrashHandler(); + virtual ~CrashHandler(); + +protected: + static CrashHandler *_self; + + bool enabled; +}; + +#endif diff --git a/core/safe_refcount.cpp b/core/safe_refcount.cpp index 033086c..27a67b4 100644 --- a/core/safe_refcount.cpp +++ b/core/safe_refcount.cpp @@ -32,8 +32,9 @@ #include "safe_refcount.h" -#include "core/error_macros.h" +//#include "core/error_macros.h" +/* // On C++14 we don't have std::atomic::is_always_lockfree, so this is the best we can do void check_lockless_atomics() { // Doing the check for the types we actually care about @@ -41,5 +42,6 @@ void check_lockless_atomics() { WARN_PRINT("Your compiler doesn't seem to support lockless atomics. Performance will be degraded. Please consider upgrading to a different or newer compiler."); } } +*/ #endif diff --git a/core/string.cpp b/core/string.cpp index 6897414..6dd509f 100644 --- a/core/string.cpp +++ b/core/string.cpp @@ -44,7 +44,9 @@ void String::erase(const char element) { void String::erase(const int start_index, const int length) { int sil = start_index + length; - if (sil >= _size) { + ERR_FAIL_INDEX(sil, _size); + + if (length >= _size) { _size = 0; _data[_size] = '\0'; return; diff --git a/platform/SCsub b/platform/SCsub new file mode 100644 index 0000000..798087b --- /dev/null +++ b/platform/SCsub @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +Import("env") + +env.core_sources = [] + +env.Append(CPPDEFINES=[ "PLATFORM_LINUX", "DEBUG_ENABLED" ]) +env.Append(LIBS=["dl"]) + +env.add_source_files(env.core_sources, "platform_initializer.cpp") +env.add_source_files(env.core_sources, "./linux/*.cpp") + +env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"]) + +# Build it all as a library +lib = env.add_library("platform", env.core_sources) +env.Prepend(LIBS=[lib]) diff --git a/platform/linux/SCsub b/platform/linux/SCsub new file mode 100644 index 0000000..61f9cc0 --- /dev/null +++ b/platform/linux/SCsub @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +Import("env_db") +Import("env") + +env_db.core_sources = [] + +#env_db.add_source_files(env_db.core_sources, "*.cpp") + +# Build it all as a library +lib = env_db.add_library("linux", env_db.core_sources) +env.Prepend(LIBS=[lib]) diff --git a/platform/linux/crash_handler_linux.cpp b/platform/linux/crash_handler_linux.cpp new file mode 100644 index 0000000..4c2d863 --- /dev/null +++ b/platform/linux/crash_handler_linux.cpp @@ -0,0 +1,303 @@ +/*************************************************************************/ +/* crash_handler_x11.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "crash_handler_linux.h" + +#ifdef DEBUG_ENABLED +#define CRASH_HANDLER_ENABLED 1 +#endif + +#ifdef CRASH_HANDLER_ENABLED +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/string.h" +#include "core/containers/vector.h" + +String get_executable_path() { +#ifdef __linux__ + //fix for running from a symlink + char buf[256]; + memset(buf, 0, 256); + ssize_t len = readlink("/proc/self/exe", buf, sizeof(buf)); + String b; + if (len > 0) { + //b.parse_utf8(buf, len); + b = buf; + } + if (b == "") { + //WARN_PRINT("Couldn't get executable path from /proc/self/exe, using argv[0]"); + //return OS::get_executable_path(); + + return ""; + } + return b; + /* +#elif defined(__OpenBSD__) || defined(__NetBSD__) + char resolved_path[MAXPATHLEN]; + + realpath(OS::get_executable_path().utf8().get_data(), resolved_path); + + return String(resolved_path); +#elif defined(__FreeBSD__) + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; + char buf[MAXPATHLEN]; + size_t len = sizeof(buf); + if (sysctl(mib, 4, buf, &len, NULL, 0) != 0) { + WARN_PRINT("Couldn't get executable path from sysctl"); + return OS::get_executable_path(); + } + String b; + b.parse_utf8(buf); + return b; +#elif defined(__APPLE__) + char temp_path[1]; + uint32_t buff_size = 1; + _NSGetExecutablePath(temp_path, &buff_size); + + char *resolved_path = new char[buff_size + 1]; + + if (_NSGetExecutablePath(resolved_path, &buff_size) == 1) + WARN_PRINT("MAXPATHLEN is too small"); + + String path(resolved_path); + delete[] resolved_path; + + return path; + */ +#else + //ERR_PRINT("Warning, don't know how to obtain executable path on this OS! Please override this function properly."); + //return OS::get_executable_path(); + return ""; +#endif +} + + +int execute(const String &p_path, const Vector &p_arguments, bool p_blocking, int64_t *r_child_id, String *r_pipe, int *r_exitcode) { +#ifdef __EMSCRIPTEN__ + // Don't compile this code at all to avoid undefined references. + // Actual virtual call goes to OS_JavaScript. + ERR_FAIL_V(ERR_BUG); +#else + bool read_stderr = false; +//Mutex *p_pipe_mutex; + + if (p_blocking && r_pipe) { + String argss; + argss = "\"" + p_path + "\""; + + for (int i = 0; i < p_arguments.size(); i++) { + argss += String(" \"") + p_arguments[i] + "\""; + } + + if (read_stderr) { + argss += " 2>&1"; // Read stderr too + } else { + argss += " 2>/dev/null"; //silence stderr + } + FILE *f = popen(argss.data(), "r"); + + if (!f) { + return 2; + } + + //ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot pipe stream from process running with following arguments '" + argss + "'."); + + char buf[65535]; + + while (fgets(buf, 65535, f)) { + //if (p_pipe_mutex) { + // p_pipe_mutex->lock(); + //} + + (*r_pipe) += buf;//String::utf8(buf); + //if (p_pipe_mutex) { + // p_pipe_mutex->unlock(); + //} + } + int rv = pclose(f); + if (r_exitcode) { + *r_exitcode = WEXITSTATUS(rv); + } + + return 0; + } + + pid_t pid = fork(); + //ERR_FAIL_COND_V(pid < 0, ERR_CANT_FORK); + return 1; + + if (pid == 0) { + // is child + + if (!p_blocking) { + // For non blocking calls, create a new session-ID so parent won't wait for it. + // This ensures the process won't go zombie at end. + setsid(); + } + + Vector cs; + cs.push_back(p_path); + for (int i = 0; i < p_arguments.size(); i++) { + cs.push_back(p_arguments[i]); + } + + Vector args; + for (int i = 0; i < cs.size(); i++) { + args.push_back((char *)cs[i].c_str()); + } + args.push_back(0); + + execvp(p_path.c_str(), &args[0]); + // still alive? something failed.. + fprintf(stderr, "**ERROR** OS_Unix::execute - Could not create child process while executing: %s\n", p_path.data()); + raise(SIGKILL); + } + + if (p_blocking) { + int status; + waitpid(pid, &status, 0); + if (r_exitcode) { + *r_exitcode = WIFEXITED(status) ? WEXITSTATUS(status) : status; + } + + } else { + if (r_child_id) { + *r_child_id = pid; + } + } + + return 0; +#endif +} + +static void handle_crash(int sig) { + void *bt_buffer[256]; + size_t size = backtrace(bt_buffer, 256); + String _execpath;// = OS::get_singleton()->get_executable_path(); + + // Dump the backtrace to stderr with a message to the user + fprintf(stderr, "\n================================================================\n"); + fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig); + + fprintf(stderr, "Dumping the backtrace.\n"); + char **strings = backtrace_symbols(bt_buffer, size); + if (strings) { + for (size_t i = 1; i < size; i++) { + char fname[1024]; + Dl_info info; + + snprintf(fname, 1024, "%s", strings[i]); + + // Try to demangle the function name to provide a more readable one + if (dladdr(bt_buffer[i], &info) && info.dli_sname) { + if (info.dli_sname[0] == '_') { + int status; + char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status); + + if (status == 0 && demangled) { + snprintf(fname, 1024, "%s", demangled); + } + + if (demangled) { + free(demangled); + } + } + } + + Vector args; + + char str[1024]; + snprintf(str, 1024, "%p", bt_buffer[i]); + args.push_back(str); + args.push_back("-e"); + args.push_back(_execpath); + + String output = ""; + + // Try to get the file/line number using addr2line + int ret; + int err = execute(String("addr2line"), args, true, nullptr, &output, &ret); + //if (err == 0) { + // output.erase(output.size() - 1, 1); + //} + + fprintf(stderr, "[%ld] %s (%ls)\n", (long int)i, fname, output.c_str()); + } + + free(strings); + } + fprintf(stderr, "-- END OF BACKTRACE --\n"); + fprintf(stderr, "================================================================\n"); + + // Abort to pass the error to the OS + abort(); +} +#endif + +CrashHandlerLinux::CrashHandlerLinux() : CrashHandler() { +} + +CrashHandlerLinux::~CrashHandlerLinux() { +} + +void CrashHandlerLinux::disable() { + if (!enabled) { + return; + } + +#ifdef CRASH_HANDLER_ENABLED + signal(SIGSEGV, nullptr); + signal(SIGFPE, nullptr); + signal(SIGILL, nullptr); +#endif + + enabled = false; +} + +void CrashHandlerLinux::enable() { + if (enabled) { + return; + } + +#ifdef CRASH_HANDLER_ENABLED + signal(SIGSEGV, handle_crash); + signal(SIGFPE, handle_crash); + signal(SIGILL, handle_crash); +#endif + + enabled = true; +} diff --git a/platform/linux/crash_handler_linux.h b/platform/linux/crash_handler_linux.h new file mode 100644 index 0000000..f958fd9 --- /dev/null +++ b/platform/linux/crash_handler_linux.h @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* crash_handler_x11.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef CRASH_HANDLER_LINUX_H +#define CRASH_HANDLER_LINUX_H + +#include "core/os/crash_handler.h" + +class CrashHandlerLinux : public CrashHandler { +public: + void enable(); + void disable(); + + CrashHandlerLinux(); + ~CrashHandlerLinux(); +}; + +#endif // CRASH_HANDLER_LINUX_H diff --git a/platform/linux/detect.py b/platform/linux/detect.py new file mode 100644 index 0000000..3058ed3 --- /dev/null +++ b/platform/linux/detect.py @@ -0,0 +1,27 @@ +import os +import platform +import sys + + +def is_active(): + return True + + +def get_name(): + return "platform_linux" + + +def can_build(): + return True + + +def get_opts(): + return [] + +def get_flags(): + + return [] + + +def configure(env): + pass diff --git a/platform/platform_initializer.cpp b/platform/platform_initializer.cpp new file mode 100644 index 0000000..3da47f8 --- /dev/null +++ b/platform/platform_initializer.cpp @@ -0,0 +1,33 @@ +#include "platform_initializer.h" + +#include "core/error_macros.h" + +#if PLATFORM_LINUX +#include "linux/crash_handler_linux.h" +#endif + +void PlatformInitializer::allocate_crash_handler() { + ERR_FAIL_COND(_crash_handler); + +#if PLATFORM_LINUX + _crash_handler = new CrashHandlerLinux(); + _crash_handler->enable(); +#endif +} + +void PlatformInitializer::free_crash_handler() { + if (_crash_handler) { + delete _crash_handler; + _crash_handler = nullptr; + } +} + +void PlatformInitializer::allocate_all() { + allocate_crash_handler(); +} + +void PlatformInitializer::free_all() { + free_crash_handler(); +} + +CrashHandler *PlatformInitializer::_crash_handler = nullptr; \ No newline at end of file diff --git a/platform/platform_initializer.h b/platform/platform_initializer.h new file mode 100644 index 0000000..975a78b --- /dev/null +++ b/platform/platform_initializer.h @@ -0,0 +1,19 @@ +#ifndef PLATFORM_INITIALIZER_H +#define PLATFORM_INITIALIZER_H + +#include "core/os/crash_handler.h" + + +class PlatformInitializer { +public: + static void allocate_crash_handler(); + static void free_crash_handler(); + + static void allocate_all(); + static void free_all(); + +protected: + static CrashHandler *_crash_handler; +}; + +#endif \ No newline at end of file