summaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/tools/driver/Driver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/tools/driver/Driver.cpp')
-rw-r--r--contrib/llvm-project/lldb/tools/driver/Driver.cpp895
1 files changed, 895 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/tools/driver/Driver.cpp b/contrib/llvm-project/lldb/tools/driver/Driver.cpp
new file mode 100644
index 000000000000..043aba7b07ba
--- /dev/null
+++ b/contrib/llvm-project/lldb/tools/driver/Driver.cpp
@@ -0,0 +1,895 @@
+//===-- Driver.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Driver.h"
+
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBHostOS.h"
+#include "lldb/API/SBLanguageRuntime.h"
+#include "lldb/API/SBReproducer.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBStringList.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <algorithm>
+#include <atomic>
+#include <bitset>
+#include <csignal>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Includes for pipe()
+#if defined(_WIN32)
+#include <fcntl.h>
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+#if !defined(__APPLE__)
+#include "llvm/Support/DataTypes.h"
+#endif
+
+using namespace lldb;
+using namespace llvm;
+
+namespace {
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Options.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Options.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Options.inc"
+#undef OPTION
+};
+
+class LLDBOptTable : public opt::OptTable {
+public:
+ LLDBOptTable() : OptTable(InfoTable) {}
+};
+} // namespace
+
+static void reset_stdin_termios();
+static bool g_old_stdin_termios_is_valid = false;
+static struct termios g_old_stdin_termios;
+
+static Driver *g_driver = nullptr;
+
+// In the Driver::MainLoop, we change the terminal settings. This function is
+// added as an atexit handler to make sure we clean them up.
+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)) {
+ // We want to be able to handle CTRL+D in the terminal to have it terminate
+ // certain input
+ m_debugger.SetCloseInputOnEOF(false);
+ g_driver = this;
+}
+
+Driver::~Driver() { 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);
+ }
+}
+
+// Check the arguments that were passed to this program to make sure they are
+// valid and to get their argument values (if any). Return a boolean value
+// indicating whether or not to start up the full debugger (i.e. the Command
+// Interpreter) or not. Return FALSE if the arguments were invalid OR if the
+// user only wanted help or version information.
+SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) {
+ SBError error;
+
+ // This is kind of a pain, but since we make the debugger in the Driver's
+ // constructor, we can't know at that point whether we should read in init
+ // files yet. So we don't read them in in the Driver constructor, then set
+ // the flags back to "read them in" here, and then if we see the "-n" flag,
+ // we'll turn it off again. Finally we have to read them in by hand later in
+ // the main loop.
+ m_debugger.SkipLLDBInitFiles(false);
+ m_debugger.SkipAppInitFiles(false);
+
+ 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_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 (args.hasArg(OPT_no_use_colors)) {
+ m_debugger.SetUseColor(false);
+ }
+
+ 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_no_use_colors)) {
+ m_option_data.m_debug_mode = true;
+ }
+
+ if (args.hasArg(OPT_no_use_colors)) {
+ m_debugger.SetUseColor(false);
+ }
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+
+ // We need to process the options below together as their relative order
+ // matters.
+ 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) {
+
+ // If the option data args array is empty that means the file was not
+ // specified with -f and we need to get it from the input args.
+ if (m_option_data.m_args.empty()) {
+ if (auto *arg = args.getLastArgNoClaim(OPT_INPUT)) {
+ m_option_data.m_args.push_back(arg->getAsString((args)));
+ }
+ }
+
+ // Any argument following -- is an argument for the inferior.
+ 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) {
+ 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;
+ }
+
+ return error;
+}
+
+static inline int OpenPipe(int fds[2], std::size_t size) {
+#ifdef _WIN32
+ return _pipe(fds, size, O_BINARY);
+#else
+ (void)size;
+ return pipe(fds);
+#endif
+}
+
+static ::FILE *PrepareCommandsForSourcing(const char *commands_data,
+ size_t commands_size) {
+ enum PIPES { READ, WRITE }; // Indexes for the read and write fds
+ int fds[2] = {-1, -1};
+
+ if (OpenPipe(fds, commands_size) != 0) {
+ WithColor::error()
+ << "can't create pipe file descriptors for LLDB commands\n";
+ return nullptr;
+ }
+
+ ssize_t nrwr = write(fds[WRITE], commands_data, commands_size);
+ if (size_t(nrwr) != commands_size) {
+ WithColor::error()
+ << format(
+ "write(%i, %p, %" PRIu64
+ ") failed (errno = %i) when trying to open LLDB commands pipe",
+ fds[WRITE], static_cast<const void *>(commands_data),
+ static_cast<uint64_t>(commands_size), errno)
+ << '\n';
+ llvm::sys::Process::SafelyCloseFileDescriptor(fds[READ]);
+ llvm::sys::Process::SafelyCloseFileDescriptor(fds[WRITE]);
+ return nullptr;
+ }
+
+ // Close the write end of the pipe, so that the command interpreter will exit
+ // when it consumes all the data.
+ llvm::sys::Process::SafelyCloseFileDescriptor(fds[WRITE]);
+
+ // Open the read file descriptor as a FILE * that we can return as an input
+ // handle.
+ ::FILE *commands_file = fdopen(fds[READ], "rb");
+ if (commands_file == nullptr) {
+ WithColor::error() << format("fdopen(%i, \"rb\") failed (errno = %i) "
+ "when trying to open LLDB commands pipe",
+ fds[READ], errno)
+ << '\n';
+ llvm::sys::Process::SafelyCloseFileDescriptor(fds[READ]);
+ return nullptr;
+ }
+
+ // 'commands_file' now owns the read descriptor.
+ return commands_file;
+}
+
+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
+ // Disabling stdin buffering with MSVC's 2015 CRT exposes a bug in fgets
+ // which causes it to miss newlines depending on whether there have been an
+ // odd or even number of characters. Bug has been reported to MS via Connect.
+ ::setbuf(stdin, nullptr);
+#endif
+ ::setbuf(stdout, nullptr);
+
+ m_debugger.SetErrorFileHandle(stderr, false);
+ m_debugger.SetOutputFileHandle(stdout, false);
+ // Don't take ownership of STDIN yet...
+ 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();
+
+ // Before we handle any options from the command line, we parse the
+ // .lldbinit file in the user's home directory.
+ SBCommandReturnObject result;
+ sb_interpreter.SourceInitFileInHomeDirectory(result);
+ if (m_option_data.m_debug_mode) {
+ result.PutError(m_debugger.GetErrorFileHandle());
+ result.PutOutput(m_debugger.GetOutputFileHandle());
+ }
+
+ // Source the local .lldbinit file if it exists and we're allowed to source.
+ // Here we want to always print the return object because it contains the
+ // warning and instructions to load local lldbinit files.
+ sb_interpreter.SourceInitFileInCurrentWorkingDirectory(result);
+ result.PutError(m_debugger.GetErrorFileHandle());
+ result.PutOutput(m_debugger.GetOutputFileHandle());
+
+ // We allow the user to specify an exit code when calling quit which we will
+ // return when exiting.
+ m_debugger.GetCommandInterpreter().AllowExitCodeOnQuit(true);
+
+ // Now we handle options we got from the command line
+ SBStream commands_stream;
+
+ // First source in the commands specified to be run before the file arguments
+ // are processed.
+ WriteCommandsForSourcing(eCommandPlacementBeforeFile, commands_stream);
+
+ // If we're not in --repl mode, add the commands to process the file
+ // arguments, and the commands specified to run afterwards.
+ 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()) {
+ // We're in repl mode and after-file-load commands were specified.
+ WithColor::warning() << "commands specified to run after file load (via -o "
+ "or -s) are ignored in REPL mode.\n";
+ }
+
+ if (m_option_data.m_debug_mode) {
+ result.PutError(m_debugger.GetErrorFileHandle());
+ result.PutOutput(m_debugger.GetOutputFileHandle());
+ }
+
+ const bool handle_events = true;
+ const bool spawn_thread = false;
+
+ // Check if we have any data in the commands stream, and if so, save it to a
+ // temp file
+ // so we can then run the command interpreter using the file contents.
+ const char *commands_data = commands_stream.GetData();
+ const size_t commands_size = commands_stream.GetSize();
+
+ // The command file might have requested that we quit, this variable will
+ // track that.
+ bool quit_requested = false;
+ bool stopped_for_crash = false;
+ if ((commands_data != nullptr) && (commands_size != 0u)) {
+ bool success = true;
+ FILE *commands_file =
+ PrepareCommandsForSourcing(commands_data, commands_size);
+ if (commands_file != nullptr) {
+ m_debugger.SetInputFileHandle(commands_file, true);
+
+ // Set the debugger into Sync mode when running the command file.
+ // Otherwise command files
+ // that run the target won't run in a sensible way.
+ bool old_async = m_debugger.GetAsync();
+ m_debugger.SetAsync(false);
+ int num_errors = 0;
+
+ SBCommandInterpreterRunOptions options;
+ options.SetStopOnError(true);
+ if (m_option_data.m_batch)
+ options.SetStopOnCrash(true);
+
+ m_debugger.RunCommandInterpreter(handle_events, spawn_thread, options,
+ num_errors, quit_requested,
+ stopped_for_crash);
+
+ if (m_option_data.m_batch && stopped_for_crash &&
+ !m_option_data.m_after_crash_commands.empty()) {
+ SBStream crash_commands_stream;
+ WriteCommandsForSourcing(eCommandPlacementAfterCrash,
+ crash_commands_stream);
+ const char *crash_commands_data = crash_commands_stream.GetData();
+ const size_t crash_commands_size = crash_commands_stream.GetSize();
+ commands_file = PrepareCommandsForSourcing(crash_commands_data,
+ crash_commands_size);
+ if (commands_file != nullptr) {
+ bool local_quit_requested;
+ bool local_stopped_for_crash;
+ m_debugger.SetInputFileHandle(commands_file, true);
+
+ m_debugger.RunCommandInterpreter(handle_events, spawn_thread, options,
+ num_errors, local_quit_requested,
+ local_stopped_for_crash);
+ if (local_quit_requested)
+ quit_requested = true;
+ }
+ }
+ m_debugger.SetAsync(old_async);
+ } else
+ success = false;
+
+ // Something went wrong with command pipe
+ if (!success) {
+ exit(1);
+ }
+ }
+
+ // Now set the input file handle to STDIN and run the command
+ // interpreter again in interactive mode or repl mode and let the debugger
+ // take ownership of stdin
+
+ bool go_interactive = true;
+ if (quit_requested)
+ go_interactive = false;
+ else if (m_option_data.m_batch && !stopped_for_crash)
+ go_interactive = false;
+
+ 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);
+
+ int exit_code = sb_interpreter.GetQuitStatus();
+ SBDebugger::Destroy(m_debugger);
+ return exit_code;
+}
+
+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 // Restore handler as it is not persistent on Windows
+ 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);
+}
+
+void sigtstp_handler(int signo) {
+ if (g_driver != nullptr)
+ g_driver->GetDebugger().SaveInputTerminalState();
+
+ signal(signo, SIG_DFL);
+ kill(getpid(), signo);
+ signal(signo, sigtstp_handler);
+}
+
+void sigcont_handler(int signo) {
+ if (g_driver != nullptr)
+ g_driver->GetDebugger().RestoreInputTerminalState();
+
+ signal(signo, SIG_DFL);
+ kill(getpid(), signo);
+ signal(signo, sigcont_handler);
+}
+
+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. Arguments passed after -- are considered arguments to the
+ debugged executable.
+
+ lldb --arch x86_64 /path/to/program -- --arch arvm7
+
+ 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;
+}
+
+llvm::Optional<int> InitializeReproducer(opt::InputArgList &input_args) {
+ if (auto *replay_path = input_args.getLastArg(OPT_replay)) {
+ if (const char *error = SBReproducer::Replay(replay_path->getValue())) {
+ WithColor::error() << "reproducer replay failed: " << error << '\n';
+ return 1;
+ }
+ return 0;
+ }
+
+ bool capture = input_args.hasArg(OPT_capture);
+ auto *capture_path = input_args.getLastArg(OPT_capture_path);
+
+ if (capture || capture_path) {
+ if (capture_path) {
+ if (!capture)
+ WithColor::warning() << "-capture-path specified without -capture\n";
+ if (const char *error = SBReproducer::Capture(capture_path->getValue())) {
+ WithColor::error() << "reproducer capture failed: " << error << '\n';
+ return 1;
+ }
+ } else {
+ const char *error = SBReproducer::Capture();
+ if (error) {
+ WithColor::error() << "reproducer capture failed: " << error << '\n';
+ return 1;
+ }
+ }
+ }
+
+ return llvm::None;
+}
+
+int
+#ifdef _MSC_VER
+wmain(int argc, wchar_t const *wargv[])
+#else
+main(int argc, char const *argv[])
+#endif
+{
+#ifdef _MSC_VER
+ // Convert wide arguments to UTF-8
+ std::vector<std::string> argvStrings(argc);
+ std::vector<const char *> argvPointers(argc);
+ for (int i = 0; i != argc; ++i) {
+ llvm::convertWideToUTF8(wargv[i], argvStrings[i]);
+ argvPointers[i] = argvStrings[i].c_str();
+ }
+ const char **argv = argvPointers.data();
+#endif
+
+ // Print stack trace on crash.
+ llvm::StringRef ToolName = llvm::sys::path::filename(argv[0]);
+ llvm::sys::PrintStackTraceOnErrorSignal(ToolName);
+ llvm::PrettyStackTraceProgram X(argc, argv);
+
+ // Parse arguments.
+ LLDBOptTable T;
+ unsigned MAI;
+ unsigned MAC;
+ ArrayRef<const char *> arg_arr = makeArrayRef(argv + 1, argc - 1);
+ opt::InputArgList input_args = T.ParseArgs(arg_arr, MAI, MAC);
+
+ if (input_args.hasArg(OPT_help)) {
+ printHelp(T, ToolName);
+ return 0;
+ }
+
+ for (auto *arg : input_args.filtered(OPT_UNKNOWN)) {
+ WithColor::warning() << "ignoring unknown option: " << arg->getSpelling()
+ << '\n';
+ }
+
+ if (auto exit_code = InitializeReproducer(input_args)) {
+ return *exit_code;
+ }
+
+ SBError error = SBDebugger::InitializeWithErrorHandling();
+ if (error.Fail()) {
+ WithColor::error() << "initialization failed: " << error.GetCString()
+ << '\n';
+ return 1;
+ }
+ SBHostOS::ThreadCreated("<lldb.driver.main-thread>");
+
+ signal(SIGINT, sigint_handler);
+#if !defined(_MSC_VER)
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGWINCH, sigwinch_handler);
+ signal(SIGTSTP, sigtstp_handler);
+ signal(SIGCONT, sigcont_handler);
+#endif
+
+ int exit_code = 0;
+ // Create a scope for driver so that the driver object will destroy itself
+ // before SBDebugger::Terminate() is called.
+ {
+ 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();
+ }
+ }
+
+ SBDebugger::Terminate();
+ return exit_code;
+}