| 
									
										
										
										
											2023-01-05 13:25:55 +01:00
										 |  |  | /**************************************************************************/ | 
					
						
							|  |  |  | /*  crash_handler_macos.mm                                                */ | 
					
						
							|  |  |  | /**************************************************************************/ | 
					
						
							|  |  |  | /*                         This file is part of:                          */ | 
					
						
							|  |  |  | /*                             GODOT ENGINE                               */ | 
					
						
							|  |  |  | /*                        https://godotengine.org                         */ | 
					
						
							|  |  |  | /**************************************************************************/ | 
					
						
							|  |  |  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | 
					
						
							|  |  |  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | 
					
						
							|  |  |  | /*                                                                        */ | 
					
						
							|  |  |  | /* 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.                 */ | 
					
						
							|  |  |  | /**************************************************************************/ | 
					
						
							| 
									
										
										
										
											2018-01-05 00:50:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-20 09:28:22 +03:00
										 |  |  | #include "crash_handler_macos.h" | 
					
						
							| 
									
										
										
										
											2019-02-12 15:43:54 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-07 19:33:38 -03:00
										 |  |  | #include "core/config/project_settings.h" | 
					
						
							| 
									
										
										
										
											2019-02-12 15:43:54 +01:00
										 |  |  | #include "core/os/os.h" | 
					
						
							| 
									
										
										
										
											2022-05-05 11:45:42 +02:00
										 |  |  | #include "core/string/print_string.h" | 
					
						
							| 
									
										
										
										
											2021-04-12 16:59:23 +02:00
										 |  |  | #include "core/version.h" | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | #include "main/main.h" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <string.h> | 
					
						
							|  |  |  | #include <unistd.h> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-19 12:49:31 +01:00
										 |  |  | #if defined(DEBUG_ENABLED) | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | #define CRASH_HANDLER_ENABLED 1 | 
					
						
							|  |  |  | #endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CRASH_HANDLER_ENABLED | 
					
						
							|  |  |  | #include <cxxabi.h> | 
					
						
							|  |  |  | #include <dlfcn.h> | 
					
						
							|  |  |  | #include <execinfo.h> | 
					
						
							|  |  |  | #include <signal.h> | 
					
						
							| 
									
										
										
										
											2017-09-19 11:21:45 -03:00
										 |  |  | #include <stdlib.h> | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <mach-o/dyld.h> | 
					
						
							|  |  |  | #include <mach-o/getsect.h> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint64_t load_address() { | 
					
						
							|  |  |  | 	const struct segment_command_64 *cmd = getsegbyname("__TEXT"); | 
					
						
							|  |  |  | 	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) { | 
					
						
							| 
									
										
										
										
											2023-09-22 19:29:53 -06:00
										 |  |  | 	signal(SIGSEGV, SIG_DFL); | 
					
						
							|  |  |  | 	signal(SIGFPE, SIG_DFL); | 
					
						
							|  |  |  | 	signal(SIGILL, SIG_DFL); | 
					
						
							| 
									
										
										
										
											2024-02-01 16:17:29 +02:00
										 |  |  | 	signal(SIGTRAP, SIG_DFL); | 
					
						
							| 
									
										
										
										
											2023-09-22 19:29:53 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-29 11:47:24 +02:00
										 |  |  | 	if (OS::get_singleton() == nullptr) { | 
					
						
							| 
									
										
										
										
											2018-09-24 22:21:05 +01:00
										 |  |  | 		abort(); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	void *bt_buffer[256]; | 
					
						
							|  |  |  | 	size_t size = backtrace(bt_buffer, 256); | 
					
						
							|  |  |  | 	String _execpath = OS::get_singleton()->get_executable_path(); | 
					
						
							| 
									
										
										
										
											2019-05-23 19:57:10 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	String msg; | 
					
						
							|  |  |  | 	const ProjectSettings *proj_settings = ProjectSettings::get_singleton(); | 
					
						
							|  |  |  | 	if (proj_settings) { | 
					
						
							|  |  |  | 		msg = proj_settings->get("debug/settings/crash_handler/message"); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-05 11:45:42 +02:00
										 |  |  | 	// Tell MainLoop about the crash. This can be handled by users too in Node. | 
					
						
							| 
									
										
										
										
											2022-02-16 13:56:32 +01:00
										 |  |  | 	if (OS::get_singleton()->get_main_loop()) { | 
					
						
							| 
									
										
										
										
											2018-07-02 16:18:58 -03:00
										 |  |  | 		OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH); | 
					
						
							| 
									
										
										
										
											2022-02-16 13:56:32 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-02 16:18:58 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-05 11:45:42 +02:00
										 |  |  | 	// Dump the backtrace to stderr with a message to the user | 
					
						
							|  |  |  | 	print_error("\n================================================================"); | 
					
						
							|  |  |  | 	print_error(vformat("%s: Program crashed with signal %d", __FUNCTION__, sig)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-12 16:59:23 +02:00
										 |  |  | 	// Print the engine version just before, so that people are reminded to include the version in backtrace reports. | 
					
						
							| 
									
										
										
										
											2022-02-08 20:50:37 +01:00
										 |  |  | 	if (String(VERSION_HASH).is_empty()) { | 
					
						
							| 
									
										
										
										
											2022-05-05 11:45:42 +02:00
										 |  |  | 		print_error(vformat("Engine version: %s", VERSION_FULL_NAME)); | 
					
						
							| 
									
										
										
										
											2021-04-12 16:59:23 +02:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2022-05-05 11:45:42 +02:00
										 |  |  | 		print_error(vformat("Engine version: %s (%s)", VERSION_FULL_NAME, VERSION_HASH)); | 
					
						
							| 
									
										
										
										
											2021-04-12 16:59:23 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-05-05 11:45:42 +02:00
										 |  |  | 	print_error(vformat("Dumping the backtrace. %s", msg)); | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 	char **strings = backtrace_symbols(bt_buffer, size); | 
					
						
							|  |  |  | 	if (strings) { | 
					
						
							|  |  |  | 		void *load_addr = (void *)load_address(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-24 17:34:18 +03:00
										 |  |  | 		for (size_t i = 1; i < size; i++) { | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 			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; | 
					
						
							| 
									
										
										
										
											2021-04-29 11:47:24 +02:00
										 |  |  | 					char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status); | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					if (status == 0 && demangled) { | 
					
						
							|  |  |  | 						snprintf(fname, 1024, "%s", demangled); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-16 13:56:32 +01:00
										 |  |  | 					if (demangled) { | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 						free(demangled); | 
					
						
							| 
									
										
										
										
											2022-02-16 13:56:32 +01:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			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); | 
					
						
							| 
									
										
										
										
											2021-11-15 15:04:01 +02:00
										 |  |  | #if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 				args.push_back("-arch"); | 
					
						
							|  |  |  | 				args.push_back("x86_64"); | 
					
						
							| 
									
										
										
										
											2021-11-15 15:04:01 +02:00
										 |  |  | #elif defined(__aarch64__) | 
					
						
							|  |  |  | 				args.push_back("-arch"); | 
					
						
							|  |  |  | 				args.push_back("arm64"); | 
					
						
							|  |  |  | #endif | 
					
						
							| 
									
										
										
										
											2024-12-11 09:21:25 +02:00
										 |  |  | 				args.push_back("--fullPath"); | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 				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 = ""; | 
					
						
							| 
									
										
										
										
											2020-12-18 18:49:13 +00:00
										 |  |  | 				Error err = OS::get_singleton()->execute(String("atos"), args, &out, &ret); | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 				if (err == OK && out.substr(0, 2) != "0x") { | 
					
						
							| 
									
										
										
										
											2021-11-11 09:08:08 +01:00
										 |  |  | 					out = out.substr(0, out.length() - 1); | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 					output = out; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-05 11:45:42 +02:00
										 |  |  | 			print_error(vformat("[%d] %s", (int64_t)i, output)); | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		free(strings); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-05-05 11:45:42 +02:00
										 |  |  | 	print_error("-- END OF BACKTRACE --"); | 
					
						
							|  |  |  | 	print_error("================================================================"); | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Abort to pass the error to the OS | 
					
						
							|  |  |  | 	abort(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CrashHandler::CrashHandler() { | 
					
						
							|  |  |  | 	disabled = false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CrashHandler::~CrashHandler() { | 
					
						
							| 
									
										
										
										
											2018-09-24 22:21:05 +01:00
										 |  |  | 	disable(); | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CrashHandler::disable() { | 
					
						
							| 
									
										
										
										
											2022-02-16 13:56:32 +01:00
										 |  |  | 	if (disabled) { | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2022-02-16 13:56:32 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | #ifdef CRASH_HANDLER_ENABLED | 
					
						
							| 
									
										
										
										
											2023-09-22 19:29:53 -06:00
										 |  |  | 	signal(SIGSEGV, SIG_DFL); | 
					
						
							|  |  |  | 	signal(SIGFPE, SIG_DFL); | 
					
						
							|  |  |  | 	signal(SIGILL, SIG_DFL); | 
					
						
							| 
									
										
										
										
											2024-02-01 16:17:29 +02:00
										 |  |  | 	signal(SIGTRAP, SIG_DFL); | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | #endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	disabled = true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CrashHandler::initialize() { | 
					
						
							|  |  |  | #ifdef CRASH_HANDLER_ENABLED | 
					
						
							|  |  |  | 	signal(SIGSEGV, handle_crash); | 
					
						
							|  |  |  | 	signal(SIGFPE, handle_crash); | 
					
						
							|  |  |  | 	signal(SIGILL, handle_crash); | 
					
						
							| 
									
										
										
										
											2024-02-01 16:17:29 +02:00
										 |  |  | 	signal(SIGTRAP, handle_crash); | 
					
						
							| 
									
										
										
										
											2017-09-07 22:01:49 -03:00
										 |  |  | #endif | 
					
						
							|  |  |  | } |