1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-08-11 17:51:04 +00:00

Implement --debugger option to improve UX when debugging crashes (#13157)

This commit is contained in:
sfan5 2023-01-23 00:19:30 +01:00 committed by GitHub
parent 6f5703baf1
commit 87d509e462
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 193 additions and 17 deletions

View file

@ -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();