Index: include/lldb/API/SBCommandInterpreter.h =================================================================== --- include/lldb/API/SBCommandInterpreter.h +++ include/lldb/API/SBCommandInterpreter.h @@ -225,6 +225,13 @@ void SetPromptOnQuit(bool b); + //---------------------------------------------------------------------- + /// Resolve the command just as HandleCommand would, expanding abbreviations + /// and aliases. If successful, result->GetOutput has the full expansion. + //---------------------------------------------------------------------- + void + ResolveCommand(const char *command_line, SBCommandReturnObject &result); + protected: lldb_private::CommandInterpreter & Index: include/lldb/Interpreter/CommandInterpreter.h =================================================================== --- include/lldb/Interpreter/CommandInterpreter.h +++ include/lldb/Interpreter/CommandInterpreter.h @@ -628,6 +628,9 @@ void SetPromptOnQuit (bool b); + void + ResolveCommand(const char *command_line, CommandReturnObject &result); + bool GetStopCmdSourceOnError () const; @@ -688,6 +691,13 @@ Error PreprocessCommand (std::string &command); + // Completely resolves aliases and abbreviations, returning a pointer to the + // final command object and updating command_line to the fully substituted + // and translated command. + CommandObject * + ResolveCommandImpl(std::string &command_line, CommandReturnObject &result); + + Debugger &m_debugger; // The debugger session that this interpreter is associated with ExecutionContextRef m_exe_ctx_ref; // The current execution context to use when handling commands bool m_synchronous_execution; Index: scripts/Python/interface/SBCommandInterpreter.i =================================================================== --- scripts/Python/interface/SBCommandInterpreter.i +++ scripts/Python/interface/SBCommandInterpreter.i @@ -155,6 +155,9 @@ void SetPromptOnQuit(bool b); + void + ResolveCommand(const char *command_line, SBCommandReturnObject &result); + bool CommandExists (const char *cmd); Index: source/API/SBCommandInterpreter.cpp =================================================================== --- source/API/SBCommandInterpreter.cpp +++ source/API/SBCommandInterpreter.cpp @@ -462,6 +462,22 @@ m_opaque_ptr->SetPromptOnQuit(b); } +void +SBCommandInterpreter::ResolveCommand(const char *command_line, SBCommandReturnObject &result) +{ + result.Clear(); + if (command_line && m_opaque_ptr) + { + m_opaque_ptr->ResolveCommand(command_line, result.ref()); + } + else + { + result->AppendError("SBCommandInterpreter or the command line is not valid"); + result->SetStatus(eReturnStatusFailed); + } +} + + CommandInterpreter * SBCommandInterpreter::get () { Index: source/Commands/CommandObjectMultiword.cpp =================================================================== --- source/Commands/CommandObjectMultiword.cpp +++ source/Commands/CommandObjectMultiword.cpp @@ -153,7 +153,7 @@ error_msg.append (GetCommandName()); error_msg.append (" "); error_msg.append (sub_command); - error_msg.append ("'"); + error_msg.append ("'."); if (num_subcmd_matches > 0) { Index: source/Interpreter/CommandInterpreter.cpp =================================================================== --- source/Interpreter/CommandInterpreter.cpp +++ source/Interpreter/CommandInterpreter.cpp @@ -79,6 +79,7 @@ using namespace lldb; using namespace lldb_private; +static const char *k_white_space = " \t\v"; static PropertyDefinition g_properties[] = @@ -156,6 +157,17 @@ m_collection_sp->SetPropertyAtIndexAsBoolean (nullptr, idx, b); } +void +CommandInterpreter::ResolveCommand(const char *command_line, CommandReturnObject &result) +{ + std::string command = command_line; + if (ResolveCommandImpl(command, result) != nullptr) { + result.AppendMessageWithFormat("%s", command.c_str()); + result.SetStatus(eReturnStatusSuccessFinishResult); + } +} + + bool CommandInterpreter::GetStopCmdSourceOnError () const { @@ -1258,8 +1270,7 @@ // eventually be invoked by the given command line. CommandObject *cmd_obj = nullptr; - std::string white_space (" \t\v"); - size_t start = command_string.find_first_not_of (white_space); + size_t start = command_string.find_first_not_of (k_white_space); size_t end = 0; bool done = false; while (!done) @@ -1267,7 +1278,7 @@ if (start != std::string::npos) { // Get the next word from command_string. - end = command_string.find_first_of (white_space, start); + end = command_string.find_first_of (k_white_space, start); if (end == std::string::npos) end = command_string.size(); std::string cmd_word = command_string.substr (start, end - start); @@ -1296,7 +1307,7 @@ if (!cmd_obj || !cmd_obj->IsMultiwordObject() || end >= command_string.size()) done = true; else - start = command_string.find_first_not_of (white_space, end); + start = command_string.find_first_not_of (k_white_space, end); } else // Unable to find any more words. @@ -1311,7 +1322,6 @@ return cmd_obj; } -static const char *k_white_space = " \t\v"; static const char *k_valid_command_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; static void StripLeadingSpaces (std::string &s) @@ -1471,7 +1481,7 @@ ("Not enough arguments provided; you need at least %d arguments to use this alias.\n", index); result.SetStatus (eReturnStatusFailed); - return alias_cmd_obj; + return nullptr; } else { @@ -1496,7 +1506,7 @@ { // The command preprocessor needs to do things to the command // line before any parsing of arguments or anything else is done. - // The only current stuff that gets proprocessed is anyting enclosed + // The only current stuff that gets preprocessed is anything enclosed // in backtick ('`') characters is evaluated as an expression and // the result of the expression must be a scalar that can be substituted // into the command. An example would be: @@ -1637,9 +1647,6 @@ { - bool done = false; - CommandObject *cmd_obj = nullptr; - bool wants_raw_input = false; std::string command_string (command_line); std::string original_command_string (command_line); @@ -1739,192 +1746,34 @@ result.SetStatus(eReturnStatusFailed); return false; } - // Phase 1. - - // Before we do ANY kind of argument processing, etc. we need to figure out what the real/final command object - // is for the specified command, and whether or not it wants raw input. This gets complicated by the fact that - // the user could have specified an alias, and in translating the alias there may also be command options and/or - // even data (including raw text strings) that need to be found and inserted into the command line as part of - // the translation. So this first step is plain look-up & replacement, resulting in three things: 1). the command - // object whose Execute method will actually be called; 2). a revised command string, with all substitutions & - // replacements taken care of; 3). whether or not the Execute function wants raw input or not. - - StreamString revised_command_line; - size_t actual_cmd_name_len = 0; - std::string next_word; - StringList matches; - while (!done) - { - char quote_char = '\0'; - std::string suffix; - ExtractCommand (command_string, next_word, suffix, quote_char); - if (cmd_obj == nullptr) - { - std::string full_name; - if (GetAliasFullName(next_word.c_str(), full_name)) - { - std::string alias_result; - cmd_obj = BuildAliasResult (full_name.c_str(), command_string, alias_result, result); - revised_command_line.Printf ("%s", alias_result.c_str()); - if (cmd_obj) - { - wants_raw_input = cmd_obj->WantsRawCommandString (); - actual_cmd_name_len = strlen (cmd_obj->GetCommandName()); - } - } - else - { - cmd_obj = GetCommandObject (next_word.c_str(), &matches); - if (cmd_obj) - { - actual_cmd_name_len += next_word.length(); - revised_command_line.Printf ("%s", next_word.c_str()); - wants_raw_input = cmd_obj->WantsRawCommandString (); - } - else - { - revised_command_line.Printf ("%s", next_word.c_str()); - } - } - } - else - { - if (cmd_obj->IsMultiwordObject ()) - { - CommandObject *sub_cmd_obj = cmd_obj->GetSubcommandObject (next_word.c_str()); - if (sub_cmd_obj) - { - actual_cmd_name_len += next_word.length() + 1; - revised_command_line.Printf (" %s", next_word.c_str()); - cmd_obj = sub_cmd_obj; - wants_raw_input = cmd_obj->WantsRawCommandString (); - } - else - { - if (quote_char) - revised_command_line.Printf (" %c%s%s%c", quote_char, next_word.c_str(), suffix.c_str(), quote_char); - else - revised_command_line.Printf (" %s%s", next_word.c_str(), suffix.c_str()); - done = true; - } - } - else - { - if (quote_char) - revised_command_line.Printf (" %c%s%s%c", quote_char, next_word.c_str(), suffix.c_str(), quote_char); - else - revised_command_line.Printf (" %s%s", next_word.c_str(), suffix.c_str()); - done = true; - } - } - if (cmd_obj == nullptr) - { - const size_t num_matches = matches.GetSize(); - if (matches.GetSize() > 1) { - StreamString error_msg; - error_msg.Printf ("Ambiguous command '%s'. Possible matches:\n", next_word.c_str()); - - for (uint32_t i = 0; i < num_matches; ++i) { - error_msg.Printf ("\t%s\n", matches.GetStringAtIndex(i)); - } - result.AppendRawError (error_msg.GetString().c_str()); - } else { - // We didn't have only one match, otherwise we wouldn't get here. - assert(num_matches == 0); - result.AppendErrorWithFormat ("'%s' is not a valid command.\n", next_word.c_str()); - } - result.SetStatus (eReturnStatusFailed); - return false; - } - - if (cmd_obj->IsMultiwordObject ()) - { - if (!suffix.empty()) - { - - result.AppendErrorWithFormat ("command '%s' did not recognize '%s%s%s' as valid (subcommand might be invalid).\n", - cmd_obj->GetCommandName(), - next_word.empty() ? "" : next_word.c_str(), - next_word.empty() ? " -- " : " ", - suffix.c_str()); - result.SetStatus (eReturnStatusFailed); - return false; - } - } - else - { - // If we found a normal command, we are done - done = true; - if (!suffix.empty()) - { - switch (suffix[0]) - { - case '/': - // GDB format suffixes - { - Options *command_options = cmd_obj->GetOptions(); - if (command_options && command_options->SupportsLongOption("gdb-format")) - { - std::string gdb_format_option ("--gdb-format="); - gdb_format_option += (suffix.c_str() + 1); + // Phase 1. - bool inserted = false; - std::string &cmd = revised_command_line.GetString(); - size_t arg_terminator_idx = FindArgumentTerminator (cmd); - if (arg_terminator_idx != std::string::npos) - { - // Insert the gdb format option before the "--" that terminates options - gdb_format_option.append(1,' '); - cmd.insert(arg_terminator_idx, gdb_format_option); - inserted = true; - } - - if (!inserted) - revised_command_line.Printf (" %s", gdb_format_option.c_str()); - - if (wants_raw_input && FindArgumentTerminator(cmd) == std::string::npos) - revised_command_line.PutCString (" --"); - } - else - { - result.AppendErrorWithFormat ("the '%s' command doesn't support the --gdb-format option\n", - cmd_obj->GetCommandName()); - result.SetStatus (eReturnStatusFailed); - return false; - } - } - break; - - default: - result.AppendErrorWithFormat ("unknown command shorthand suffix: '%s'\n", - suffix.c_str()); - result.SetStatus (eReturnStatusFailed); - return false; - - } - } - } - if (command_string.length() == 0) - done = true; - - } + // Before we do ANY kind of argument processing, we need to figure out what + // the real/final command object is for the specified command. This gets + // complicated by the fact that the user could have specified an alias, and, + // in translating the alias, there may also be command options and/or even + // data (including raw text strings) that need to be found and inserted into + // the command line as part of the translation. So this first step is plain + // look-up and replacement, resulting in: + // 1. the command object whose Execute method will actually be called + // 2. a revised command string, with all substitutions and replacements + // taken care of + // From 1 above, we can determine whether the Execute function wants raw + // input or not. + + CommandObject *cmd_obj = ResolveCommandImpl(command_string, result); + + // Although the user may have abbreviated the command, the command_string now + // has the command expanded to the full name. For example, if the input + // was "br s -n main", command_string is now "breakpoint set -n main". - if (!command_string.empty()) - revised_command_line.Printf (" %s", command_string.c_str()); - - // End of Phase 1. - // At this point cmd_obj should contain the CommandObject whose Execute method will be called, if the command - // specified was valid; revised_command_line contains the complete command line (including command name(s)), - // fully translated with all substitutions & translations taken care of (still in raw text format); and - // wants_raw_input specifies whether the Execute method expects raw input or not. - - if (log) { - log->Printf ("HandleCommand, cmd_obj : '%s'", cmd_obj ? cmd_obj->GetCommandName() : ""); - log->Printf ("HandleCommand, revised_command_line: '%s'", revised_command_line.GetData()); - log->Printf ("HandleCommand, wants_raw_input:'%s'", wants_raw_input ? "True" : "False"); + log->Printf("HandleCommand, cmd_obj : '%s'", cmd_obj ? cmd_obj->GetCommandName() : ""); + log->Printf("HandleCommand, (revised) command_string: '%s'", command_string.c_str()); + const bool wants_raw_input = (cmd_obj != NULL) ? cmd_obj->WantsRawCommandString() : false; + log->Printf("HandleCommand, wants_raw_input:'%s'", wants_raw_input ? "True" : "False"); } // Phase 2. @@ -1935,7 +1784,7 @@ { if (add_to_history) { - Args command_args (revised_command_line.GetData()); + Args command_args (command_string); const char *repeat_command = cmd_obj->GetRepeatCommand(command_args, 0); if (repeat_command != nullptr) m_repeat_command.assign(repeat_command); @@ -1945,18 +1794,13 @@ m_command_history.AppendString (original_command_string); } - command_string = revised_command_line.GetData(); - std::string command_name (cmd_obj->GetCommandName()); std::string remainder; + const std::size_t actual_cmd_name_len = strlen (cmd_obj->GetCommandName()); if (actual_cmd_name_len < command_string.length()) - remainder = command_string.substr (actual_cmd_name_len); // Note: 'actual_cmd_name_len' may be considerably shorter - // than cmd_obj->GetCommandName(), because name completion - // allows users to enter short versions of the names, - // e.g. 'br s' for 'breakpoint set'. + remainder = command_string.substr (actual_cmd_name_len); // Remove any initial spaces - std::string white_space (" \t\v"); - size_t pos = remainder.find_first_not_of (white_space); + size_t pos = remainder.find_first_not_of (k_white_space); if (pos != 0 && pos != std::string::npos) remainder.erase(0, pos); @@ -1968,7 +1812,7 @@ else { // We didn't find the first command object, so complete the first argument. - Args command_args (revised_command_line.GetData()); + Args command_args (command_string); StringList matches; int num_matches; int cursor_index = 0; @@ -3393,3 +3237,179 @@ } +CommandObject * +CommandInterpreter::ResolveCommandImpl(std::string &command_line, CommandReturnObject &result) +{ + std::string scratch_command(command_line); // working copy so we don't modify command_line unless we succeed + CommandObject *cmd_obj = nullptr; + StreamString revised_command_line; + bool wants_raw_input = false; + size_t actual_cmd_name_len = 0; + std::string next_word; + StringList matches; + bool done = false; + while (!done) + { + char quote_char = '\0'; + std::string suffix; + ExtractCommand(scratch_command, next_word, suffix, quote_char); + if (cmd_obj == nullptr) + { + std::string full_name; + if (GetAliasFullName(next_word.c_str(), full_name)) + { + std::string alias_result; + cmd_obj = BuildAliasResult(full_name.c_str(), scratch_command, alias_result, result); + revised_command_line.Printf("%s", alias_result.c_str()); + if (cmd_obj) + { + wants_raw_input = cmd_obj->WantsRawCommandString(); + actual_cmd_name_len = strlen(cmd_obj->GetCommandName()); + } + } + else + { + cmd_obj = GetCommandObject(next_word.c_str(), &matches); + if (cmd_obj) + { + actual_cmd_name_len += strlen(cmd_obj->GetCommandName()); + revised_command_line.Printf("%s", cmd_obj->GetCommandName()); + wants_raw_input = cmd_obj->WantsRawCommandString(); + } + else + { + revised_command_line.Printf ("%s", next_word.c_str()); + } + } + } + else + { + if (cmd_obj->IsMultiwordObject ()) + { + CommandObject *sub_cmd_obj = cmd_obj->GetSubcommandObject(next_word.c_str()); + if (sub_cmd_obj) + { + // The subcommand's name includes the parent command's name, + // so restart rather than append to the revised_command_line. + actual_cmd_name_len = strlen(sub_cmd_obj->GetCommandName()) + 1; + revised_command_line.Clear(); + revised_command_line.Printf("%s", sub_cmd_obj->GetCommandName()); + cmd_obj = sub_cmd_obj; + wants_raw_input = cmd_obj->WantsRawCommandString(); + } + else + { + if (quote_char) + revised_command_line.Printf(" %c%s%s%c", quote_char, next_word.c_str(), suffix.c_str(), quote_char); + else + revised_command_line.Printf(" %s%s", next_word.c_str(), suffix.c_str()); + done = true; + } + } + else + { + if (quote_char) + revised_command_line.Printf(" %c%s%s%c", quote_char, next_word.c_str(), suffix.c_str(), quote_char); + else + revised_command_line.Printf(" %s%s", next_word.c_str(), suffix.c_str()); + done = true; + } + } + + if (cmd_obj == nullptr) + { + const size_t num_matches = matches.GetSize(); + if (matches.GetSize() > 1) { + StreamString error_msg; + error_msg.Printf("Ambiguous command '%s'. Possible matches:\n", next_word.c_str()); + + for (uint32_t i = 0; i < num_matches; ++i) { + error_msg.Printf("\t%s\n", matches.GetStringAtIndex(i)); + } + result.AppendRawError(error_msg.GetString().c_str()); + } else { + // We didn't have only one match, otherwise we wouldn't get here. + assert(num_matches == 0); + result.AppendErrorWithFormat("'%s' is not a valid command.\n", next_word.c_str()); + } + result.SetStatus(eReturnStatusFailed); + return nullptr; + } + + if (cmd_obj->IsMultiwordObject()) + { + if (!suffix.empty()) + { + result.AppendErrorWithFormat("command '%s' did not recognize '%s%s%s' as valid (subcommand might be invalid).\n", + cmd_obj->GetCommandName(), + next_word.empty() ? "" : next_word.c_str(), + next_word.empty() ? " -- " : " ", + suffix.c_str()); + result.SetStatus(eReturnStatusFailed); + return nullptr; + } + } + else + { + // If we found a normal command, we are done + done = true; + if (!suffix.empty()) + { + switch (suffix[0]) + { + case '/': + // GDB format suffixes + { + Options *command_options = cmd_obj->GetOptions(); + if (command_options && command_options->SupportsLongOption("gdb-format")) + { + std::string gdb_format_option("--gdb-format="); + gdb_format_option += (suffix.c_str() + 1); + + bool inserted = false; + std::string &cmd = revised_command_line.GetString(); + size_t arg_terminator_idx = FindArgumentTerminator(cmd); + if (arg_terminator_idx != std::string::npos) + { + // Insert the gdb format option before the "--" that terminates options + gdb_format_option.append(1,' '); + cmd.insert(arg_terminator_idx, gdb_format_option); + inserted = true; + } + + if (!inserted) + revised_command_line.Printf(" %s", gdb_format_option.c_str()); + + if (wants_raw_input && FindArgumentTerminator(cmd) == std::string::npos) + revised_command_line.PutCString(" --"); + } + else + { + result.AppendErrorWithFormat("the '%s' command doesn't support the --gdb-format option\n", + cmd_obj->GetCommandName()); + result.SetStatus(eReturnStatusFailed); + return nullptr; + } + } + break; + + default: + result.AppendErrorWithFormat("unknown command shorthand suffix: '%s'\n", + suffix.c_str()); + result.SetStatus(eReturnStatusFailed); + return nullptr; + } + } + } + if (scratch_command.empty()) + done = true; + } + + if (!scratch_command.empty()) + revised_command_line.Printf(" %s", scratch_command.c_str()); + + if (cmd_obj != NULL) + command_line = revised_command_line.GetData(); + + return cmd_obj; +} Index: test/functionalities/abbreviation/TestAbbreviations.py =================================================================== --- test/functionalities/abbreviation/TestAbbreviations.py +++ test/functionalities/abbreviation/TestAbbreviations.py @@ -1,5 +1,5 @@ """ -Test some lldb command abbreviations. +Test some lldb command abbreviations and aliases for proper resolution. """ import os, time @@ -13,172 +13,88 @@ mydir = TestBase.compute_mydir(__file__) @expectedFailureFreeBSD("llvm.org/pr22611 thread race condition breaks prompt setting") - @skipIfWindows # one-shot script commands deadlock on Windows. - def test_nonrunning_command_abbreviations (self): - self.expect("ap script", - startstr = "The following built-in commands may relate to 'script':", - substrs = ['breakpoint command add', - 'breakpoint command list', - 'breakpoint list', - 'command alias', - 'expression', - 'script']) - + def test_command_abbreviations_and_aliases (self): + command_interpreter = self.dbg.GetCommandInterpreter() + self.assertTrue(command_interpreter, VALID_COMMAND_INTERPRETER) + result = lldb.SBCommandReturnObject() + + # Check that abbreviations are expanded to the full command. + command_interpreter.ResolveCommand("ap script", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("apropos script", result.GetOutput()) + + command_interpreter.ResolveCommand("h", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("help", result.GetOutput()) + + # Check resolution of abbreviations for multi-word commands. + command_interpreter.ResolveCommand("lo li", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("log list", result.GetOutput()) + + command_interpreter.ResolveCommand("br s", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("breakpoint set", result.GetOutput()) + + # Try an ambiguous abbreviation. + # "pl" could be "platform" or "plugin". + command_interpreter.ResolveCommand("pl", result) + self.assertFalse(result.Succeeded()) + self.assertTrue(result.GetError().startswith("Ambiguous command")) + + # Make sure an unabbreviated command is not mangled. + command_interpreter.ResolveCommand("breakpoint set --name main --line 123", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("breakpoint set --name main --line 123", result.GetOutput()) + + # Create some aliases. self.runCmd("com a alias com al") self.runCmd("alias gurp help") - self.expect("gurp target create", - substrs = ['Syntax: target create ']) - self.runCmd("com u gurp") - self.expect("gurp", - COMMAND_FAILED_AS_EXPECTED, error = True, - substrs = ["error: 'gurp' is not a valid command."]) - # Only one matching command: execute it. - self.expect("h", - startstr = "Debugger commands:") - - # Execute cleanup function during test tear down - def cleanup(): - self.runCmd("command alias t thread select") - self.addTearDownHook(cleanup) - - # Several matching commands: list them and error out. - self.runCmd("command unalias t") - self.expect("t", - COMMAND_FAILED_AS_EXPECTED, error = True, - substrs = ["Ambiguous command 't'. Possible matches:", - "target", "thread", "type"]) - - self.runCmd("com sou ./change_prompt.lldb") - - self.expect("settings show prompt", - startstr = 'prompt (string) = "[with-three-trailing-spaces] "') - - - self.runCmd("settings clear prompt") - self.expect("settings show prompt", - startstr = 'prompt (string) = "(lldb) "') - - - self.expect("lo li", - startstr = "Logging categories for ") - - self.runCmd("se se prompt 'Sycamore> '") - self.expect("se sh prompt", - startstr = 'prompt (string) = "Sycamore> "') - - self.runCmd("se cl prompt") - self.expect("set sh prompt", - startstr = 'prompt (string) = "(lldb) "') - - # We don't want to display the stdout if not in TraceOn() mode. - if not self.TraceOn(): - self.HideStdout() - - self.runCmd (r'''sc print "\n\n\tHello!\n"''') - - - @skipUnlessDarwin - @dsym_test - def test_with_dsym (self): - self.buildDsym () - self.running_abbreviations () - - @dwarf_test - @expectedFailureLinux # not related to abbreviations, "dis -f" output has int3 in it - def test_with_dwarf (self): - self.buildDwarf () - self.running_abbreviations () - - def running_abbreviations (self): - exe = os.path.join (os.getcwd(), "a.out") - # Use "file", i.e., no abbreviation. We're exactly matching the command - # verbatim when dealing with remote testsuite execution. - # For more details, see TestBase.runCmd(). - self.expect("file " + exe, - patterns = [ "Current executable set to .*a.out.*" ]) - - # By default, the setting interpreter.expand-regex-aliases is false. - self.expect("_regexp-br product", matching=False, - substrs = [ "breakpoint set --name" ]) - - match_object = lldbutil.run_break_set_command (self, "br s -n sum") - lldbutil.check_breakpoint_result (self, match_object, symbol_name='sum', symbol_match_exact=False, num_locations=1) - - match_object = lldbutil.run_break_set_command (self, "br s -f main.cpp -l 32") - lldbutil.check_breakpoint_result (self, match_object, file_name='main.cpp', line_number=32, num_locations=1) - - self.runCmd("br co a -s python 1 -o 'print frame'") - self.expect("br co l 1", - substrs = [ "Breakpoint 1:", - "Breakpoint commands:", - "print frame" ]) - - self.runCmd("br co del 1") - self.expect("breakpoint command list 1", - startstr = "Breakpoint 1 does not have an associated command.") - - self.expect("br di", - startstr = 'All breakpoints disabled. (3 breakpoints)') - - self.expect("bre e", - startstr = "All breakpoints enabled. (3 breakpoints)") - - self.expect("break list", - substrs = ["1: name = 'product', locations = 1", - "2: name = 'sum', locations = 1", - "3: file = 'main.cpp', line = 32, locations = 1"]) - self.expect("br cl -l 32 -f main.cpp", - startstr = "1 breakpoints cleared:", - substrs = ["3: file = 'main.cpp', line = 32, locations = 1"]) - - # Add a future to terminate the current process being debugged. - # - # The test framework relies on detecting either "run" or "process launch" - # command to automatically kill the inferior upon tear down. - # But we'll be using "pro la" command to launch the inferior. - self.expect("pro la", - patterns = [ "Process .* launched: "]) - - self.expect("pro st", - patterns = [ "Process .* stopped", - "thread #1:", - "a.out", - "sum\(a=1238, b=78392\)", - "at main.cpp\:25", - "stop reason = breakpoint 2.1" ]) - - # ARCH, if not specified, defaults to x86_64. - self.runCmd("dis -f") - disassembly = self.res.GetOutput() - if self.getArchitecture() in ["", 'x86_64', 'i386']: - # hey! we shouldn't have a software breakpoint in here - self.assertFalse("int3" in disassembly) - self.expect(disassembly, exe=False, - startstr = "a.out`sum(int, int)", - substrs = [' mov', - ' addl ', - 'ret']) - else: - self.fail('unimplemented for arch = "{arch}"'.format(arch=self.getArchitecture())) - - self.expect("i d l main.cpp", - patterns = ["Line table for .*main.cpp in `a.out"]) - - self.expect("i d se", - patterns = ["Dumping sections for [0-9]+ modules."]) - - self.expect("i d symf", - patterns = ["Dumping debug symbols for [0-9]+ modules."]) - - self.expect("i d symt", - patterns = ["Dumping symbol table for [0-9]+ modules."]) - - if self.platformIsDarwin(): - self.expect("i li", - substrs = [ 'a.out', - '/usr/lib/dyld', - '/usr/lib/libSystem.B.dylib']) + # Check that an alias is replaced with the actual command + command_interpreter.ResolveCommand("gurp target create", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("help target create", result.GetOutput()) + + # Delete the alias and make sure it no longer has an effect. + self.runCmd("com u gurp") + command_interpreter.ResolveCommand("gurp", result) + self.assertFalse(result.Succeeded()) + + # Check aliases with text replacement. + self.runCmd("alias pltty process launch -s -o %1 -e %1") + command_interpreter.ResolveCommand("pltty /dev/tty0", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("process launch -s -o /dev/tty0 -e /dev/tty0", result.GetOutput()) + + self.runCmd("alias xyzzy breakpoint set -n %1 -l %2") + command_interpreter.ResolveCommand("xyzzy main 123", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("breakpoint set -n main -l 123", result.GetOutput().strip()) + + # And again, without enough parameters. + command_interpreter.ResolveCommand("xyzzy main", result) + self.assertFalse(result.Succeeded()) + + # Check a command that wants the raw input. + command_interpreter.ResolveCommand(r'''sc print "\n\n\tHello!\n"''', result) + self.assertTrue(result.Succeeded()) + self.assertEqual(r'''script print "\n\n\tHello!\n"''', result.GetOutput()) + + # Prompt changing stuff should be tested, but this doesn't seem like the + # right test to do it in. It has nothing to do with aliases or abbreviations. + #self.runCmd("com sou ./change_prompt.lldb") + #self.expect("settings show prompt", + # startstr = 'prompt (string) = "[with-three-trailing-spaces] "') + #self.runCmd("settings clear prompt") + #self.expect("settings show prompt", + # startstr = 'prompt (string) = "(lldb) "') + #self.runCmd("se se prompt 'Sycamore> '") + #self.expect("se sh prompt", + # startstr = 'prompt (string) = "Sycamore> "') + #self.runCmd("se cl prompt") + #self.expect("set sh prompt", + # startstr = 'prompt (string) = "(lldb) "') if __name__ == '__main__':