Path: blob/master/platform/windows/crash_handler_windows_signal.cpp
20934 views
/**************************************************************************/1/* crash_handler_windows_signal.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "crash_handler_windows.h"3132#include "core/config/project_settings.h"33#include "core/io/file_access.h"34#include "core/object/script_language.h"35#include "core/os/main_loop.h"36#include "core/os/os.h"37#include "core/string/print_string.h"38#include "core/version.h"39#include "main/main.h"4041#ifdef CRASH_HANDLER_EXCEPTION4243#include <cxxabi.h>44#include <algorithm>45#include <csignal>46#include <cstdlib>47#include <string>48#include <vector>4950#include <psapi.h>5152#include "thirdparty/libbacktrace/backtrace.h"5354struct CrashHandlerData {55int64_t index = 0;56backtrace_state *state = nullptr;57int64_t offset = 0;58};5960int symbol_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function) {61CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data);62if (!function) {63return 0;64}6566char fname[1024];67snprintf(fname, 1024, "%s", function);6869if (function[0] == '_') {70int status;71char *demangled = abi::__cxa_demangle(function, nullptr, nullptr, &status);7273if (status == 0 && demangled) {74snprintf(fname, 1024, "%s", demangled);75}7677if (demangled) {78free(demangled);79}80}8182print_error(vformat("[%d] %s (%s:%d)", ch_data->index++, String::utf8(fname), String::utf8(filename), lineno));83return 0;84}8586void error_callback(void *data, const char *msg, int errnum) {87CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data);88if (ch_data->index == 0) {89print_error(vformat("Error(%d): %s", errnum, String::utf8(msg)));90} else {91print_error(vformat("[%d] error(%d): %s", ch_data->index++, errnum, String::utf8(msg)));92}93}9495int trace_callback(void *data, uintptr_t pc) {96CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data);97backtrace_pcinfo(ch_data->state, pc - ch_data->offset, &symbol_callback, &error_callback, data);98return 0;99}100101int64_t get_image_base(const String &p_path) {102Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);103if (f.is_null()) {104return 0;105}106{107f->seek(0x3c);108uint32_t pe_pos = f->get_32();109110f->seek(pe_pos);111uint32_t magic = f->get_32();112if (magic != 0x00004550) {113return 0;114}115}116int64_t opt_header_pos = f->get_position() + 0x14;117f->seek(opt_header_pos);118119uint16_t opt_header_magic = f->get_16();120if (opt_header_magic == 0x10B) {121f->seek(opt_header_pos + 0x1C);122return f->get_32();123} else if (opt_header_magic == 0x20B) {124f->seek(opt_header_pos + 0x18);125return f->get_64();126} else {127return 0;128}129}130131extern void CrashHandlerException(int signal) {132CrashHandlerData data;133134if (OS::get_singleton() == nullptr || OS::get_singleton()->is_disable_crash_handler() || IsDebuggerPresent()) {135return;136}137138if (OS::get_singleton()->is_crash_handler_silent()) {139std::_Exit(0);140}141142String msg;143if (ProjectSettings::get_singleton()) {144msg = GLOBAL_GET("debug/settings/crash_handler/message");145}146147// Tell MainLoop about the crash. This can be handled by users too in Node.148if (OS::get_singleton()->get_main_loop()) {149OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);150}151152print_error("\n================================================================");153print_error(vformat("%s: Program crashed with signal %d", __FUNCTION__, signal));154155// Print the engine version just before, so that people are reminded to include the version in backtrace reports.156if (String(GODOT_VERSION_HASH).is_empty()) {157print_error(vformat("Engine version: %s", GODOT_VERSION_FULL_NAME));158} else {159print_error(vformat("Engine version: %s (%s)", GODOT_VERSION_FULL_NAME, GODOT_VERSION_HASH));160}161print_error(vformat("Dumping the backtrace. %s", msg));162163String _execpath = OS::get_singleton()->get_executable_path();164165// Load process and image info to determine ASLR addresses offset.166MODULEINFO mi;167GetModuleInformation(GetCurrentProcess(), GetModuleHandle(nullptr), &mi, sizeof(mi));168int64_t image_mem_base = reinterpret_cast<int64_t>(mi.lpBaseOfDll);169int64_t image_file_base = get_image_base(_execpath);170data.offset = image_mem_base - image_file_base;171172if (FileAccess::exists(_execpath + ".debugsymbols")) {173_execpath = _execpath + ".debugsymbols";174}175_execpath = _execpath.replace_char('/', '\\');176177CharString cs = _execpath.utf8(); // Note: should remain in scope during backtrace_simple call.178data.state = backtrace_create_state(cs.get_data(), 0, &error_callback, reinterpret_cast<void *>(&data));179if (data.state != nullptr) {180data.index = 1;181backtrace_simple(data.state, 1, &trace_callback, &error_callback, reinterpret_cast<void *>(&data));182}183184print_error("-- END OF C++ BACKTRACE --");185print_error("================================================================");186187for (const Ref<ScriptBacktrace> &backtrace : ScriptServer::capture_script_backtraces(false)) {188if (!backtrace->is_empty()) {189print_error(backtrace->format());190print_error(vformat("-- END OF %s BACKTRACE --", backtrace->get_language_name().to_upper()));191print_error("================================================================");192}193}194}195#endif196197CrashHandler::CrashHandler() {198disabled = false;199}200201CrashHandler::~CrashHandler() {202}203204void CrashHandler::disable() {205if (disabled) {206return;207}208209#if defined(CRASH_HANDLER_EXCEPTION)210signal(SIGSEGV, nullptr);211signal(SIGFPE, nullptr);212signal(SIGILL, nullptr);213#endif214215disabled = true;216}217218void CrashHandler::initialize() {219#if defined(CRASH_HANDLER_EXCEPTION)220signal(SIGSEGV, CrashHandlerException);221signal(SIGFPE, CrashHandlerException);222signal(SIGILL, CrashHandlerException);223#endif224}225226227