//===-- Driver.cpp ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "Driver.h" #include #include #include #include #include #include #include #include #include // Includes for pipe() #if defined(_WIN32) #include #include #else #include #endif #include #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBCommunication.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBHostOS.h" #include "lldb/API/SBLanguageRuntime.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" #include "lldb/API/SBTarget.h" #include "lldb/API/SBThread.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include #include #if !defined(__APPLE__) #include "llvm/Support/DataTypes.h" #endif using namespace lldb; 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 = NULL; // 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); } } typedef struct { uint32_t usage_mask; // Used to mark options that can be used together. If (1 // << n & usage_mask) != 0 // then this option belongs to option set n. bool required; // This option is required (in the current usage level) const char *long_option; // Full name for this option. int short_option; // Single character for this option. int option_has_arg; // no_argument, required_argument or optional_argument uint32_t completion_type; // Cookie the option class can use to do define the // argument completion. lldb::CommandArgumentType argument_type; // Type of argument this option takes const char *usage_text; // Full text explaining what this options does and // what (if any) argument to // pass it. } OptionDefinition; #define LLDB_3_TO_5 LLDB_OPT_SET_3 | LLDB_OPT_SET_4 | LLDB_OPT_SET_5 #define LLDB_4_TO_5 LLDB_OPT_SET_4 | LLDB_OPT_SET_5 static constexpr OptionDefinition g_options[] = { {LLDB_OPT_SET_1, true, "help", 'h', no_argument, 0, eArgTypeNone, "Prints out the usage information for the LLDB debugger."}, {LLDB_OPT_SET_2, true, "version", 'v', no_argument, 0, eArgTypeNone, "Prints out the current version number of the LLDB debugger."}, {LLDB_OPT_SET_3, true, "arch", 'a', required_argument, 0, eArgTypeArchitecture, "Tells the debugger to use the specified architecture when starting and " "running the program. must " "be one of the architectures for which the program was compiled."}, {LLDB_OPT_SET_3, true, "file", 'f', required_argument, 0, eArgTypeFilename, "Tells the debugger to use the file as the program to be " "debugged."}, {LLDB_OPT_SET_3, false, "core", 'c', required_argument, 0, eArgTypeFilename, "Tells the debugger to use the fullpath to as the core file."}, {LLDB_OPT_SET_5, true, "attach-pid", 'p', required_argument, 0, eArgTypePid, "Tells the debugger to attach to a process with the given pid."}, {LLDB_OPT_SET_4, true, "attach-name", 'n', required_argument, 0, eArgTypeProcessName, "Tells the debugger to attach to a process with the given name."}, {LLDB_OPT_SET_4, true, "wait-for", 'w', no_argument, 0, eArgTypeNone, "Tells the debugger to wait for a process with the given pid or name to " "launch before attaching."}, {LLDB_3_TO_5, false, "source", 's', required_argument, 0, eArgTypeFilename, "Tells the debugger to read in and execute the lldb commands in the given " "file, after any file provided on the command line has been loaded."}, {LLDB_3_TO_5, false, "one-line", 'o', required_argument, 0, eArgTypeNone, "Tells the debugger to execute this one-line lldb command after any file " "provided on the command line has been loaded."}, {LLDB_3_TO_5, false, "source-before-file", 'S', required_argument, 0, eArgTypeFilename, "Tells the debugger to read in and execute the lldb " "commands in the given file, before any file provided " "on the command line has been loaded."}, {LLDB_3_TO_5, false, "one-line-before-file", 'O', required_argument, 0, eArgTypeNone, "Tells the debugger to execute this one-line lldb command " "before any file provided on the command line has been " "loaded."}, {LLDB_3_TO_5, false, "one-line-on-crash", 'k', required_argument, 0, eArgTypeNone, "When in batch mode, tells the debugger to execute this " "one-line lldb command if the target crashes."}, {LLDB_3_TO_5, false, "source-on-crash", 'K', required_argument, 0, eArgTypeFilename, "When in batch mode, tells the debugger to source this " "file of lldb commands if the target crashes."}, {LLDB_3_TO_5, false, "source-quietly", 'Q', no_argument, 0, eArgTypeNone, "Tells the debugger to execute this one-line lldb command before any file " "provided on the command line has been loaded."}, {LLDB_3_TO_5, false, "batch", 'b', no_argument, 0, eArgTypeNone, "Tells the debugger to run the commands from -s, -S, -o & -O, and " "then quit. However if any run command stopped due to a signal or crash, " "the debugger will return to the interactive prompt at the place of the " "crash."}, {LLDB_3_TO_5, false, "editor", 'e', no_argument, 0, eArgTypeNone, "Tells the debugger to open source files using the host's \"external " "editor\" mechanism."}, {LLDB_3_TO_5, false, "no-lldbinit", 'x', no_argument, 0, eArgTypeNone, "Do not automatically parse any '.lldbinit' files."}, {LLDB_3_TO_5, false, "no-use-colors", 'X', no_argument, 0, eArgTypeNone, "Do not use colors."}, {LLDB_OPT_SET_6, true, "python-path", 'P', no_argument, 0, eArgTypeNone, "Prints out the path to the lldb.py file for this version of lldb."}, {LLDB_3_TO_5, false, "script-language", 'l', required_argument, 0, eArgTypeScriptLang, "Tells the debugger to use the specified scripting language for " "user-defined scripts, rather than the default. " "Valid scripting languages that can be specified include Python, Perl, " "Ruby and Tcl. Currently only the Python " "extensions have been implemented."}, {LLDB_3_TO_5, false, "debug", 'd', no_argument, 0, eArgTypeNone, "Tells the debugger to print out extra information for debugging itself."}, {LLDB_OPT_SET_7, true, "repl", 'r', optional_argument, 0, eArgTypeNone, "Runs lldb in REPL mode with a stub process."}, {LLDB_OPT_SET_7, true, "repl-language", 'R', required_argument, 0, eArgTypeNone, "Chooses the language for the REPL."}}; static constexpr auto g_num_options = sizeof(g_options)/sizeof(OptionDefinition); static const uint32_t last_option_set_with_args = 2; Driver::Driver() : SBBroadcaster("Driver"), m_debugger(SBDebugger::Create(false)), m_option_data() { // 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 = NULL; } // This function takes INDENT, which tells how many spaces to output at the // front // of each line; TEXT, which is the text that is to be output. It outputs the // text, on multiple lines if necessary, to RESULT, with INDENT spaces at the // front of each line. It breaks lines on spaces, tabs or newlines, shortening // the line if necessary to not break in the middle of a word. It assumes that // each output line should contain a maximum of OUTPUT_MAX_COLUMNS characters. void OutputFormattedUsageText(FILE *out, int indent, const char *text, int output_max_columns) { int len = strlen(text); std::string text_string(text); // Force indentation to be reasonable. if (indent >= output_max_columns) indent = 0; // Will it all fit on one line? if (len + indent < output_max_columns) // Output as a single line fprintf(out, "%*s%s\n", indent, "", text); else { // We need to break it up into multiple lines. int text_width = output_max_columns - indent - 1; int start = 0; int end = start; int final_end = len; int sub_len; while (end < final_end) { // Dont start the 'text' on a space, since we're already outputting the // indentation. while ((start < final_end) && (text[start] == ' ')) start++; end = start + text_width; if (end > final_end) end = final_end; else { // If we're not at the end of the text, make sure we break the line on // white space. while (end > start && text[end] != ' ' && text[end] != '\t' && text[end] != '\n') end--; } sub_len = end - start; std::string substring = text_string.substr(start, sub_len); fprintf(out, "%*s%s\n", indent, "", substring.c_str()); start = end + 1; } } } static void ShowUsage(FILE *out, Driver::OptionData data) { uint32_t screen_width = 80; uint32_t indent_level = 0; const char *name = "lldb"; fprintf(out, "\nUsage:\n\n"); indent_level += 2; // First, show each usage level set of options, e.g. // [options-for-level-0] // // [options-for-level-1] // etc. uint32_t num_option_sets = 0; for (const auto &opt : g_options) { uint32_t this_usage_mask = opt.usage_mask; if (this_usage_mask == LLDB_OPT_SET_ALL) { if (num_option_sets == 0) num_option_sets = 1; } else { for (uint32_t j = 0; j < LLDB_MAX_NUM_OPTION_SETS; j++) { if (this_usage_mask & 1 << j) { if (num_option_sets <= j) num_option_sets = j + 1; } } } } for (uint32_t opt_set = 0; opt_set < num_option_sets; opt_set++) { uint32_t opt_set_mask; opt_set_mask = 1 << opt_set; if (opt_set > 0) fprintf(out, "\n"); fprintf(out, "%*s%s", indent_level, "", name); bool is_help_line = false; for (const auto &opt : g_options) { if (opt.usage_mask & opt_set_mask) { CommandArgumentType arg_type = opt.argument_type; const char *arg_name = SBCommandInterpreter::GetArgumentTypeAsCString(arg_type); // This is a bit of a hack, but there's no way to say certain options // don't have arguments yet... // so we do it by hand here. if (opt.short_option == 'h') is_help_line = true; if (opt.required) { if (opt.option_has_arg == required_argument) fprintf(out, " -%c <%s>", opt.short_option, arg_name); else if (opt.option_has_arg == optional_argument) fprintf(out, " -%c [<%s>]", opt.short_option, arg_name); else fprintf(out, " -%c", opt.short_option); } else { if (opt.option_has_arg == required_argument) fprintf(out, " [-%c <%s>]", opt.short_option, arg_name); else if (opt.option_has_arg == optional_argument) fprintf(out, " [-%c [<%s>]]", opt.short_option, arg_name); else fprintf(out, " [-%c]", opt.short_option); } } } if (!is_help_line && (opt_set <= last_option_set_with_args)) fprintf(out, " [[--] [ ...]]"); } fprintf(out, "\n\n"); // Now print out all the detailed information about the various options: long // form, short form and help text: // -- long_name // - short // help text // This variable is used to keep track of which options' info we've printed // out, because some options can be in // more than one usage level, but we only want to print the long form of its // information once. Driver::OptionData::OptionSet options_seen; Driver::OptionData::OptionSet::iterator pos; indent_level += 5; for (const auto &opt : g_options) { // Only print this option if we haven't already seen it. pos = options_seen.find(opt.short_option); if (pos == options_seen.end()) { CommandArgumentType arg_type = opt.argument_type; const char *arg_name = SBCommandInterpreter::GetArgumentTypeAsCString(arg_type); options_seen.insert(opt.short_option); fprintf(out, "%*s-%c ", indent_level, "", opt.short_option); if (arg_type != eArgTypeNone) fprintf(out, "<%s>", arg_name); fprintf(out, "\n"); fprintf(out, "%*s--%s ", indent_level, "", opt.long_option); if (arg_type != eArgTypeNone) fprintf(out, "<%s>", arg_name); fprintf(out, "\n"); indent_level += 5; OutputFormattedUsageText(out, indent_level, opt.usage_text, screen_width); indent_level -= 5; fprintf(out, "\n"); } } indent_level -= 5; fprintf(out, "\n%*sNotes:\n", indent_level, ""); indent_level += 5; fprintf(out, "\n%*sMultiple \"-s\" and \"-o\" options can be provided. They will " "be processed" "\n%*sfrom left to right in order, with the source files and commands" "\n%*sinterleaved. The same is true of the \"-S\" and \"-O\" " "options. The before" "\n%*sfile and after file sets can intermixed freely, the command " "parser will" "\n%*ssort them out. The order of the file specifiers (\"-c\", " "\"-f\", etc.) is" "\n%*snot significant in this regard.\n\n", indent_level, "", indent_level, "", indent_level, "", indent_level, "", indent_level, "", indent_level, ""); fprintf( out, "\n%*sIf you don't provide -f then the first argument will be the file " "to be" "\n%*sdebugged which means that '%s -- [ []]' also" "\n%*sworks. But remember to end the options with \"--\" if any of your" "\n%*sarguments have a \"-\" in them.\n\n", indent_level, "", indent_level, "", name, indent_level, "", indent_level, ""); } static void BuildGetOptTable(std::vector