diff options
Diffstat (limited to 'lldb/tools/lldb-mi/Driver.cpp')
-rw-r--r-- | lldb/tools/lldb-mi/Driver.cpp | 1285 |
1 files changed, 1285 insertions, 0 deletions
diff --git a/lldb/tools/lldb-mi/Driver.cpp b/lldb/tools/lldb-mi/Driver.cpp new file mode 100644 index 00000000000..826a5a89dca --- /dev/null +++ b/lldb/tools/lldb-mi/Driver.cpp @@ -0,0 +1,1285 @@ +//===-- Driver.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmnConfig.h" +#if MICONFIG_COMPILE_MIDRIVER_WITH_LLDBDRIVER + +#include "Platform.h" // CODETAG_IOR_SIGNALS +#include "Driver.h" +#include <lldb\Host\windows\getopt\GetOptInc.h> + +#include <lldb/API/SBBreakpoint.h> +#include <lldb/API/SBCommandInterpreter.h> +#include <lldb/API/SBCommandReturnObject.h> +#include <lldb/API/SBCommunication.h> +#include <lldb/API/SBEvent.h> +#include <lldb/API/SBHostOS.h> +#include <lldb/API/SBListener.h> +#include <lldb/API/SBStream.h> +#include <lldb/API/SBTarget.h> +#include <lldb/API/SBThread.h> +#include <lldb/API/SBProcess.h> + +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 char *g_debugger_name = (char *) ""; +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 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. <architecture> 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 <filename> 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 <path> 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, "source-quietly" , 'Q', no_argument , 0, eArgTypeNone, + "Tells the debugger suppress output from commands provided in the -s, -S, -O and -o commands." }, + { 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." }, + { 0, false, NULL , 0 , 0 , 0, eArgTypeNone, NULL } +}; + +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_debugger_name = (char *) m_debugger.GetInstanceName(); + if (g_debugger_name == NULL) + g_debugger_name = (char *) ""; + g_driver = this; +} + +Driver::~Driver () +{ + g_driver = NULL; + g_debugger_name = 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; + } + } +} + +void +ShowUsage (FILE *out, OptionDefinition *option_table, 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. <cmd> [options-for-level-0] + // <cmd> [options-for-level-1] + // etc. + + uint32_t num_options; + uint32_t num_option_sets = 0; + + for (num_options = 0; option_table[num_options].long_option != NULL; ++num_options) + { + uint32_t this_usage_mask = option_table[num_options].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 (uint32_t i = 0; i < num_options; ++i) + { + if (option_table[i].usage_mask & opt_set_mask) + { + CommandArgumentType arg_type = option_table[i].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 (option_table[i].short_option == 'h') + is_help_line = true; + + if (option_table[i].required) + { + if (option_table[i].option_has_arg == required_argument) + fprintf (out, " -%c <%s>", option_table[i].short_option, arg_name); + else if (option_table[i].option_has_arg == optional_argument) + fprintf (out, " -%c [<%s>]", option_table[i].short_option, arg_name); + else + fprintf (out, " -%c", option_table[i].short_option); + } + else + { + if (option_table[i].option_has_arg == required_argument) + fprintf (out, " [-%c <%s>]", option_table[i].short_option, arg_name); + else if (option_table[i].option_has_arg == optional_argument) + fprintf (out, " [-%c [<%s>]]", option_table[i].short_option, arg_name); + else + fprintf (out, " [-%c]", option_table[i].short_option); + } + } + } + if (!is_help_line && (opt_set <= last_option_set_with_args)) + fprintf (out, " [[--] <PROGRAM-ARG-1> [<PROGRAM_ARG-2> ...]]"); + } + + fprintf (out, "\n\n"); + + // Now print out all the detailed information about the various options: long form, short form and help text: + // -- long_name <argument> + // - short <argument> + // 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 (uint32_t i = 0; i < num_options; ++i) + { + // Only print this option if we haven't already seen it. + pos = options_seen.find (option_table[i].short_option); + if (pos == options_seen.end()) + { + CommandArgumentType arg_type = option_table[i].argument_type; + const char *arg_name = SBCommandInterpreter::GetArgumentTypeAsCString (arg_type); + + options_seen.insert (option_table[i].short_option); + fprintf (out, "%*s-%c ", indent_level, "", option_table[i].short_option); + if (arg_type != eArgTypeNone) + fprintf (out, "<%s>", arg_name); + fprintf (out, "\n"); + fprintf (out, "%*s--%s ", indent_level, "", option_table[i].long_option); + if (arg_type != eArgTypeNone) + fprintf (out, "<%s>", arg_name); + fprintf (out, "\n"); + indent_level += 5; + OutputFormattedUsageText (out, indent_level, option_table[i].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 from left to right in order, " + "\n%*swith the source files and commands interleaved. The same is true of the \"-S\" and \"-O\" options." + "\n%*sThe before file and after file sets can intermixed freely, the command parser will sort them out." + "\n%*sThe order of the file specifiers (\"-c\", \"-f\", etc.) is not significant in this regard.\n\n", + 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 debugged" + "\n%*swhich means that '%s -- <filename> [<ARG1> [<ARG2>]]' also works." + "\n%*sBut remember to end the options with \"--\" if any of your arguments have a \"-\" in them.\n\n", + indent_level, "", + indent_level, "", + name, + indent_level, ""); +} + +void +BuildGetOptTable (OptionDefinition *expanded_option_table, std::vector<struct option> &getopt_table, + uint32_t num_options) +{ + if (num_options == 0) + return; + + uint32_t i; + uint32_t j; + std::bitset<256> option_seen; + + getopt_table.resize (num_options + 1); + + for (i = 0, j = 0; i < num_options; ++i) + { + char short_opt = expanded_option_table[i].short_option; + + if (option_seen.test(short_opt) == false) + { + getopt_table[j].name = expanded_option_table[i].long_option; + getopt_table[j].has_arg = expanded_option_table[i].option_has_arg; + getopt_table[j].flag = NULL; + getopt_table[j].val = expanded_option_table[i].short_option; + option_seen.set(short_opt); + ++j; + } + } + + getopt_table[j].name = NULL; + getopt_table[j].has_arg = 0; + getopt_table[j].flag = NULL; + getopt_table[j].val = 0; + +} + +Driver::OptionData::OptionData () : + m_args(), + m_script_lang (lldb::eScriptLanguageDefault), + m_core_file (), + m_crash_log (), + m_initial_commands (), + m_after_file_commands (), + m_debug_mode (false), + m_source_quietly(false), + m_print_version (false), + m_print_python_path (false), + m_print_help (false), + m_wait_for(false), + m_process_name(), + m_process_pid(LLDB_INVALID_PROCESS_ID), + m_use_external_editor(false), + m_seen_options() +{ +} + +Driver::OptionData::~OptionData () +{ +} + +void +Driver::OptionData::Clear () +{ + m_args.clear (); + m_script_lang = lldb::eScriptLanguageDefault; + m_initial_commands.clear (); + m_after_file_commands.clear (); + m_debug_mode = false; + m_source_quietly = false; + m_print_help = false; + m_print_version = false; + m_print_python_path = false; + m_use_external_editor = false; + m_wait_for = false; + m_process_name.erase(); + m_process_pid = LLDB_INVALID_PROCESS_ID; +} + +void +Driver::OptionData::AddInitialCommand (const char *command, bool before_file, bool is_file, SBError &error) +{ + std::vector<std::pair<bool, std::string> > *command_set; + if (before_file) + command_set = &(m_initial_commands); + else + command_set = &(m_after_file_commands); + + if (is_file) + { + SBFileSpec file(command); + if (file.Exists()) + command_set->push_back (std::pair<bool, std::string> (true, optarg)); + else if (file.ResolveExecutableLocation()) + { + char final_path[PATH_MAX]; + file.GetPath (final_path, sizeof(final_path)); + std::string path_str (final_path); + command_set->push_back (std::pair<bool, std::string> (true, path_str)); + } + else + error.SetErrorStringWithFormat("file specified in --source (-s) option doesn't exist: '%s'", optarg); + } + else + command_set->push_back (std::pair<bool, std::string> (false, optarg)); +} + +void +Driver::ResetOptionValues () +{ + m_option_data.Clear (); +} + +const char * +Driver::GetFilename() const +{ + if (m_option_data.m_args.empty()) + return NULL; + return m_option_data.m_args.front().c_str(); +} + +const char * +Driver::GetCrashLogFilename() const +{ + if (m_option_data.m_crash_log.empty()) + return NULL; + return m_option_data.m_crash_log.c_str(); +} + +lldb::ScriptLanguage +Driver::GetScriptLanguage() const +{ + return m_option_data.m_script_lang; +} + +void +Driver::ExecuteInitialCommands (bool before_file) +{ + size_t num_commands; + std::vector<std::pair<bool, std::string> > *command_set; + if (before_file) + command_set = &(m_option_data.m_initial_commands); + else + command_set = &(m_option_data.m_after_file_commands); + + num_commands = command_set->size(); + SBCommandReturnObject result; + bool old_async = GetDebugger().GetAsync(); + GetDebugger().SetAsync(false); + for (size_t idx = 0; idx < num_commands; idx++) + { + bool is_file = (*command_set)[idx].first; + const char *command = (*command_set)[idx].second.c_str(); + char command_string[PATH_MAX * 2]; + const bool dump_stream_only_if_no_immediate = true; + const char *executed_command = command; + if (is_file) + { + ::snprintf (command_string, sizeof(command_string), "command source -s %i '%s'", m_option_data.m_source_quietly, command); + executed_command = command_string; + } + + m_debugger.GetCommandInterpreter().HandleCommand (executed_command, result, false); + if (!m_option_data.m_source_quietly || result.Succeeded() == false) + { + const size_t output_size = result.GetOutputSize(); + if (output_size > 0) + { + const char *cstr = result.GetOutput(dump_stream_only_if_no_immediate); + if (cstr) + printf ("%s", cstr); + } + const size_t error_size = result.GetErrorSize(); + if (error_size > 0) + { + const char *cstr = result.GetError(dump_stream_only_if_no_immediate); + if (cstr) + printf ("%s", cstr); + } + } + + if (result.Succeeded() == false) + { + const char *type = before_file ? "before file" : "after_file"; + if (is_file) + ::fprintf(stderr, "Aborting %s command execution, command file: '%s' failed.\n", type, command); + else + ::fprintf(stderr, "Aborting %s command execution, command: '%s' failed.\n", type, command); + break; + } + result.Clear(); + } + GetDebugger().SetAsync(old_async); +} + +bool +Driver::GetDebugMode() const +{ + return m_option_data.m_debug_mode; +} + + +// 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::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exiting) +{ + ResetOptionValues (); + + SBCommandReturnObject result; + + SBError error; + std::string option_string; + struct option *long_options = NULL; + std::vector<struct option> long_options_vector; + uint32_t num_options; + + for (num_options = 0; g_options[num_options].long_option != NULL; ++num_options) + /* Do Nothing. */; + + if (num_options == 0) + { + if (argc > 1) + error.SetErrorStringWithFormat ("invalid number of options"); + return error; + } + + BuildGetOptTable (g_options, long_options_vector, num_options); + + if (long_options_vector.empty()) + long_options = NULL; + else + long_options = &long_options_vector.front(); + + if (long_options == NULL) + { + error.SetErrorStringWithFormat ("invalid long options"); + return error; + } + + // Build the option_string argument for call to getopt_long_only. + + for (int i = 0; long_options[i].name != NULL; ++i) + { + if (long_options[i].flag == NULL) + { + option_string.push_back ((char) long_options[i].val); + switch (long_options[i].has_arg) + { + default: + case no_argument: + break; + case required_argument: + option_string.push_back (':'); + break; + case optional_argument: + option_string.append ("::"); + break; + } + } + } + + // 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); + + // Prepare for & make calls to getopt_long_only. +#if __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + int val; + while (1) + { + int long_options_index = -1; + val = ::getopt_long_only (argc, const_cast<char **>(argv), option_string.c_str(), long_options, &long_options_index); + + if (val == -1) + break; + else if (val == '?') + { + m_option_data.m_print_help = true; + error.SetErrorStringWithFormat ("unknown or ambiguous option"); + break; + } + else if (val == 0) + continue; + else + { + m_option_data.m_seen_options.insert ((char) val); + if (long_options_index == -1) + { + for (int i = 0; + long_options[i].name || long_options[i].has_arg || long_options[i].flag || long_options[i].val; + ++i) + { + if (long_options[i].val == val) + { + long_options_index = i; + break; + } + } + } + + if (long_options_index >= 0) + { + const int short_option = g_options[long_options_index].short_option; + + switch (short_option) + { + case 'h': + m_option_data.m_print_help = true; + break; + + case 'v': + m_option_data.m_print_version = true; + break; + + case 'P': + m_option_data.m_print_python_path = true; + break; + + case 'c': + { + SBFileSpec file(optarg); + if (file.Exists()) + { + m_option_data.m_core_file = optarg; + } + else + error.SetErrorStringWithFormat("file specified in --core (-c) option doesn't exist: '%s'", optarg); + } + break; + + case 'e': + m_option_data.m_use_external_editor = true; + break; + + case 'x': + m_debugger.SkipLLDBInitFiles (true); + m_debugger.SkipAppInitFiles (true); + break; + + case 'X': + m_debugger.SetUseColor (false); + break; + + case 'f': + { + SBFileSpec file(optarg); + if (file.Exists()) + { + m_option_data.m_args.push_back (optarg); + } + else if (file.ResolveExecutableLocation()) + { + char path[PATH_MAX]; + file.GetPath (path, sizeof(path)); + m_option_data.m_args.push_back (path); + } + else + error.SetErrorStringWithFormat("file specified in --file (-f) option doesn't exist: '%s'", optarg); + } + break; + + case 'a': + if (!m_debugger.SetDefaultArchitecture (optarg)) + error.SetErrorStringWithFormat("invalid architecture in the -a or --arch option: '%s'", optarg); + break; + + case 'l': + m_option_data.m_script_lang = m_debugger.GetScriptingLanguage (optarg); + break; + + case 'd': + m_option_data.m_debug_mode = true; + break; + + case 'Q': + m_option_data.m_source_quietly = true; + break; + + case 'n': + m_option_data.m_process_name = optarg; + break; + + case 'w': + m_option_data.m_wait_for = true; + break; + + case 'p': + { + char *remainder; + m_option_data.m_process_pid = strtol (optarg, &remainder, 0); + if (remainder == optarg || *remainder != '\0') + error.SetErrorStringWithFormat ("Could not convert process PID: \"%s\" into a pid.", + optarg); + } + break; + case 's': + m_option_data.AddInitialCommand(optarg, false, true, error); + break; + case 'o': + m_option_data.AddInitialCommand(optarg, false, false, error); + break; + case 'S': + m_option_data.AddInitialCommand(optarg, true, true, error); + break; + case 'O': + m_option_data.AddInitialCommand(optarg, true, false, error); + break; + default: + m_option_data.m_print_help = true; + error.SetErrorStringWithFormat ("unrecognized option %c", short_option); + break; + } + } + else + { + error.SetErrorStringWithFormat ("invalid option with value %i", val); + } + if (error.Fail()) + { + return error; + } + } + } + + if (error.Fail() || m_option_data.m_print_help) + { + ShowUsage (out_fh, g_options, m_option_data); + exiting = true; + } + else if (m_option_data.m_print_version) + { + ::fprintf (out_fh, "%s\n", m_debugger.GetVersionString()); + exiting = true; + } + else 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) + { + ::fprintf (out_fh, "%s\n", python_path); + } + else + ::fprintf (out_fh, "<PATH TOO LONG>\n"); + } + else + ::fprintf (out_fh, "<COULD NOT FIND PATH>\n"); + exiting = true; + } + else if (m_option_data.m_process_name.empty() && m_option_data.m_process_pid == LLDB_INVALID_PROCESS_ID) + { + // Any arguments that are left over after option parsing are for + // the program. If a file was specified with -f then the filename + // is already in the m_option_data.m_args array, and any remaining args + // are arguments for the inferior program. If no file was specified with + // -f, then what is left is the program name followed by any arguments. + + // Skip any options we consumed with getopt_long_only + argc -= optind; + argv += optind; + + if (argc > 0) + { + for (int arg_idx=0; arg_idx<argc; ++arg_idx) + { + const char *arg = argv[arg_idx]; + if (arg) + m_option_data.m_args.push_back (arg); + } + } + + } + else + { + // Skip any options we consumed with getopt_long_only + argc -= optind; + //argv += optind; // Commented out to keep static analyzer happy + + if (argc > 0) + ::fprintf (out_fh, "Warning: program arguments are ignored when attaching.\n"); + } + + return error; +} + +void +Driver::MainLoop () +{ + if (::tcgetattr(STDIN_FILENO, &g_old_stdin_termios) == 0) + { + g_old_stdin_termios_is_valid = true; + atexit (reset_stdin_termios); + } + + ::setbuf (stdin, NULL); + ::setbuf (stdout, NULL); + + m_debugger.SetErrorFileHandle (stderr, false); + m_debugger.SetOutputFileHandle (stdout, false); + m_debugger.SetInputFileHandle (stdin, true); + + m_debugger.SetUseExternalEditor(m_option_data.m_use_external_editor); + + struct winsize window_size; + if (isatty (STDIN_FILENO) + && ::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 (GetDebugMode()) + { + result.PutError (m_debugger.GetErrorFileHandle()); + result.PutOutput (m_debugger.GetOutputFileHandle()); + } + + // Now we handle options we got from the command line + // First source in the commands specified to be run before the file arguments are processed. + ExecuteInitialCommands(true); + + // Was there a core file specified? + std::string core_file_spec(""); + if (!m_option_data.m_core_file.empty()) + core_file_spec.append("--core ").append(m_option_data.m_core_file); + + char command_string[PATH_MAX * 2]; + const size_t num_args = m_option_data.m_args.size(); + if (num_args > 0) + { + char arch_name[64]; + if (m_debugger.GetDefaultArchitecture (arch_name, sizeof (arch_name))) + ::snprintf (command_string, + sizeof (command_string), + "target create --arch=%s %s \"%s\"", + arch_name, + core_file_spec.c_str(), + m_option_data.m_args[0].c_str()); + else + ::snprintf (command_string, + sizeof(command_string), + "target create %s \"%s\"", + core_file_spec.c_str(), + m_option_data.m_args[0].c_str()); + + m_debugger.HandleCommand (command_string); + + if (num_args > 1) + { + m_debugger.HandleCommand ("settings clear target.run-args"); + char arg_cstr[1024]; + for (size_t arg_idx = 1; arg_idx < num_args; ++arg_idx) + { + ::snprintf (arg_cstr, + sizeof(arg_cstr), + "settings append target.run-args \"%s\"", + m_option_data.m_args[arg_idx].c_str()); + m_debugger.HandleCommand (arg_cstr); + } + } + } + else if (!core_file_spec.empty()) + { + ::snprintf (command_string, + sizeof(command_string), + "target create %s", + core_file_spec.c_str()); + m_debugger.HandleCommand (command_string);; + } + else if (!m_option_data.m_process_name.empty()) + { + ::snprintf (command_string, + sizeof(command_string), + "process attach --name '%s'%s", + m_option_data.m_process_name.c_str(), + m_option_data.m_wait_for ? " --waitfor" : ""); + m_debugger.HandleCommand (command_string); + } + else if (LLDB_INVALID_PROCESS_ID != m_option_data.m_process_pid) + { + ::snprintf (command_string, + sizeof(command_string), + "process attach --pid %" PRIu64, + m_option_data.m_process_pid); + m_debugger.HandleCommand (command_string); + } + + ExecuteInitialCommands(false); + + // Now that all option parsing is done, we try and parse the .lldbinit + // file in the current working directory + sb_interpreter.SourceInitFileInCurrentWorkingDirectory (result); + if (GetDebugMode()) + { + result.PutError(m_debugger.GetErrorFileHandle()); + result.PutOutput(m_debugger.GetOutputFileHandle()); + } + + bool handle_events = true; + bool spawn_thread = false; + m_debugger.RunCommandInterpreter(handle_events, spawn_thread); + + reset_stdin_termios(); + fclose (stdin); + + SBDebugger::Destroy (m_debugger); +} + +void +Driver::ResizeWindow (unsigned short col) +{ + GetDebugger().SetTerminalWidth (col); +} + +//++ ------------------------------------------------------------------------------------ +// Details: Setup *this driver so it works as pass through (child) driver for the MI +// driver. Called by the parent (MI driver) driver. +// This driver has setup code in two places. The original in MainLoop() and +// in int main() (if MICONFIG_COMPILE_MIDRIVER_VERSION == 0) so that code can remain +// as much near to the original code as possible. If MI driver is the main +// driver then this function is used to set up the Driver to work with the +// MI driver. +// Type: Method. +// Args: vwErrMsg - (W) On failure current error discription. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool Driver::MISetup( CMIUtilString & vwErrMsg ) +{ + bool bOk = MIstatus::success; + + // Is *this driver a pass through driver to the MI driver + CMIDriverBase * pParent = GetDriversParent(); + if( pParent == nullptr ) + { + // No it is not. + // If MI is the main driver (which passes through to *this driver) then + // *this driver needs to be initialized after MI is initialize to have a valid + // pointer to the parent driver. *this is the parent's pass thru driver. + assert( pParent == nullptr ); + return MIstatus::success; // Allow success for if Driver is the main driver + } + + // MI driver may have streams it wants *this driver to use - still to be sorted + m_debugger.SetErrorFileHandle( pParent->GetStderr(), false ); // MI may redirect to its own stream + m_debugger.SetOutputFileHandle( pParent->GetStdout(), false ); // MI likely to NULL this + m_debugger.SetInputFileHandle( pParent->GetStdin(), false ); // MI could use this to feed input + + // ToDo: Do I need this? + m_debugger.SetUseExternalEditor( m_option_data.m_use_external_editor ); + + // ToDo: Do I need this? + struct winsize window_size; + if( isatty( STDIN_FILENO ) && ::ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) == 0 ) + { + if( window_size.ws_col > 0 ) + m_debugger.SetTerminalWidth( window_size.ws_col ); + } + + return bOk; +} + +//++ ------------------------------------------------------------------------------------ +// Details: Initialize setup *this driver ready for use. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool Driver::DoInitialize( void ) +{ + // Do nothing + return MIstatus::success; +} + +//++ ------------------------------------------------------------------------------------ +// Details: Unbind detach or release resources used by *this driver. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool Driver::DoShutdown( void ) +{ + SBDebugger::Destroy( m_debugger ); + + // Is *this driver a pass through driver to the MI driver + CMIDriverBase * pParent = GetDriversParent(); + if( pParent == nullptr ) + { + // See DoInitialize(). + assert( pParent == nullptr ); + return MIstatus::success; + } + + // Put stuff here when *this driver is a pass thru driver to the MI driver + + return MIstatus::success; +} + +//++ ------------------------------------------------------------------------------------ +// Details: Retrieve the name for *this driver. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString & - Driver name. +// Throws: None. +//-- +const CMIUtilString & Driver::GetName( void ) const +{ + static CMIUtilString name( "LLDB driver" ); + return name; +} + +//++ ------------------------------------------------------------------------------------ +// Details: Retrieve *this driver's last error condition. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString - Text description. +// Throws: None. +//-- +CMIUtilString Driver::GetError( void ) const +{ + // Do nothing - to implement + return CMIUtilString(); +} + +//++ ------------------------------------------------------------------------------------ +// Details: Call this function puts *this driver to work. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool Driver::DoMainLoop( void ) +{ + MainLoop(); + + return MIstatus::success; +} + +//++ ------------------------------------------------------------------------------------ +// Details: Call *this driver to resize the console window. +// Type: Overridden. +// Args: vTermWidth - (R) New window column size. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +void Driver::DoResizeWindow( const uint32_t vTermWidth ) +{ + ResizeWindow( (unsigned short) vTermWidth ); +} + +//++ ------------------------------------------------------------------------------------ +// Details: Call *this driver to return it's debugger. +// Type: Overridden. +// Args: None. +// Return: lldb::SBDebugger & - LLDB debugger object reference. +// Throws: None. +//-- +lldb::SBDebugger & Driver::GetTheDebugger( void ) +{ + return GetDebugger(); +} + +//++ ------------------------------------------------------------------------------------ +// Details: Proxy function to allow the driver implementation to validate executable +// command line arguments. +// Type: Overrideable. +// Args: argc - (R) An integer that contains the count of arguments that follow in +// argv. The argc parameter is always greater than or equal to 1. +// argv - (R) An array of null-terminated strings representing command-line +// arguments entered by the user of the program. By convention, +// argv[0] is the command with which the program is invoked. +// vpStdOut - (R) Pointer to a standard output stream. +// vwbExiting - (W) True = *this want to exit, Reasons: help, invalid arg(s), +// version information only. +// False = Continue to work, start debugger i.e. Command +// interpreter. +// Return: lldb::SBError - LLDB current error status. +// Throws: None. +//-- +lldb::SBError Driver::DoParseArgs( const int argc, const char * argv[], FILE * vpStdOut, bool & vwbExiting ) +{ + return ParseArgs( argc, argv, vpStdOut, vwbExiting ); +} + +//++ ------------------------------------------------------------------------------------ +// Details: A client can ask if *this driver is GDB/MI compatible. +// Type: Overridden. +// Args: None. +// Return: True - GBD/MI compatible LLDB front end. +// False - Not GBD/MI compatible LLDB front end. +// Throws: None. +//-- +bool Driver::GetDriverIsGDBMICompatibleDriver( void ) const +{ + return false; +} + +//++ ------------------------------------------------------------------------------------ +// Details: This function allows *this driver to call on another driver to perform work +// should this driver not be able to handle the client data input. +// SetDriverToFallThruTo() specifies the fall through to driver. +// Check the error message if the function returns a failure. +// Type: Overridden. +// Args: vCmd - (R) Command instruction to interpret. +// vwErrMsg - (W) Error description on command failing. +// Return: MIstatus::success - Command succeeded. +// MIstatus::failure - Command failed. +// Throws: None. +//-- +bool Driver::DoFallThruToAnotherDriver( const CMIUtilString & vCmd, CMIUtilString & vwErrMsg ) +{ + bool bOk = MIstatus::success; + vwErrMsg.empty(); + + // ToDo: implement do work on other driver after other driver said "Give up you try" + SBCommandReturnObject returnObj = lldb::SBCommandReturnObject(); + SBCommandInterpreter cmdIntrp = m_debugger.GetCommandInterpreter(); + const lldb::ReturnStatus cmdResult = cmdIntrp.HandleCommand( vCmd.c_str(), returnObj ); + if( returnObj.Succeeded() == false ) + { + bOk = MIstatus::failure; + vwErrMsg = returnObj.GetError(); + } + + return bOk; +} + +//++ ------------------------------------------------------------------------------------ +// Details: This function allows *this driver to call functionality on the parent driver +// ask for information for example. +// Type: Overridden. +// Args: vrOtherDriver - (R) Reference to another driver object. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool Driver::SetDriverParent( const CMIDriverBase & vrOtherDriver ) +{ + m_pDriverParent = const_cast< CMIDriverBase * >( &vrOtherDriver ); + + return MIstatus::success; +} + +//++ ------------------------------------------------------------------------------------ +// Details: Set a unique ID for *this driver. It cannot be empty. +// Type: Overridden. +// Args: vId - (R) Text description. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool Driver::SetId( const CMIUtilString & vId ) +{ + if( vId.empty() ) + { + // Invalid to have it empty + return MIstatus::failure; + } + + m_strDriverId = vId; + return MIstatus::success; +} + +//++ ------------------------------------------------------------------------------------ +// Details: Get the unique ID for *this driver. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString & - Text description. +// Throws: None. +//-- +const CMIUtilString & Driver::GetId( void ) const +{ + return m_strDriverId; +} + +//++ ------------------------------------------------------------------------------------ +// Details: Create *this driver. Function contains functionality that needs to be called +// prior to constructing the *this driver. +// Type: Static method. +// Args: None. +// Return: Driver * - Ptr to the LLDB driver object. +// Throws: None. +//-- +Driver * Driver::CreateSelf( void ) +{ + lldb::SBDebugger::Initialize(); + + Driver * pDriver = new Driver; + return pDriver; +} + +//++ ------------------------------------------------------------------------------------ +// Details: Retrieve the name for *this driver. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString - Driver name. +// Throws: None. +//-- +const CMIUtilString & Driver::GetDriverName( void ) const +{ + return GetName(); +} + +//++ ------------------------------------------------------------------------------------ +// Details: Get the unique ID for *this driver. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString & - Text description. +// Throws: None. +//-- +const CMIUtilString & Driver::GetDriverId( void ) const +{ + return GetId(); +} + +#endif // MICONFIG_COMPILE_MIDRIVER_WITH_LLDBDRIVER
\ No newline at end of file |