#include "Driver.h"
#include "lldb/API/SBCommandInterpreter.h"
#include "lldb/API/SBCommandInterpreterRunOptions.h"
#include "lldb/API/SBCommandReturnObject.h"
#include "lldb/API/SBDebugger.h"
#include "lldb/API/SBFile.h"
#include "lldb/API/SBHostOS.h"
#include "lldb/API/SBLanguageRuntime.h"
#include "lldb/API/SBStream.h"
#include "lldb/API/SBStringList.h"
#include "lldb/API/SBStructuredData.h"
#include "lldb/Host/Config.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <atomic>
#include <bitset>
#include <clocale>
#include <csignal>
#include <future>
#include <string>
#include <thread>
#include <utility>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#ifdef MS_DEBUGGER
#include "cli_logo.h"
#endif
#if !defined(__APPLE__)
#include "llvm/Support/DataTypes.h"
#endif
using namespace lldb;
using namespace llvm;
namespace {
using namespace llvm::opt;
enum ID {
OPT_INVALID = 0,
#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
#include "Options.inc"
#undef OPTION
};
#define PREFIX(NAME, VALUE) \
static constexpr StringLiteral NAME##_init[] = VALUE; \
static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \
std::size(NAME##_init) - 1);
#include "Options.inc"
#undef PREFIX
static constexpr opt::OptTable::Info InfoTable[] = {
#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
#include "Options.inc"
#undef OPTION
};
class LLDBOptTable : public opt::GenericOptTable {
public:
LLDBOptTable() : opt::GenericOptTable(InfoTable) {}
};
}
static void reset_stdin_termios();
static bool g_old_stdin_termios_is_valid = false;
static struct termios g_old_stdin_termios;
static bool disable_color(const raw_ostream &OS) { return false; }
static Driver *g_driver = nullptr;
static void reset_stdin_termios() {
if (g_old_stdin_termios_is_valid) {
g_old_stdin_termios_is_valid = false;
::tcsetattr(STDIN_FILENO, TCSANOW, &g_old_stdin_termios);
}
}
Driver::Driver()
: SBBroadcaster("Driver"), m_debugger(SBDebugger::Create(false)) {
m_debugger.SetCloseInputOnEOF(false);
g_driver = this;
}
Driver::~Driver() {
SBDebugger::Destroy(m_debugger);
g_driver = nullptr;
}
void Driver::OptionData::AddInitialCommand(std::string command,
CommandPlacement placement,
bool is_file, SBError &error) {
std::vector<InitialCmdEntry> *command_set;
switch (placement) {
case eCommandPlacementBeforeFile:
command_set = &(m_initial_commands);
break;
case eCommandPlacementAfterFile:
command_set = &(m_after_file_commands);
break;
case eCommandPlacementAfterCrash:
command_set = &(m_after_crash_commands);
break;
}
if (is_file) {
SBFileSpec file(command.c_str());
if (file.Exists())
command_set->push_back(InitialCmdEntry(command, is_file));
else if (file.ResolveExecutableLocation()) {
char final_path[PATH_MAX];
file.GetPath(final_path, sizeof(final_path));
command_set->push_back(InitialCmdEntry(final_path, is_file));
} else
error.SetErrorStringWithFormat(
"file specified in --source (-s) option doesn't exist: '%s'",
command.c_str());
} else
command_set->push_back(InitialCmdEntry(command, is_file));
}
void Driver::WriteCommandsForSourcing(CommandPlacement placement,
SBStream &strm) {
std::vector<OptionData::InitialCmdEntry> *command_set;
switch (placement) {
case eCommandPlacementBeforeFile:
command_set = &m_option_data.m_initial_commands;
break;
case eCommandPlacementAfterFile:
command_set = &m_option_data.m_after_file_commands;
break;
case eCommandPlacementAfterCrash:
command_set = &m_option_data.m_after_crash_commands;
break;
}
for (const auto &command_entry : *command_set) {
const char *command = command_entry.contents.c_str();
if (command_entry.is_file) {
bool source_quietly =
m_option_data.m_source_quietly || command_entry.source_quietly;
strm.Printf("command source -s %i '%s'\n",
static_cast<int>(source_quietly), command);
} else
strm.Printf("%s\n", command);
}
}
SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) {
SBError error;
m_debugger.SkipLLDBInitFiles(false);
m_debugger.SkipAppInitFiles(false);
#ifdef MS_DEBUGGER
const bool has_version_arg = args.hasArg(OPT_version);
if (!has_version_arg) {
const bool should_colorize =
!args.hasArg(OPT_no_use_colors) && llvm::outs().has_colors();
constexpr llvm::StringLiteral kPinkStart("\033[95m");
constexpr llvm::StringLiteral kColorReset("\033[0m");
constexpr llvm::StringLiteral kMsdebugVersionPrefix("msdebug version ");
llvm::StringRef version = lldb::SBDebugger::GetVersionString();
llvm::StringRef version_first_line = version.split('\n').first.trim();
llvm::StringRef concise_version = version_first_line;
bool is_msdebug_version = false;
if (concise_version.consume_front(kMsdebugVersionPrefix)) {
is_msdebug_version = true;
size_t revision_pos = concise_version.find('-');
if (revision_pos != llvm::StringRef::npos)
concise_version = concise_version.take_front(revision_pos);
}
PrintLogo();
if (should_colorize)
llvm::outs() << kPinkStart;
if (is_msdebug_version)
llvm::outs() << "msdebug " << concise_version;
else
llvm::outs() << version_first_line;
if (should_colorize)
llvm::outs() << kColorReset;
llvm::outs() << '\n';
}
if (has_version_arg) {
m_option_data.m_print_version = true;
}
#endif
if (args.hasArg(OPT_no_use_colors)) {
m_debugger.SetUseColor(false);
WithColor::setAutoDetectFunction(disable_color);
}
if (args.hasArg(OPT_version)) {
m_option_data.m_print_version = true;
}
if (args.hasArg(OPT_python_path)) {
m_option_data.m_print_python_path = true;
}
if (args.hasArg(OPT_print_script_interpreter_info)) {
m_option_data.m_print_script_interpreter_info = true;
}
if (args.hasArg(OPT_batch)) {
m_option_data.m_batch = true;
}
if (auto *arg = args.getLastArg(OPT_core)) {
auto *arg_value = arg->getValue();
SBFileSpec file(arg_value);
if (!file.Exists()) {
error.SetErrorStringWithFormat(
"file specified in --core (-c) option doesn't exist: '%s'",
arg_value);
return error;
}
m_option_data.m_core_file = arg_value;
}
if (args.hasArg(OPT_editor)) {
m_option_data.m_use_external_editor = true;
}
if (args.hasArg(OPT_no_lldbinit)) {
m_debugger.SkipLLDBInitFiles(true);
m_debugger.SkipAppInitFiles(true);
}
if (args.hasArg(OPT_local_lldbinit)) {
lldb::SBDebugger::SetInternalVariable("target.load-cwd-lldbinit", "true",
m_debugger.GetInstanceName());
}
if (auto *arg = args.getLastArg(OPT_file)) {
auto *arg_value = arg->getValue();
SBFileSpec file(arg_value);
if (file.Exists()) {
m_option_data.m_args.emplace_back(arg_value);
} else if (file.ResolveExecutableLocation()) {
char path[PATH_MAX];
file.GetPath(path, sizeof(path));
m_option_data.m_args.emplace_back(path);
} else {
error.SetErrorStringWithFormat(
"file specified in --file (-f) option doesn't exist: '%s'",
arg_value);
return error;
}
}
if (auto *arg = args.getLastArg(OPT_arch)) {
auto *arg_value = arg->getValue();
if (!lldb::SBDebugger::SetDefaultArchitecture(arg_value)) {
error.SetErrorStringWithFormat(
"invalid architecture in the -a or --arch option: '%s'", arg_value);
return error;
}
}
if (auto *arg = args.getLastArg(OPT_script_language)) {
auto *arg_value = arg->getValue();
m_debugger.SetScriptLanguage(m_debugger.GetScriptingLanguage(arg_value));
}
if (args.hasArg(OPT_source_quietly)) {
m_option_data.m_source_quietly = true;
}
if (auto *arg = args.getLastArg(OPT_attach_name)) {
auto *arg_value = arg->getValue();
m_option_data.m_process_name = arg_value;
}
if (args.hasArg(OPT_wait_for)) {
m_option_data.m_wait_for = true;
}
if (auto *arg = args.getLastArg(OPT_attach_pid)) {
auto *arg_value = arg->getValue();
char *remainder;
m_option_data.m_process_pid = strtol(arg_value, &remainder, 0);
if (remainder == arg_value || *remainder != '\0') {
error.SetErrorStringWithFormat(
"Could not convert process PID: \"%s\" into a pid.", arg_value);
return error;
}
}
if (auto *arg = args.getLastArg(OPT_repl_language)) {
auto *arg_value = arg->getValue();
m_option_data.m_repl_lang =
SBLanguageRuntime::GetLanguageTypeFromString(arg_value);
if (m_option_data.m_repl_lang == eLanguageTypeUnknown) {
error.SetErrorStringWithFormat("Unrecognized language name: \"%s\"",
arg_value);
return error;
}
m_debugger.SetREPLLanguage(m_option_data.m_repl_lang);
}
if (args.hasArg(OPT_repl)) {
m_option_data.m_repl = true;
}
if (auto *arg = args.getLastArg(OPT_repl_)) {
m_option_data.m_repl = true;
if (auto *arg_value = arg->getValue())
m_option_data.m_repl_options = arg_value;
}
for (auto *arg : args.filtered(OPT_source_on_crash, OPT_one_line_on_crash,
OPT_source, OPT_source_before_file,
OPT_one_line, OPT_one_line_before_file)) {
auto *arg_value = arg->getValue();
if (arg->getOption().matches(OPT_source_on_crash)) {
m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash,
true, error);
if (error.Fail())
return error;
}
if (arg->getOption().matches(OPT_one_line_on_crash)) {
m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash,
false, error);
if (error.Fail())
return error;
}
if (arg->getOption().matches(OPT_source)) {
m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile,
true, error);
if (error.Fail())
return error;
}
if (arg->getOption().matches(OPT_source_before_file)) {
m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile,
true, error);
if (error.Fail())
return error;
}
if (arg->getOption().matches(OPT_one_line)) {
m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile,
false, error);
if (error.Fail())
return error;
}
if (arg->getOption().matches(OPT_one_line_before_file)) {
m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile,
false, error);
if (error.Fail())
return error;
}
}
if (m_option_data.m_process_name.empty() &&
m_option_data.m_process_pid == LLDB_INVALID_PROCESS_ID) {
for (auto *arg : args.filtered(OPT_INPUT))
m_option_data.m_args.push_back(arg->getAsString((args)));
if (auto *arg = args.getLastArgNoClaim(OPT_REM)) {
for (auto *value : arg->getValues())
m_option_data.m_args.emplace_back(value);
}
} else if (args.getLastArgNoClaim() != nullptr) {
WithColor::warning() << "program arguments are ignored when attaching.\n";
}
if (m_option_data.m_print_version) {
#ifdef MS_DEBUGGER
PrintLogo();
#endif
llvm::outs() << lldb::SBDebugger::GetVersionString() << '\n';
exiting = true;
return error;
}
if (m_option_data.m_print_python_path) {
SBFileSpec python_file_spec = SBHostOS::GetLLDBPythonPath();
if (python_file_spec.IsValid()) {
char python_path[PATH_MAX];
size_t num_chars = python_file_spec.GetPath(python_path, PATH_MAX);
if (num_chars < PATH_MAX) {
llvm::outs() << python_path << '\n';
} else
llvm::outs() << "<PATH TOO LONG>\n";
} else
llvm::outs() << "<COULD NOT FIND PATH>\n";
exiting = true;
return error;
}
if (m_option_data.m_print_script_interpreter_info) {
SBStructuredData info =
m_debugger.GetScriptInterpreterInfo(m_debugger.GetScriptLanguage());
if (!info) {
error.SetErrorString("no script interpreter.");
} else {
SBStream stream;
error = info.GetAsJSON(stream);
if (error.Success()) {
llvm::outs() << stream.GetData() << '\n';
}
}
exiting = true;
return error;
}
return error;
}
std::string EscapeString(std::string arg) {
std::string::size_type pos = 0;
while ((pos = arg.find_first_of("\"\\", pos)) != std::string::npos) {
arg.insert(pos, 1, '\\');
pos += 2;
}
return '"' + arg + '"';
}
int Driver::MainLoop() {
if (::tcgetattr(STDIN_FILENO, &g_old_stdin_termios) == 0) {
g_old_stdin_termios_is_valid = true;
atexit(reset_stdin_termios);
}
#ifndef _MSC_VER
::setbuf(stdin, nullptr);
#endif
::setbuf(stdout, nullptr);
m_debugger.SetErrorFileHandle(stderr, false);
m_debugger.SetOutputFileHandle(stdout, false);
m_debugger.SetInputFileHandle(stdin, false);
m_debugger.SetUseExternalEditor(m_option_data.m_use_external_editor);
struct winsize window_size;
if ((isatty(STDIN_FILENO) != 0) &&
::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) {
if (window_size.ws_col > 0)
m_debugger.SetTerminalWidth(window_size.ws_col);
}
SBCommandInterpreter sb_interpreter = m_debugger.GetCommandInterpreter();
SBCommandReturnObject result;
sb_interpreter.SourceInitFileInGlobalDirectory(result);
sb_interpreter.SourceInitFileInHomeDirectory(result, m_option_data.m_repl);
sb_interpreter.SourceInitFileInCurrentWorkingDirectory(result);
result.PutError(m_debugger.GetErrorFile());
result.PutOutput(m_debugger.GetOutputFile());
m_debugger.GetCommandInterpreter().AllowExitCodeOnQuit(true);
SBStream commands_stream;
WriteCommandsForSourcing(eCommandPlacementBeforeFile, commands_stream);
if (!m_option_data.m_repl) {
const size_t num_args = m_option_data.m_args.size();
if (num_args > 0) {
char arch_name[64];
if (lldb::SBDebugger::GetDefaultArchitecture(arch_name,
sizeof(arch_name)))
commands_stream.Printf("target create --arch=%s %s", arch_name,
EscapeString(m_option_data.m_args[0]).c_str());
else
commands_stream.Printf("target create %s",
EscapeString(m_option_data.m_args[0]).c_str());
if (!m_option_data.m_core_file.empty()) {
commands_stream.Printf(" --core %s",
EscapeString(m_option_data.m_core_file).c_str());
}
commands_stream.Printf("\n");
if (num_args > 1) {
commands_stream.Printf("settings set -- target.run-args ");
for (size_t arg_idx = 1; arg_idx < num_args; ++arg_idx)
commands_stream.Printf(
" %s", EscapeString(m_option_data.m_args[arg_idx]).c_str());
commands_stream.Printf("\n");
}
} else if (!m_option_data.m_core_file.empty()) {
commands_stream.Printf("target create --core %s\n",
EscapeString(m_option_data.m_core_file).c_str());
} else if (!m_option_data.m_process_name.empty()) {
commands_stream.Printf(
"process attach --name %s",
EscapeString(m_option_data.m_process_name).c_str());
if (m_option_data.m_wait_for)
commands_stream.Printf(" --waitfor");
commands_stream.Printf("\n");
} else if (LLDB_INVALID_PROCESS_ID != m_option_data.m_process_pid) {
commands_stream.Printf("process attach --pid %" PRIu64 "\n",
m_option_data.m_process_pid);
}
WriteCommandsForSourcing(eCommandPlacementAfterFile, commands_stream);
} else if (!m_option_data.m_after_file_commands.empty()) {
WithColor::warning() << "commands specified to run after file load (via -o "
"or -s) are ignored in REPL mode.\n";
}
const bool handle_events = true;
const bool spawn_thread = false;
bool go_interactive = true;
if ((commands_stream.GetData() != nullptr) &&
(commands_stream.GetSize() != 0u)) {
SBError error = m_debugger.SetInputString(commands_stream.GetData());
if (error.Fail()) {
WithColor::error() << error.GetCString() << '\n';
return 1;
}
bool old_async = m_debugger.GetAsync();
m_debugger.SetAsync(false);
SBCommandInterpreterRunOptions options;
options.SetAutoHandleEvents(true);
options.SetSpawnThread(false);
options.SetStopOnError(true);
options.SetStopOnCrash(m_option_data.m_batch);
options.SetEchoCommands(!m_option_data.m_source_quietly);
SBCommandInterpreterRunResult results =
m_debugger.RunCommandInterpreter(options);
if (results.GetResult() == lldb::eCommandInterpreterResultQuitRequested)
go_interactive = false;
if (m_option_data.m_batch &&
results.GetResult() != lldb::eCommandInterpreterResultInferiorCrash)
go_interactive = false;
if (m_option_data.m_batch &&
results.GetResult() == lldb::eCommandInterpreterResultCommandError)
return 1;
if (m_option_data.m_batch &&
results.GetResult() == lldb::eCommandInterpreterResultInferiorCrash &&
!m_option_data.m_after_crash_commands.empty()) {
SBStream crash_commands_stream;
WriteCommandsForSourcing(eCommandPlacementAfterCrash,
crash_commands_stream);
SBError error =
m_debugger.SetInputString(crash_commands_stream.GetData());
if (error.Success()) {
SBCommandInterpreterRunResult local_results =
m_debugger.RunCommandInterpreter(options);
if (local_results.GetResult() ==
lldb::eCommandInterpreterResultQuitRequested)
go_interactive = false;
if (m_option_data.m_batch &&
local_results.GetResult() ==
lldb::eCommandInterpreterResultCommandError)
return 1;
}
}
m_debugger.SetAsync(old_async);
}
if (go_interactive) {
m_debugger.SetInputFileHandle(stdin, true);
if (m_option_data.m_repl) {
const char *repl_options = nullptr;
if (!m_option_data.m_repl_options.empty())
repl_options = m_option_data.m_repl_options.c_str();
SBError error(
m_debugger.RunREPL(m_option_data.m_repl_lang, repl_options));
if (error.Fail()) {
const char *error_cstr = error.GetCString();
if ((error_cstr != nullptr) && (error_cstr[0] != 0))
WithColor::error() << error_cstr << '\n';
else
WithColor::error() << error.GetError() << '\n';
}
} else {
m_debugger.RunCommandInterpreter(handle_events, spawn_thread);
}
}
reset_stdin_termios();
fclose(stdin);
return sb_interpreter.GetQuitStatus();
}
void Driver::ResizeWindow(unsigned short col) {
GetDebugger().SetTerminalWidth(col);
}
void sigwinch_handler(int signo) {
struct winsize window_size;
if ((isatty(STDIN_FILENO) != 0) &&
::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) {
if ((window_size.ws_col > 0) && g_driver != nullptr) {
g_driver->ResizeWindow(window_size.ws_col);
}
}
}
void sigint_handler(int signo) {
#ifdef _WIN32
signal(SIGINT, sigint_handler);
#endif
static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT;
if (g_driver != nullptr) {
if (!g_interrupt_sent.test_and_set()) {
g_driver->GetDebugger().DispatchInputInterrupt();
g_interrupt_sent.clear();
return;
}
}
_exit(signo);
}
#ifndef _WIN32
static void sigtstp_handler(int signo) {
if (g_driver != nullptr)
g_driver->GetDebugger().SaveInputTerminalState();
sigset_t set;
sigemptyset(&set);
sigaddset(&set, signo);
pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
signal(signo, SIG_DFL);
raise(signo);
pthread_sigmask(SIG_BLOCK, &set, nullptr);
signal(signo, sigtstp_handler);
if (g_driver != nullptr)
g_driver->GetDebugger().RestoreInputTerminalState();
}
#endif
#ifdef MS_DEBUGGER
static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) {
std::string usage_str = tool_name.str() + " [options]";
table.printHelp(llvm::outs(), usage_str.c_str(), "", false);
std::string examples = R"___(
EXAMPLES:
The debugger can be started in several modes.
Passing an executable as a positional argument prepares msdebug to debug the
given executable. To disambiguate between arguments passed to msdebug and
arguments passed to the debugged executable, arguments starting with a - must
be passed after --.
msdebug --arch x86_64 /path/to/program program argument -- --arch armv7
For convenience, passing the executable after -- is also supported.
msdebug --arch x86_64 -- /path/to/program program argument --arch armv7
Passing one of the attach options causes msdebug to immediately attach to the
given process.
msdebug -p <pid>
msdebug -n <process-name>
Passing --repl starts msdebug in REPL mode.
msdebug -r
Passing --core causes msdebug to debug the core file.
msdebug -c /path/to/core
Command options can be combined with these modes and cause msdebug to run the
specified commands before or after events, like loading the file or crashing,
in the order provided on the command line.
msdebug -O 'settings set stop-disassembly-count 20' -o 'run' -o 'bt'
msdebug -S /source/before/file -s /source/after/file
msdebug -K /source/before/crash -k /source/after/crash
Note: In REPL mode no file is loaded, so commands specified to run after
loading the file (via -o or -s) will be ignored.)___";
llvm::outs() << examples << '\n';
}
#else
static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) {
std::string usage_str = tool_name.str() + " [options]";
table.printHelp(llvm::outs(), usage_str.c_str(), "LLDB", false);
std::string examples = R"___(
EXAMPLES:
The debugger can be started in several modes.
Passing an executable as a positional argument prepares lldb to debug the
given executable. To disambiguate between arguments passed to lldb and
arguments passed to the debugged executable, arguments starting with a - must
be passed after --.
lldb --arch x86_64 /path/to/program program argument -- --arch armv7
For convenience, passing the executable after -- is also supported.
lldb --arch x86_64 -- /path/to/program program argument --arch armv7
Passing one of the attach options causes lldb to immediately attach to the
given process.
lldb -p <pid>
lldb -n <process-name>
Passing --repl starts lldb in REPL mode.
lldb -r
Passing --core causes lldb to debug the core file.
lldb -c /path/to/core
Command options can be combined with these modes and cause lldb to run the
specified commands before or after events, like loading the file or crashing,
in the order provided on the command line.
lldb -O 'settings set stop-disassembly-count 20' -o 'run' -o 'bt'
lldb -S /source/before/file -s /source/after/file
lldb -K /source/before/crash -k /source/after/crash
Note: In REPL mode no file is loaded, so commands specified to run after
loading the file (via -o or -s) will be ignored.)___";
llvm::outs() << examples << '\n';
}
#endif
int main(int argc, char const *argv[]) {
std::setlocale(LC_ALL, "");
std::setlocale(LC_CTYPE, "");
llvm::InitLLVM IL(argc, argv, false);
#if !defined(__APPLE__)
llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL
" and include the crash backtrace.\n");
#else
llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL
" and include the crash report from "
"~/Library/Logs/DiagnosticReports/.\n");
#endif
LLDBOptTable T;
unsigned MissingArgIndex;
unsigned MissingArgCount;
ArrayRef<const char *> arg_arr = ArrayRef(argv + 1, argc - 1);
opt::InputArgList input_args =
T.ParseArgs(arg_arr, MissingArgIndex, MissingArgCount);
llvm::StringRef argv0 = llvm::sys::path::filename(argv[0]);
if (input_args.hasArg(OPT_help)) {
printHelp(T, argv0);
return 0;
}
if (MissingArgCount) {
WithColor::error() << "argument to '"
<< input_args.getArgString(MissingArgIndex)
<< "' is missing\n";
}
if (input_args.hasArg(OPT_UNKNOWN)) {
for (auto *arg : input_args.filtered(OPT_UNKNOWN)) {
WithColor::error() << "unknown option: " << arg->getSpelling() << '\n';
}
}
if (MissingArgCount || input_args.hasArg(OPT_UNKNOWN)) {
llvm::errs() << "Use '" << argv0
<< " --help' for a complete list of options.\n";
return 1;
}
SBError error = SBDebugger::InitializeWithErrorHandling();
if (error.Fail()) {
WithColor::error() << "initialization failed: " << error.GetCString()
<< '\n';
return 1;
}
SBDebugger::PrintDiagnosticsOnError();
signal(SIGINT, sigint_handler);
#if !defined(_WIN32)
signal(SIGPIPE, SIG_IGN);
signal(SIGWINCH, sigwinch_handler);
signal(SIGTSTP, sigtstp_handler);
#endif
int exit_code = 0;
{
Driver driver;
bool exiting = false;
SBError error(driver.ProcessArgs(input_args, exiting));
if (error.Fail()) {
exit_code = 1;
if (const char *error_cstr = error.GetCString())
WithColor::error() << error_cstr << '\n';
} else if (!exiting) {
exit_code = driver.MainLoop();
}
}
{
std::future<void> future =
std::async(std::launch::async, []() { SBDebugger::Terminate(); });
if (future.wait_for(std::chrono::seconds(1)) == std::future_status::timeout)
fprintf(stderr, "Waiting for background tasks to complete...\n");
future.wait();
}
return exit_code;
}