From 60cf34b968b14ea7bebd267c4328eb5a5cda89e5 Mon Sep 17 00:00:00 2001
From: Marcelo Fernandez <marcelofg55@gmail.com>
Date: Thu, 3 Aug 2017 21:07:16 -0300
Subject: [PATCH] Added a crash handler to dump the backtrace on Windows, Linux
 and OS X

---
 core/os/os.h                           |   3 +
 editor/editor_run.cpp                  |   4 +
 editor/project_manager.cpp             |   8 +
 main/main.cpp                          |   5 +
 platform/osx/SCsub                     |   7 +-
 platform/osx/crash_handler_osx.h       |  47 ++++++
 platform/osx/crash_handler_osx.mm      | 179 +++++++++++++++++++++
 platform/osx/godot_main_osx.mm         |   1 -
 platform/osx/os_osx.h                  |   6 +
 platform/osx/os_osx.mm                 |  10 ++
 platform/windows/SCsub                 |   1 +
 platform/windows/crash_handler_win.cpp | 211 +++++++++++++++++++++++++
 platform/windows/crash_handler_win.h   |  56 +++++++
 platform/windows/detect.py             |   2 +-
 platform/windows/godot_win.cpp         |  20 ++-
 platform/windows/os_windows.cpp        |  10 ++
 platform/windows/os_windows.h          |   8 +-
 platform/x11/SCsub                     |  11 +-
 platform/x11/crash_handler_x11.cpp     | 136 ++++++++++++++++
 platform/x11/crash_handler_x11.h       |  47 ++++++
 platform/x11/detect.py                 |   1 +
 platform/x11/os_x11.cpp                |  15 ++
 platform/x11/os_x11.h                  |   7 +
 23 files changed, 782 insertions(+), 13 deletions(-)
 create mode 100644 platform/osx/crash_handler_osx.h
 create mode 100644 platform/osx/crash_handler_osx.mm
 create mode 100644 platform/windows/crash_handler_win.cpp
 create mode 100644 platform/windows/crash_handler_win.h
 create mode 100644 platform/x11/crash_handler_x11.cpp
 create mode 100644 platform/x11/crash_handler_x11.h

diff --git a/core/os/os.h b/core/os/os.h
index d636d154999..5ee24c9585c 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -285,6 +285,9 @@ public:
 
 	bool is_stdout_verbose() const;
 
+	virtual void disable_crash_handler() {}
+	virtual bool is_disable_crash_handler() const { return false; }
+
 	enum CursorShape {
 		CURSOR_ARROW,
 		CURSOR_IBEAM,
diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp
index 0a8047d9232..a2047273df3 100644
--- a/editor/editor_run.cpp
+++ b/editor/editor_run.cpp
@@ -64,6 +64,10 @@ Error EditorRun::run(const String &p_scene, const String p_custom_args, const Li
 		args.push_back("-debugnav");
 	}
 
+	if (OS::get_singleton()->is_disable_crash_handler()) {
+		args.push_back("--disable-crash-handler");
+	}
+
 	int screen = EditorSettings::get_singleton()->get("game_window_placement/screen");
 
 	if (screen == 0) {
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 9c8cff1ee81..a2c23acdbc7 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -945,6 +945,10 @@ void ProjectManager::_open_project_confirm() {
 
 		args.push_back("-editor");
 
+		if (OS::get_singleton()->is_disable_crash_handler()) {
+			args.push_back("--disable-crash-handler");
+		}
+
 		String exec = OS::get_singleton()->get_executable_path();
 
 		OS::ProcessID pid = 0;
@@ -985,6 +989,10 @@ void ProjectManager::_run_project_confirm() {
 		args.push_back("-path");
 		args.push_back(path);
 
+		if (OS::get_singleton()->is_disable_crash_handler()) {
+			args.push_back("--disable-crash-handler");
+		}
+
 		String exec = OS::get_singleton()->get_executable_path();
 
 		OS::ProcessID pid = 0;
diff --git a/main/main.cpp b/main/main.cpp
index e94e3626681..5727784517f 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -179,6 +179,7 @@ void Main::print_help(const char *p_binary) {
 	OS::get_singleton()->print("\t-lang [locale]: Use a specific locale\n");
 	OS::get_singleton()->print("\t-rfs <host/ip>[:<port>] : Remote FileSystem.\n");
 	OS::get_singleton()->print("\t-rfs_pass <password> : Password for Remote FileSystem.\n");
+	OS::get_singleton()->print("\t-dch : Disable crash handler when supported by the platform code.\n");
 #ifdef TOOLS_ENABLED
 	OS::get_singleton()->print("\t-doctool FILE: Dump the whole engine api to FILE in XML format. If FILE exists, it will be merged.\n");
 	OS::get_singleton()->print("\t-nodocbase: Disallow dump the base types (used with -doctool).\n");
@@ -212,6 +213,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 	performance = memnew(Performance);
 	globals->add_singleton(Globals::Singleton("Performance", performance));
 
+	GLOBAL_DEF("application/crash_handler_message", String("Please include this when reporting the bug on https://github.com/godotengine/godot/issues"));
+
 	MAIN_PRINT("Main: Parse CMDLine");
 
 	/* argument parsing and main creation */
@@ -536,6 +539,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 			} else {
 				goto error;
 			}
+		} else if (I->get() == "-dch") {
+			OS::get_singleton()->disable_crash_handler();
 		} else {
 
 			//test for game path
diff --git a/platform/osx/SCsub b/platform/osx/SCsub
index c8e0e17612a..d53c52b4e6e 100644
--- a/platform/osx/SCsub
+++ b/platform/osx/SCsub
@@ -3,6 +3,7 @@
 Import('env')
 
 files = [
+    'crash_handler_osx.mm',
     'os_osx.mm',
     'godot_main_osx.mm',
     'audio_driver_osx.cpp',
@@ -12,4 +13,8 @@ files = [
     'joystick_osx.cpp',
 ]
 
-env.Program('#bin/godot', files)
+prog = env.Program('#bin/godot', files)
+if (env['target'] == "debug" or env['target'] == "release_debug"):
+    # Build the .dSYM file for atos
+    action = "dsymutil " + File(prog)[0].path + " -o " + File(prog)[0].path + ".dSYM"
+    env.AddPostAction(prog, action)
diff --git a/platform/osx/crash_handler_osx.h b/platform/osx/crash_handler_osx.h
new file mode 100644
index 00000000000..ff037e6b7a6
--- /dev/null
+++ b/platform/osx/crash_handler_osx.h
@@ -0,0 +1,47 @@
+/*************************************************************************/
+/*  crash_handler_osx.h                                                  */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 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_OSX_H
+#define CRASH_HANDLER_OSX_H
+
+class CrashHandler {
+
+	bool disabled;
+
+public:
+	void initialize();
+
+	void disable();
+	bool is_disabled() const { return disabled; };
+
+	CrashHandler();
+	~CrashHandler();
+};
+
+#endif
diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm
new file mode 100644
index 00000000000..469fa2c4927
--- /dev/null
+++ b/platform/osx/crash_handler_osx.mm
@@ -0,0 +1,179 @@
+/*************************************************************************/
+/*  crash_handler_osx.mm                                                 */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 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 "main/main.h"
+#include "os_osx.h"
+
+#include <string.h>
+#include <unistd.h>
+
+// Note: Dump backtrace in 32bit mode is getting a bus error on the fgets by the ->execute, so enable only on 64bit
+#if defined(DEBUG_ENABLED) && defined(__x86_64__)
+#define CRASH_HANDLER_ENABLED 1
+#endif
+
+#ifdef CRASH_HANDLER_ENABLED
+#include <cxxabi.h>
+#include <dlfcn.h>
+#include <execinfo.h>
+#include <signal.h>
+#include <stdlib.h>
+
+#include <mach-o/dyld.h>
+#include <mach-o/getsect.h>
+
+#ifdef __x86_64__
+static uint64_t load_address() {
+	const struct segment_command_64 *cmd = getsegbyname("__TEXT");
+#else
+static uint32_t load_address() {
+	const struct segment_command *cmd = getsegbyname("__TEXT");
+#endif
+	char full_path[1024];
+	uint32_t size = sizeof(full_path);
+
+	if (cmd && !_NSGetExecutablePath(full_path, &size)) {
+		uint32_t dyld_count = _dyld_image_count();
+		for (uint32_t i = 0; i < dyld_count; i++) {
+			const char *image_name = _dyld_get_image_name(i);
+			if (image_name && strncmp(image_name, full_path, 1024) == 0) {
+				return cmd->vmaddr + _dyld_get_image_vmaddr_slide(i);
+			}
+		}
+	}
+
+	return 0;
+}
+
+static void handle_crash(int sig) {
+	if (OS::get_singleton() == NULL || Globals::get_singleton() == NULL)
+		return;
+
+	void *bt_buffer[256];
+	size_t size = backtrace(bt_buffer, 256);
+	String _execpath = OS::get_singleton()->get_executable_path();
+	String msg = Globals::get_singleton()->get("application/crash_handler_message");
+
+	// Dump the backtrace to stderr with a message to the user
+	fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig);
+	fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
+	char **strings = backtrace_symbols(bt_buffer, size);
+	if (strings) {
+		void *load_addr = (void *)load_address();
+
+		for (int 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, NULL, 0, &status);
+
+					if (status == 0 && demangled) {
+						snprintf(fname, 1024, "%s", demangled);
+					}
+
+					if (demangled)
+						free(demangled);
+				}
+			}
+
+			String output = fname;
+
+			// Try to get the file/line number using atos
+			if (bt_buffer[i] > (void *)0x0 && OS::get_singleton()) {
+				List<String> args;
+				char str[1024];
+
+				args.push_back("-o");
+				args.push_back(_execpath);
+				args.push_back("-arch");
+#ifdef __x86_64__
+				args.push_back("x86_64");
+#else
+				args.push_back("i386");
+#endif
+				args.push_back("-l");
+				snprintf(str, 1024, "%p", load_addr);
+				args.push_back(str);
+				snprintf(str, 1024, "%p", bt_buffer[i]);
+				args.push_back(str);
+
+				int ret;
+				String out = "";
+				Error err = OS::get_singleton()->execute(String("atos"), args, true, NULL, &out, &ret);
+				if (err == OK && out.substr(0, 2) != "0x") {
+					out.erase(out.length() - 1, 1);
+					output = out;
+				}
+			}
+
+			fprintf(stderr, "[%d] %ls\n", i, output.c_str());
+		}
+
+		free(strings);
+	}
+	fprintf(stderr, "-- END OF BACKTRACE --\n");
+
+	// Abort to pass the error to the OS
+	abort();
+}
+#endif
+
+CrashHandler::CrashHandler() {
+	disabled = false;
+}
+
+CrashHandler::~CrashHandler() {
+}
+
+void CrashHandler::disable() {
+	if (disabled)
+		return;
+
+#ifdef CRASH_HANDLER_ENABLED
+	signal(SIGSEGV, NULL);
+	signal(SIGFPE, NULL);
+	signal(SIGILL, NULL);
+#endif
+
+	disabled = true;
+}
+
+void CrashHandler::initialize() {
+#ifdef CRASH_HANDLER_ENABLED
+	signal(SIGSEGV, handle_crash);
+	signal(SIGFPE, handle_crash);
+	signal(SIGILL, handle_crash);
+#endif
+}
diff --git a/platform/osx/godot_main_osx.mm b/platform/osx/godot_main_osx.mm
index a1f65f21806..b7a02b72843 100644
--- a/platform/osx/godot_main_osx.mm
+++ b/platform/osx/godot_main_osx.mm
@@ -35,7 +35,6 @@
 #include <unistd.h>
 
 int main(int argc, char **argv) {
-
 	int first_arg = 1;
 	const char *dbg_arg = "-NSDocumentRevisionsDebugMode";
 	printf("arguments\n");
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index 0b6f45abe29..de97b89ec09 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -30,6 +30,7 @@
 #ifndef OS_OSX_H
 #define OS_OSX_H
 
+#include "crash_handler_osx.h"
 #include "drivers/alsa/audio_driver_alsa.h"
 #include "drivers/rtaudio/audio_driver_rtaudio.h"
 #include "drivers/unix/os_unix.h"
@@ -111,6 +112,8 @@ public:
 	Size2 window_size;
 	Rect2 restore_rect;
 
+	CrashHandler crash_handler;
+
 	float _mouse_scale(float p_scale) {
 		if (display_scale > 1.0)
 			return p_scale;
@@ -214,6 +217,9 @@ public:
 	void set_mouse_mode(MouseMode p_mode);
 	MouseMode get_mouse_mode() const;
 
+	void disable_crash_handler();
+	bool is_disable_crash_handler() const;
+
 	OS_OSX();
 };
 
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 48cf40585fa..a55354a4254 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -842,6 +842,8 @@ OS::VideoMode OS_OSX::get_default_video_mode() const {
 
 void OS_OSX::initialize_core() {
 
+	crash_handler.initialize();
+
 	OS_Unix::initialize_core();
 
 	DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_RESOURCES);
@@ -1888,3 +1890,11 @@ OS_OSX::OS_OSX() {
 	zoomed = false;
 	display_scale = 1.0;
 }
+
+void OS_OSX::disable_crash_handler() {
+	crash_handler.disable();
+}
+
+bool OS_OSX::is_disable_crash_handler() const {
+	return crash_handler.is_disabled();
+}
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 32c23b906ad..395489b62b0 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -5,6 +5,7 @@ Import('env')
 
 common_win = [
     "context_gl_win.cpp",
+    "crash_handler_win.cpp",
     "os_windows.cpp",
     "ctxgl_procaddr.cpp",
     "key_mapping_win.cpp",
diff --git a/platform/windows/crash_handler_win.cpp b/platform/windows/crash_handler_win.cpp
new file mode 100644
index 00000000000..51b0d73b7a5
--- /dev/null
+++ b/platform/windows/crash_handler_win.cpp
@@ -0,0 +1,211 @@
+/*************************************************************************/
+/*  crash_handler_win.cpp                                                */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 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 "main/main.h"
+#include "os_windows.h"
+
+#ifdef CRASH_HANDLER_EXCEPTION
+
+// Backtrace code code based on: https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app
+
+#include <psapi.h>
+#include <algorithm>
+#include <iterator>
+
+#pragma comment(lib, "psapi.lib")
+#pragma comment(lib, "dbghelp.lib")
+
+// Some versions of imagehlp.dll lack the proper packing directives themselves
+// so we need to do it.
+#pragma pack(push, before_imagehlp, 8)
+#include <imagehlp.h>
+#pragma pack(pop, before_imagehlp)
+
+struct module_data {
+	std::string image_name;
+	std::string module_name;
+	void *base_address;
+	DWORD load_size;
+};
+
+class symbol {
+	typedef IMAGEHLP_SYMBOL64 sym_type;
+	sym_type *sym;
+	static const int max_name_len = 1024;
+
+public:
+	symbol(HANDLE process, DWORD64 address)
+		: sym((sym_type *)::operator new(sizeof(*sym) + max_name_len)) {
+		memset(sym, '\0', sizeof(*sym) + max_name_len);
+		sym->SizeOfStruct = sizeof(*sym);
+		sym->MaxNameLength = max_name_len;
+		DWORD64 displacement;
+
+		SymGetSymFromAddr64(process, address, &displacement, sym);
+	}
+
+	std::string name() { return std::string(sym->Name); }
+	std::string undecorated_name() {
+		if (*sym->Name == '\0')
+			return "<couldn't map PC to fn name>";
+		std::vector<char> und_name(max_name_len);
+		UnDecorateSymbolName(sym->Name, &und_name[0], max_name_len, UNDNAME_COMPLETE);
+		return std::string(&und_name[0], strlen(&und_name[0]));
+	}
+};
+
+class get_mod_info {
+	HANDLE process;
+
+public:
+	get_mod_info(HANDLE h)
+		: process(h) {}
+
+	module_data operator()(HMODULE module) {
+		module_data ret;
+		char temp[4096];
+		MODULEINFO mi;
+
+		GetModuleInformation(process, module, &mi, sizeof(mi));
+		ret.base_address = mi.lpBaseOfDll;
+		ret.load_size = mi.SizeOfImage;
+
+		GetModuleFileNameEx(process, module, temp, sizeof(temp));
+		ret.image_name = temp;
+		GetModuleBaseName(process, module, temp, sizeof(temp));
+		ret.module_name = temp;
+		std::vector<char> img(ret.image_name.begin(), ret.image_name.end());
+		std::vector<char> mod(ret.module_name.begin(), ret.module_name.end());
+		SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, ret.load_size);
+		return ret;
+	}
+};
+
+DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
+	HANDLE process = GetCurrentProcess();
+	HANDLE hThread = GetCurrentThread();
+	DWORD offset_from_symbol = 0;
+	IMAGEHLP_LINE64 line = { 0 };
+	std::vector<module_data> modules;
+	DWORD cbNeeded;
+	std::vector<HMODULE> module_handles(1);
+
+	if (OS::get_singleton() == NULL || OS::get_singleton()->is_disable_crash_handler() || Globals::get_singleton() == NULL || IsDebuggerPresent()) {
+		return EXCEPTION_CONTINUE_SEARCH;
+	}
+
+	fprintf(stderr, "%s: Program crashed\n", __FUNCTION__);
+
+	// Load the symbols:
+	if (!SymInitialize(process, NULL, false))
+		return EXCEPTION_CONTINUE_SEARCH;
+
+	SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
+	EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded);
+	module_handles.resize(cbNeeded / sizeof(HMODULE));
+	EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded);
+	std::transform(module_handles.begin(), module_handles.end(), std::back_inserter(modules), get_mod_info(process));
+	void *base = modules[0].base_address;
+
+	// Setup stuff:
+	CONTEXT *context = ep->ContextRecord;
+	STACKFRAME64 frame;
+	bool skip_first = false;
+
+	frame.AddrPC.Mode = AddrModeFlat;
+	frame.AddrStack.Mode = AddrModeFlat;
+	frame.AddrFrame.Mode = AddrModeFlat;
+
+#ifdef _M_X64
+	frame.AddrPC.Offset = context->Rip;
+	frame.AddrStack.Offset = context->Rsp;
+	frame.AddrFrame.Offset = context->Rbp;
+#else
+	frame.AddrPC.Offset = context->Eip;
+	frame.AddrStack.Offset = context->Esp;
+	frame.AddrFrame.Offset = context->Ebp;
+
+	// Skip the first one to avoid a duplicate on 32-bit mode
+	skip_first = true;
+#endif
+
+	line.SizeOfStruct = sizeof(line);
+	IMAGE_NT_HEADERS *h = ImageNtHeader(base);
+	DWORD image_type = h->FileHeader.Machine;
+	int n = 0;
+	String msg = Globals::get_singleton()->get("application/crash_handler_message");
+
+	fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
+
+	do {
+		if (skip_first) {
+			skip_first = false;
+		} else {
+			if (frame.AddrPC.Offset != 0) {
+				std::string fnName = symbol(process, frame.AddrPC.Offset).undecorated_name();
+
+				if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &offset_from_symbol, &line))
+					fprintf(stderr, "[%d] %s (%s:%d)\n", n, fnName.c_str(), line.FileName, line.LineNumber);
+				else
+					fprintf(stderr, "[%d] %s\n", n, fnName.c_str());
+			} else
+				fprintf(stderr, "[%d] ???\n", n);
+
+			n++;
+		}
+
+		if (!StackWalk64(image_type, process, hThread, &frame, context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
+			break;
+	} while (frame.AddrReturn.Offset != 0 && n < 256);
+
+	fprintf(stderr, "-- END OF BACKTRACE --\n");
+
+	SymCleanup(process);
+
+	// Pass the exception to the OS
+	return EXCEPTION_CONTINUE_SEARCH;
+}
+#endif
+
+CrashHandler::CrashHandler() {
+	disabled = false;
+}
+
+CrashHandler::~CrashHandler() {
+}
+
+void CrashHandler::disable() {
+	if (disabled)
+		return;
+
+	disabled = true;
+}
+
+void CrashHandler::initialize() {
+}
diff --git a/platform/windows/crash_handler_win.h b/platform/windows/crash_handler_win.h
new file mode 100644
index 00000000000..0b1889e4fe3
--- /dev/null
+++ b/platform/windows/crash_handler_win.h
@@ -0,0 +1,56 @@
+/*************************************************************************/
+/*  crash_handler_win.h                                                  */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 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_WIN_H
+#define CRASH_HANDLER_WIN_H
+
+#include <windows.h>
+
+// Crash handler exception only enabled with MSVC
+#if defined(DEBUG_ENABLED) && defined(MSVC)
+#define CRASH_HANDLER_EXCEPTION 1
+
+extern DWORD CrashHandlerException(EXCEPTION_POINTERS *ep);
+#endif
+
+class CrashHandler {
+
+	bool disabled;
+
+public:
+	void initialize();
+
+	void disable();
+	bool is_disabled() const { return disabled; };
+
+	CrashHandler();
+	~CrashHandler();
+};
+
+#endif
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 9addee1fa81..892349d2ce4 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -240,7 +240,7 @@ def configure(env):
 
         elif (env["target"] == "debug"):
 
-            env.Append(CCFLAGS=['/Z7', '/DDEBUG_ENABLED', '/DDEBUG_MEMORY_ENABLED', '/DD3D_DEBUG_INFO', '/Od'])
+            env.Append(CCFLAGS=['/Z7', '/DDEBUG_ENABLED', '/DDEBUG_MEMORY_ENABLED', '/DD3D_DEBUG_INFO', '/Od', '/EHsc'])
             env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE'])
             env.Append(LINKFLAGS=['/DEBUG'])
 
diff --git a/platform/windows/godot_win.cpp b/platform/windows/godot_win.cpp
index d2ac6ecb506..cff2cbad429 100644
--- a/platform/windows/godot_win.cpp
+++ b/platform/windows/godot_win.cpp
@@ -156,10 +156,7 @@ int widechar_main(int argc, wchar_t **argv) {
 	return os.get_exit_code();
 };
 
-int main(int _argc, char **_argv) {
-	// _argc and _argv are ignored
-	// we are going to use the WideChar version of them instead
-
+int _main() {
 	LPWSTR *wc_argv;
 	int argc;
 	int result;
@@ -177,6 +174,21 @@ int main(int _argc, char **_argv) {
 	return result;
 }
 
+int main(int _argc, char **_argv) {
+// _argc and _argv are ignored
+// we are going to use the WideChar version of them instead
+
+#ifdef CRASH_HANDLER_EXCEPTION
+	__try {
+		return _main();
+	} __except (CrashHandlerException(GetExceptionInformation())) {
+		return 1;
+	}
+#else
+	return _main();
+#endif
+}
+
 HINSTANCE godot_hinstance = NULL;
 
 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index e65f1683631..bf9cdee5cf9 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -170,6 +170,8 @@ static MemoryPoolDynamic *mempool_dynamic = NULL;
 
 void OS_Windows::initialize_core() {
 
+	crash_handler.initialize();
+
 	last_button_state = 0;
 
 	//RedirectIOToConsole();
@@ -2309,6 +2311,14 @@ bool OS_Windows::is_vsync_enabled() const {
 	return true;
 }
 
+void OS_Windows::disable_crash_handler() {
+	crash_handler.disable();
+}
+
+bool OS_Windows::is_disable_crash_handler() const {
+	return crash_handler.is_disabled();
+}
+
 OS_Windows::OS_Windows(HINSTANCE _hInstance) {
 
 	key_event_pos = 0;
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 505b63a786d..ddd082b3cf8 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -31,6 +31,7 @@
 #define OS_WINDOWS_H
 
 #include "context_gl_win.h"
+#include "crash_handler_win.h"
 #include "os/input.h"
 #include "os/os.h"
 #include "servers/physics/physics_server_sw.h"
@@ -38,8 +39,8 @@
 #include "servers/visual_server.h"
 
 #include "drivers/rtaudio/audio_driver_rtaudio.h"
-#include "drivers/wasapi/audio_driver_wasapi.h"
 #include "drivers/unix/ip_unix.h"
+#include "drivers/wasapi/audio_driver_wasapi.h"
 #include "servers/audio/audio_server_sw.h"
 #include "servers/audio/sample_manager_sw.h"
 #include "servers/physics_2d/physics_2d_server_sw.h"
@@ -137,6 +138,8 @@ class OS_Windows : public OS {
 	AudioDriverRtAudio driver_rtaudio;
 #endif
 
+	CrashHandler crash_handler;
+
 	void _drag_event(int p_x, int p_y, int idx);
 	void _touch_event(bool p_pressed, int p_x, int p_y, int idx);
 
@@ -277,6 +280,9 @@ public:
 	virtual void set_use_vsync(bool p_enable);
 	virtual bool is_vsync_enabled() const;
 
+	void disable_crash_handler();
+	bool is_disable_crash_handler() const;
+
 	OS_Windows(HINSTANCE _hInstance);
 	~OS_Windows();
 };
diff --git a/platform/x11/SCsub b/platform/x11/SCsub
index 0defd4f0255..21daf8c4e58 100644
--- a/platform/x11/SCsub
+++ b/platform/x11/SCsub
@@ -3,11 +3,12 @@
 Import('env')
 
 
-common_x11 = [\
-    "context_gl_x11.cpp",\
-    "os_x11.cpp",\
-    "key_mapping_x11.cpp",\
-    "joystick_linux.cpp",\
+common_x11 = [
+    "context_gl_x11.cpp",
+    "crash_handler_x11.cpp",
+    "os_x11.cpp",
+    "key_mapping_x11.cpp",
+    "joystick_linux.cpp",
 ]
 
 env.Program('#bin/godot', ['godot_x11.cpp'] + common_x11)
diff --git a/platform/x11/crash_handler_x11.cpp b/platform/x11/crash_handler_x11.cpp
new file mode 100644
index 00000000000..418f87c4b91
--- /dev/null
+++ b/platform/x11/crash_handler_x11.cpp
@@ -0,0 +1,136 @@
+/*************************************************************************/
+/*  crash_handler_x11.cpp                                                */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 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.                */
+/*************************************************************************/
+#ifdef DEBUG_ENABLED
+#define CRASH_HANDLER_ENABLED 1
+#endif
+
+#include "main/main.h"
+#include "os_x11.h"
+
+#ifdef CRASH_HANDLER_ENABLED
+#include <cxxabi.h>
+#include <dlfcn.h>
+#include <execinfo.h>
+#include <signal.h>
+#include <stdlib.h>
+
+static void handle_crash(int sig) {
+	if (OS::get_singleton() == NULL || Globals::get_singleton() == NULL)
+		return;
+
+	void *bt_buffer[256];
+	size_t size = backtrace(bt_buffer, 256);
+	String _execpath = OS::get_singleton()->get_executable_path();
+	String msg = Globals::get_singleton()->get("application/crash_handler_message");
+
+	// Dump the backtrace to stderr with a message to the user
+	fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig);
+	fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
+	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, NULL, 0, &status);
+
+					if (status == 0 && demangled) {
+						snprintf(fname, 1024, "%s", demangled);
+					}
+
+					if (demangled)
+						free(demangled);
+				}
+			}
+
+			List<String> 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
+			if (OS::get_singleton()) {
+				int ret;
+				Error err = OS::get_singleton()->execute(String("addr2line"), args, true, NULL, &output, &ret);
+				if (err == OK) {
+					output.erase(output.length() - 1, 1);
+				}
+			}
+
+			fprintf(stderr, "[%ld] %s (%ls)\n", i, fname, output.c_str());
+		}
+
+		free(strings);
+	}
+	fprintf(stderr, "-- END OF BACKTRACE --\n");
+
+	// Abort to pass the error to the OS
+	abort();
+}
+#endif
+
+CrashHandler::CrashHandler() {
+	disabled = false;
+}
+
+CrashHandler::~CrashHandler() {
+}
+
+void CrashHandler::disable() {
+	if (disabled)
+		return;
+
+#ifdef CRASH_HANDLER_ENABLED
+	signal(SIGSEGV, NULL);
+	signal(SIGFPE, NULL);
+	signal(SIGILL, NULL);
+#endif
+
+	disabled = true;
+}
+
+void CrashHandler::initialize() {
+#ifdef CRASH_HANDLER_ENABLED
+	signal(SIGSEGV, handle_crash);
+	signal(SIGFPE, handle_crash);
+	signal(SIGILL, handle_crash);
+#endif
+}
diff --git a/platform/x11/crash_handler_x11.h b/platform/x11/crash_handler_x11.h
new file mode 100644
index 00000000000..e01334cbf20
--- /dev/null
+++ b/platform/x11/crash_handler_x11.h
@@ -0,0 +1,47 @@
+/*************************************************************************/
+/*  crash_handler_x11.h                                                  */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 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_X11_H
+#define CRASH_HANDLER_X11_H
+
+class CrashHandler {
+
+	bool disabled;
+
+public:
+	void initialize();
+
+	void disable();
+	bool is_disabled() const { return disabled; };
+
+	CrashHandler();
+	~CrashHandler();
+};
+
+#endif
diff --git a/platform/x11/detect.py b/platform/x11/detect.py
index 9e2c9f6e9de..40a82861511 100644
--- a/platform/x11/detect.py
+++ b/platform/x11/detect.py
@@ -138,6 +138,7 @@ def configure(env):
     elif (env["target"] == "debug"):
 
         env.Prepend(CCFLAGS=['-g2', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED'])
+        env.Append(LINKFLAGS=['-rdynamic'])
 
     env.ParseConfig('pkg-config x11 --cflags --libs')
     env.ParseConfig('pkg-config xinerama --cflags --libs')
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp
index 569d7adc178..527ad1ae641 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -90,6 +90,13 @@ const char *OS_X11::get_audio_driver_name(int p_driver) const {
 	return AudioDriverManagerSW::get_driver(p_driver)->get_name();
 }
 
+void OS_X11::initialize_core() {
+
+	crash_handler.initialize();
+
+	OS_Unix::initialize_core();
+}
+
 void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
 
 	last_button_state = 0;
@@ -2015,3 +2022,11 @@ OS_X11::OS_X11() {
 	xim_style = 0L;
 	mouse_mode = MOUSE_MODE_VISIBLE;
 }
+
+void OS_X11::disable_crash_handler() {
+	crash_handler.disable();
+}
+
+bool OS_X11::is_disable_crash_handler() const {
+	return crash_handler.is_disabled();
+}
diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h
index f18a3711b9a..6c6492a9fb4 100644
--- a/platform/x11/os_x11.h
+++ b/platform/x11/os_x11.h
@@ -31,6 +31,7 @@
 #define OS_X11_H
 
 #include "context_gl_x11.h"
+#include "crash_handler_x11.h"
 #include "drivers/alsa/audio_driver_alsa.h"
 #include "drivers/pulseaudio/audio_driver_pulseaudio.h"
 #include "drivers/rtaudio/audio_driver_rtaudio.h"
@@ -173,6 +174,8 @@ class OS_X11 : public OS_Unix {
 
 	Atom net_wm_icon;
 
+	CrashHandler crash_handler;
+
 	int audio_driver_index;
 	unsigned int capture_idle;
 	bool maximized;
@@ -194,6 +197,7 @@ protected:
 	virtual int get_audio_driver_count() const;
 	virtual const char *get_audio_driver_name(int p_driver) const;
 
+	virtual void initialize_core();
 	virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
 	virtual void finalize();
 
@@ -266,6 +270,9 @@ public:
 
 	void run();
 
+	void disable_crash_handler();
+	bool is_disable_crash_handler() const;
+
 	OS_X11();
 };