Skip to content

Commit

Permalink
Added a crash handler to dump the backtrace on Windows, Linux and OS X
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelofg55 committed Sep 13, 2017
1 parent 06138bf commit 83fe937
Show file tree
Hide file tree
Showing 23 changed files with 786 additions and 23 deletions.
3 changes: 3 additions & 0 deletions core/os/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,9 @@ class OS {

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,
Expand Down
4 changes: 4 additions & 0 deletions editor/editor_run.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ Error EditorRun::run(const String &p_scene, const String p_custom_args, const Li
screen--;
}

if (OS::get_singleton()->is_disable_crash_handler()) {
args.push_back("--disable-crash-handler");
}

Rect2 screen_rect;
screen_rect.position = OS::get_singleton()->get_screen_position(screen);
screen_rect.size = OS::get_singleton()->get_screen_size(screen);
Expand Down
8 changes: 8 additions & 0 deletions editor/project_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,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;
Expand Down Expand Up @@ -999,6 +1003,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;
Expand Down
5 changes: 5 additions & 0 deletions main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print(" --frame-delay <ms> Simulate high CPU load (delay each frame by <ms> milliseconds).\n");
OS::get_singleton()->print(" --time-scale <scale> Force time scale (higher values are faster, 1.0 is normal speed).\n");
OS::get_singleton()->print(" --disable-render-loop Disable render loop so rendering only occurs when called explicitly from script.\n");
OS::get_singleton()->print(" --disable-crash-handler Disable crash handler when supported by the platform code.\n");
OS::get_singleton()->print(" --fixed-fps <fps> Forces a fixed ratio between process and fixed_process timing, for use when precision is required, or when rendering to video files. Setting this will disable real-time syncronization, so that run speed is only capped by performance\n");
OS::get_singleton()->print("\n");

Expand Down Expand Up @@ -247,6 +248,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
performance = memnew(Performance);
globals->add_singleton(ProjectSettings::Singleton("Performance", performance));

GLOBAL_DEF("debug/settings/backtrace/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 */
Expand Down Expand Up @@ -582,6 +585,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->print("Missing fixed-fps argument, aborting.\n");
goto error;
}
} else if (I->get() == "--disable-crash-handler") {
OS::get_singleton()->disable_crash_handler();
} else {

//test for game path
Expand Down
7 changes: 6 additions & 1 deletion platform/osx/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Import('env')

files = [
'crash_handler_osx.mm',
'os_osx.mm',
'godot_main_osx.mm',
'audio_driver_osx.cpp',
Expand All @@ -12,4 +13,8 @@ files = [
'power_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)
47 changes: 47 additions & 0 deletions platform/osx/crash_handler_osx.h
Original file line number Diff line number Diff line change
@@ -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
178 changes: 178 additions & 0 deletions platform/osx/crash_handler_osx.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*************************************************************************/
/* 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 <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)
return;

void *bt_buffer[256];
size_t size = backtrace(bt_buffer, 256);
String _execpath = OS::get_singleton()->get_executable_path();
String msg = GLOBAL_GET("debug/settings/backtrace/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
}
1 change: 0 additions & 1 deletion platform/osx/godot_main_osx.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
6 changes: 6 additions & 0 deletions platform/osx/os_osx.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -110,6 +111,8 @@ class OS_OSX : public OS_Unix {

power_osx *power_manager;

CrashHandler crash_handler;

float _mouse_scale(float p_scale) {
if (display_scale > 1.0)
return p_scale;
Expand Down Expand Up @@ -224,6 +227,9 @@ class OS_OSX : public OS_Unix {
void set_mouse_mode(MouseMode p_mode);
MouseMode get_mouse_mode() const;

void disable_crash_handler();
bool is_disable_crash_handler() const;

OS_OSX();
};

Expand Down
10 changes: 10 additions & 0 deletions platform/osx/os_osx.mm
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,8 @@ - (BOOL)canBecomeKeyWindow {

void OS_OSX::initialize_core() {

crash_handler.initialize();

OS_Unix::initialize_core();

DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_RESOURCES);
Expand Down Expand Up @@ -2022,3 +2024,11 @@ static void keyboardLayoutChanged(CFNotificationCenterRef center, void *observer
bool OS_OSX::_check_internal_feature_support(const String &p_feature) {
return p_feature == "pc" || p_feature == "s3tc";
}

void OS_OSX::disable_crash_handler() {
crash_handler.disable();
}

bool OS_OSX::is_disable_crash_handler() const {
return crash_handler.is_disabled();
}
3 changes: 2 additions & 1 deletion platform/windows/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ Import('env')

common_win = [
"context_gl_win.cpp",
"crash_handler_win.cpp",
"os_windows.cpp",
"ctxgl_procaddr.cpp",
"key_mapping_win.cpp",
"tcp_server_winsock.cpp",
"packet_peer_udp_winsock.cpp",
"stream_peer_winsock.cpp",
"joypad.cpp",
"power_windows.cpp",
"power_windows.cpp",
]

restarget = "godot_res" + env["OBJSUFFIX"]
Expand Down
Loading

0 comments on commit 83fe937

Please sign in to comment.