diff options
Diffstat (limited to 'src/main.cpp')
-rw-r--r-- | src/main.cpp | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp index 35dcb708b..ec805f5d3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -102,6 +102,7 @@ static void list_game_ids(); static void list_worlds(bool print_name, bool print_path); static bool setup_log_params(const Settings &cmd_args); static bool create_userdata_path(); +static bool use_debugger(int argc, char *argv[]); static bool init_common(const Settings &cmd_args, int argc, char *argv[]); static void uninit_common(); static void startup_message(); @@ -162,6 +163,11 @@ int main(int argc, char *argv[]) if (!setup_log_params(cmd_args)) return 1; + if (cmd_args.getFlag("debugger")) { + if (!use_debugger(argc, argv)) + warningstream << "Continuing without debugger" << std::endl; + } + porting::signal_handler_init(); #ifdef __ANDROID__ @@ -341,6 +347,8 @@ static void set_allowed_options(OptionList *allowed_options) _("Print even more information to console")))); allowed_options->insert(std::make_pair("trace", ValueSpec(VALUETYPE_FLAG, _("Print enormous amounts of information to log and console")))); + allowed_options->insert(std::make_pair("debugger", ValueSpec(VALUETYPE_FLAG, + _("Try to automatically attach a debugger before starting (convenience option)")))); allowed_options->insert(std::make_pair("logfile", ValueSpec(VALUETYPE_STRING, _("Set logfile path ('' = no logging)")))); allowed_options->insert(std::make_pair("gameid", ValueSpec(VALUETYPE_STRING, @@ -535,6 +543,126 @@ static bool create_userdata_path() return success; } +namespace { + std::string findProgram(const char *name) + { + char *path_c = getenv("PATH"); + if (!path_c) + return ""; + std::istringstream iss(path_c); + std::string checkpath; + while (!iss.eof()) { + std::getline(iss, checkpath, PATH_DELIM[0]); + if (!checkpath.empty() && checkpath.back() != DIR_DELIM_CHAR) + checkpath.push_back(DIR_DELIM_CHAR); + checkpath.append(name); + if (fs::IsExecutable(checkpath)) + return checkpath; + } + return ""; + } + +#ifdef _WIN32 + const char *debuggerNames[] = {"gdb.exe", "lldb.exe"}; +#else + const char *debuggerNames[] = {"gdb", "lldb"}; +#endif + template <class T> + void getDebuggerArgs(T &out, int i) { + if (i == 0) { + for (auto s : {"-q", "--batch", "-iex", "set confirm off", + "-ex", "run", "-ex", "bt", "--args"}) + out.push_back(s); + } else if (i == 1) { + for (auto s : {"-Q", "-b", "-o", "run", "-k", "bt\nq", "--"}) + out.push_back(s); + } + } +} + +static bool use_debugger(int argc, char *argv[]) +{ +#if defined(__ANDROID__) + return false; +#else +#ifdef _WIN32 + if (IsDebuggerPresent()) { + warningstream << "Process is already being debugged." << std::endl; + return false; + } +#endif + + char exec_path[1024]; + if (!porting::getCurrentExecPath(exec_path, sizeof(exec_path))) + return false; + + int debugger = -1; + std::string debugger_path; + for (u32 i = 0; i < ARRLEN(debuggerNames); i++) { + debugger_path = findProgram(debuggerNames[i]); + if (!debugger_path.empty()) { + debugger = i; + break; + } + } + if (debugger == -1) { + warningstream << "Couldn't find a debugger to use. Try installing gdb or lldb." << std::endl; + return false; + } + + // Try to be helpful +#ifdef NDEBUG + if (strcmp(BUILD_TYPE, "RelWithDebInfo") != 0) { + warningstream << "It looks like your " PROJECT_NAME_C " executable was built without " + "debug symbols (BUILD_TYPE=" BUILD_TYPE "), so you won't get useful backtraces." + << std::endl; + } +#endif + + std::vector<const char*> new_args; + new_args.push_back(debugger_path.c_str()); + getDebuggerArgs(new_args, debugger); + // Copy the existing arguments + new_args.push_back(exec_path); + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "--debugger")) + continue; + new_args.push_back(argv[i]); + } + new_args.push_back(nullptr); + +#ifdef _WIN32 + // Special treatment for Windows + std::string cmdline; + for (int i = 1; new_args[i]; i++) { + if (i > 1) + cmdline += ' '; + cmdline += porting::QuoteArgv(new_args[i]); + } + + STARTUPINFO startup_info = {}; + PROCESS_INFORMATION process_info = {}; + bool ok = CreateProcess(new_args[0], cmdline.empty() ? nullptr : &cmdline[0], + nullptr, nullptr, false, CREATE_UNICODE_ENVIRONMENT, + nullptr, nullptr, &startup_info, &process_info); + if (!ok) { + warningstream << "CreateProcess: " << GetLastError() << std::endl; + return false; + } + DWORD exitcode = 0; + WaitForSingleObject(process_info.hProcess, INFINITE); + GetExitCodeProcess(process_info.hProcess, &exitcode); + exit(exitcode); + // not reached +#else + errno = 0; + execv(new_args[0], const_cast<char**>(new_args.data())); + warningstream << "execv: " << strerror(errno) << std::endl; + return false; +#endif +#endif +} + static bool init_common(const Settings &cmd_args, int argc, char *argv[]) { startup_message(); |