diff options
10 files changed, 192 insertions, 55 deletions
diff --git a/lldb/include/lldb/Interpreter/CommandObject.h b/lldb/include/lldb/Interpreter/CommandObject.h index f1f140a8313..66cdf8eae35 100644 --- a/lldb/include/lldb/Interpreter/CommandObject.h +++ b/lldb/include/lldb/Interpreter/CommandObject.h @@ -60,6 +60,12 @@ public: { lldb::CommandArgumentType arg_type; ArgumentRepetitionType arg_repetition; + uint32_t arg_opt_set_association; // This arg might be associated only with some particular option set(s). + CommandArgumentData(): + arg_type(lldb::eArgTypeNone), + arg_repetition(eArgRepeatPlain), + arg_opt_set_association(LLDB_OPT_SET_ALL) // By default, the arg associates to all option sets. + {} }; typedef std::vector<CommandArgumentData> CommandArgumentEntry; // Used to build individual command argument lists @@ -171,8 +177,13 @@ public: static const char * GetArgumentName (lldb::CommandArgumentType arg_type); + // Generates a nicely formatted command args string for help command output. + // By default, all possible args are taken into account, for example, + // '<expr | variable-name>'. This can be refined by passing a second arg + // specifying which option set(s) we are interested, which could then, for + // example, produce either '<expr>' or '<variable-name>'. void - GetFormattedCommandArguments (Stream &str); + GetFormattedCommandArguments (Stream &str, uint32_t opt_set_mask = LLDB_OPT_SET_ALL); bool IsPairType (ArgumentRepetitionType arg_repeat_type); diff --git a/lldb/source/Commands/CommandObjectWatchpoint.cpp b/lldb/source/Commands/CommandObjectWatchpoint.cpp index f72a900dbba..bb715d81d79 100644 --- a/lldb/source/Commands/CommandObjectWatchpoint.cpp +++ b/lldb/source/Commands/CommandObjectWatchpoint.cpp @@ -853,6 +853,70 @@ CommandObjectWatchpointModify::Execute } //------------------------------------------------------------------------- +// CommandObjectWatchpointSet::CommandOptions +//------------------------------------------------------------------------- +#pragma mark Set::CommandOptions + +CommandObjectWatchpointSet::CommandOptions::CommandOptions() : + OptionGroup() +{ +} + +CommandObjectWatchpointSet::CommandOptions::~CommandOptions () +{ +} + +OptionDefinition +CommandObjectWatchpointSet::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, true, "expression", 'e', no_argument, NULL, NULL, eArgTypeNone, "Watch an address with an expression specified at the end."}, +{ LLDB_OPT_SET_2, true, "variable", 'v', no_argument, NULL, NULL, eArgTypeNone, "Watch a variable name specified at the end."} +}; + +uint32_t +CommandObjectWatchpointSet::CommandOptions::GetNumDefinitions () +{ + return sizeof(g_option_table)/sizeof(OptionDefinition); +} + +const OptionDefinition* +CommandObjectWatchpointSet::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + +Error +CommandObjectWatchpointSet::CommandOptions::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + char short_option = (char) g_option_table[option_idx].short_option; + + switch (short_option) + { + case 'e': + m_do_expression = true; + break; + case 'v': + m_do_variable = true; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; +} + +void +CommandObjectWatchpointSet::CommandOptions::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_do_expression = false; + m_do_variable = false; +} + +//------------------------------------------------------------------------- // CommandObjectWatchpointSet //------------------------------------------------------------------------- #pragma mark Set @@ -861,45 +925,51 @@ CommandObjectWatchpointSet::CommandObjectWatchpointSet (CommandInterpreter &inte CommandObject (interpreter, "watchpoint set", "Set a watchpoint. " - "You can choose to watch a variable in scope with just the '-w' option. " - "If you use the '-x' option to specify the byte size, it is implied " - "that the remaining string is evaluated as an expression with the result " - "interpreted as an address to watch for, i.e., the pointee is watched. " + "You can choose to watch a variable in scope with the '-v' option " + "or to watch an address with the '-e' option by supplying an expression. " + "Use the '-w' option to specify the type of watchpoint and " + "the '-x' option to specify the byte size to watch for. " "If no '-w' option is specified, it defaults to read_write. " "Note that hardware resources for watching are often limited.", NULL, eFlagProcessMustBeLaunched | eFlagProcessMustBePaused), m_option_group (interpreter), - m_option_watchpoint() + m_option_watchpoint (), + m_command_options () { SetHelpLong( "Examples: \n\ \n\ - watchpoint set -w read_wriate my_global_var \n\ - # Watch my_global_var for read/write access.\n\ + watchpoint set -w read_wriate -v my_global_var \n\ + # Watch my_global_var for read/write access, with the region to watch corresponding to the byte size of the data type.\n\ \n\ - watchpoint set -w write -x 1 foo + 32\n\ - # Watch write access for the 1-byte region pointed to by the address 'foo + 32'.\n"); + watchpoint set -w write -x 1 -e foo + 32\n\ + # Watch write access for the 1-byte region pointed to by the address 'foo + 32'.\n\ + # If no '-x' option is specified, byte size defaults to 4.\n"); CommandArgumentEntry arg; CommandArgumentData var_name_arg, expression_arg; // Define the first variant of this arg. - var_name_arg.arg_type = eArgTypeVarName; - var_name_arg.arg_repetition = eArgRepeatPlain; - - // Define the second variant of this arg. expression_arg.arg_type = eArgTypeExpression; expression_arg.arg_repetition = eArgRepeatPlain; + expression_arg.arg_opt_set_association = LLDB_OPT_SET_1; + // Define the second variant of this arg. + var_name_arg.arg_type = eArgTypeVarName; + var_name_arg.arg_repetition = eArgRepeatPlain; + var_name_arg.arg_opt_set_association = LLDB_OPT_SET_2; + // Push the two variants into the argument entry. - arg.push_back (var_name_arg); arg.push_back (expression_arg); + arg.push_back (var_name_arg); - // Push the data for the first argument into the m_arguments vector. + // Push the data for the only argument into the m_arguments vector. m_arguments.push_back (arg); - - m_option_group.Append (&m_option_watchpoint, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + + // Absorb the '-w' and '-x' options. + m_option_group.Append (&m_option_watchpoint, LLDB_OPT_SET_1, LLDB_OPT_SET_1|LLDB_OPT_SET_2); + m_option_group.Append (&m_command_options); m_option_group.Finalize(); } @@ -930,35 +1000,25 @@ CommandObjectWatchpointSet::Execute return false; } - // Be careful about the stack frame, if any summary formatter runs code, it might clear the StackFrameList - // for the thread. So hold onto a shared pointer to the frame so it stays alive. - bool get_file_globals = true; - VariableList *variable_list = frame->GetVariableList (get_file_globals); + // If no argument is present, issue an error message. There's no way to set a watchpoint. + if (command.GetArgumentCount() <= 0) + { + result.GetErrorStream().Printf("error: required argument missing; specify your program variable ('-v') or an address ('-e') to watch for\n"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + // It's either '-e' to watch an address with expression' or '-v' to watch a variable. + bool watch_address = m_command_options.m_do_expression; - bool watch_address = (m_option_watchpoint.watch_size > 0); - // If no '-w' is specified, default to '-w read_write'. if (!m_option_watchpoint.watch_variable) { m_option_watchpoint.watch_variable = true; m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchReadWrite; } - // It's possible to specify an address to watch for with the '-x' option. - if (!variable_list && !watch_address) - { - result.GetErrorStream().Printf("error: no variables found, did you forget to use '-x' option to watch an address?\n"); - result.SetStatus(eReturnStatusFailed); - return false; - } - // If thre's no argument, it is an error. - if (command.GetArgumentCount() <= 0) - { - result.GetErrorStream().Printf("error: specify your target variable (no '-x') or expression (with '-x') to watch for\n"); - result.SetStatus(eReturnStatusFailed); - return false; - } - // We passed the sanity check for the options. + // We passed the sanity check for the command. // Proceed to set the watchpoint now. lldb::addr_t addr = 0; size_t size = 0; @@ -968,6 +1028,7 @@ CommandObjectWatchpointSet::Execute Stream &output_stream = result.GetOutputStream(); if (watch_address) { + // Use expression evaluation to arrive at the address to watch. std::string expr_str; command.GetQuotedCommandString(expr_str); const bool coerce_to_id = true; @@ -983,6 +1044,7 @@ CommandObjectWatchpointSet::Execute valobj_sp); if (expr_result != eExecutionCompleted) { result.GetErrorStream().Printf("error: expression evaluation of address to watch failed\n"); + result.GetErrorStream().Printf("expression evaluated: %s\n", expr_str.c_str()); result.SetStatus(eReturnStatusFailed); } @@ -993,11 +1055,12 @@ CommandObjectWatchpointSet::Execute result.SetStatus(eReturnStatusFailed); return false; } - size = m_option_watchpoint.watch_size; + size = m_option_watchpoint.watch_size == 0 ? 4 /* Could use a better default size? */ + : m_option_watchpoint.watch_size; } else { // A simple watch variable gesture allows only one argument. - if (m_option_watchpoint.watch_size == 0 && command.GetArgumentCount() != 1) { - result.GetErrorStream().Printf("error: specify exactly one variable with the '-w' option, i.e., no '-x'\n"); + if (command.GetArgumentCount() != 1) { + result.GetErrorStream().Printf("error: specify exactly one variable with the '-v' option\n"); result.SetStatus(eReturnStatusFailed); return false; } @@ -1016,7 +1079,8 @@ CommandObjectWatchpointSet::Execute if (addr_type == eAddressTypeLoad) { // We're in business. // Find out the size of this variable. - size = valobj_sp->GetByteSize(); + size = m_option_watchpoint.watch_size == 0 ? valobj_sp->GetByteSize() + : m_option_watchpoint.watch_size; } } else { const char *error_cstr = error.AsCString(NULL); @@ -1045,7 +1109,8 @@ CommandObjectWatchpointSet::Execute output_stream.EOL(); result.SetStatus(eReturnStatusSuccessFinishResult); } else { - result.AppendErrorWithFormat("Watchpoint creation failed.\n"); + result.AppendErrorWithFormat("Watchpoint creation failed (addr=0x%llx, size=%lu).\n", + addr, size); result.SetStatus(eReturnStatusFailed); } diff --git a/lldb/source/Commands/CommandObjectWatchpoint.h b/lldb/source/Commands/CommandObjectWatchpoint.h index c61ebf44d3a..ff8d613abd2 100644 --- a/lldb/source/Commands/CommandObjectWatchpoint.h +++ b/lldb/source/Commands/CommandObjectWatchpoint.h @@ -251,6 +251,36 @@ class CommandObjectWatchpointSet : public CommandObject { public: + class CommandOptions : public OptionGroup + { + public: + + CommandOptions (); + + virtual + ~CommandOptions (); + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + bool m_do_expression; + bool m_do_variable; + }; + CommandObjectWatchpointSet (CommandInterpreter &interpreter); virtual @@ -266,6 +296,7 @@ public: private: OptionGroupOptions m_option_group; OptionGroupWatchpoint m_option_watchpoint; + CommandOptions m_command_options; }; } // namespace lldb_private diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp index 7624e860691..5618b6ccc3f 100644 --- a/lldb/source/Interpreter/CommandObject.cpp +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -535,15 +535,30 @@ CommandObject::IsPairType (ArgumentRepetitionType arg_repeat_type) return false; } +static CommandObject::CommandArgumentEntry +OptSetFiltered(uint32_t opt_set_mask, CommandObject::CommandArgumentEntry &cmd_arg_entry) +{ + CommandObject::CommandArgumentEntry ret_val; + for (unsigned i = 0; i < cmd_arg_entry.size(); ++i) + if (opt_set_mask & cmd_arg_entry[i].arg_opt_set_association) + ret_val.push_back(cmd_arg_entry[i]); + return ret_val; +} + +// Default parameter value of opt_set_mask is LLDB_OPT_SET_ALL, which means take +// all the argument data into account. On rare cases where some argument sticks +// with certain option sets, this function returns the option set filtered args. void -CommandObject::GetFormattedCommandArguments (Stream &str) +CommandObject::GetFormattedCommandArguments (Stream &str, uint32_t opt_set_mask) { int num_args = m_arguments.size(); for (int i = 0; i < num_args; ++i) { if (i > 0) str.Printf (" "); - CommandArgumentEntry arg_entry = m_arguments[i]; + CommandArgumentEntry arg_entry = + opt_set_mask == LLDB_OPT_SET_ALL ? m_arguments[i] + : OptSetFiltered(opt_set_mask, m_arguments[i]); int num_alternatives = arg_entry.size(); if ((num_alternatives == 2) diff --git a/lldb/source/Interpreter/OptionGroupWatchpoint.cpp b/lldb/source/Interpreter/OptionGroupWatchpoint.cpp index 159d00eea7f..4a6afb44c48 100644 --- a/lldb/source/Interpreter/OptionGroupWatchpoint.cpp +++ b/lldb/source/Interpreter/OptionGroupWatchpoint.cpp @@ -40,8 +40,8 @@ static OptionEnumValueElement g_watch_size[] = static OptionDefinition g_option_table[] = { - { LLDB_OPT_SET_1, false, "watch", 'w', required_argument, g_watch_type, 0, eArgTypeWatchType, "Determine how to watch a variable; or, with -x option, its pointee."}, - { LLDB_OPT_SET_1, false, "xsize", 'x', required_argument, g_watch_size, 0, eArgTypeByteSize, "Number of bytes to use to watch the pointee."} + { LLDB_OPT_SET_1, false, "watch", 'w', required_argument, g_watch_type, 0, eArgTypeWatchType, "Specify the type of watching to perform."}, + { LLDB_OPT_SET_1, false, "xsize", 'x', required_argument, g_watch_size, 0, eArgTypeByteSize, "Number of bytes to use to watch a region."} }; diff --git a/lldb/source/Interpreter/Options.cpp b/lldb/source/Interpreter/Options.cpp index 09b17a8eb25..daf631f3b05 100644 --- a/lldb/source/Interpreter/Options.cpp +++ b/lldb/source/Interpreter/Options.cpp @@ -437,6 +437,10 @@ Options::GenerateOptionUsage strm.Printf ("\n"); strm.Indent (name); + // Different option sets may require different args. + StreamString args_str; + cmd->GetFormattedCommandArguments(args_str, opt_set_mask); + // First go through and print all options that take no arguments as // a single string. If a command has "-a" "-b" and "-c", this will show // up as [-abc] @@ -556,12 +560,12 @@ Options::GenerateOptionUsage } } - if (arguments_str.GetSize() > 0) + if (args_str.GetSize() > 0) { if (cmd->WantsRawCommandString()) strm.Printf(" --"); - strm.Printf (" %s", arguments_str.GetData()); + strm.Printf (" %s", args_str.GetData()); } } diff --git a/lldb/test/functionalities/watchpoint/hello_watchpoint/TestMyFirstWatchpoint.py b/lldb/test/functionalities/watchpoint/hello_watchpoint/TestMyFirstWatchpoint.py index 30ba801b318..cfc5f60e16e 100644 --- a/lldb/test/functionalities/watchpoint/hello_watchpoint/TestMyFirstWatchpoint.py +++ b/lldb/test/functionalities/watchpoint/hello_watchpoint/TestMyFirstWatchpoint.py @@ -75,7 +75,7 @@ class HelloWatchpointTestCase(TestBase): substrs = ['Watchpoint created', 'size = 4', 'type = w', '%s:%d' % (self.source, self.decl)]) else: - self.expect("watchpoint set -w write global", WATCHPOINT_CREATED, + self.expect("watchpoint set -w write -v global", WATCHPOINT_CREATED, substrs = ['Watchpoint created', 'size = 4', 'type = w', '%s:%d' % (self.source, self.decl)]) diff --git a/lldb/test/functionalities/watchpoint/watchpoint_set_command/TestWatchLocationWithWatchSet.py b/lldb/test/functionalities/watchpoint/watchpoint_set_command/TestWatchLocationWithWatchSet.py index de9016f5cb3..74fc12a481e 100644 --- a/lldb/test/functionalities/watchpoint/watchpoint_set_command/TestWatchLocationWithWatchSet.py +++ b/lldb/test/functionalities/watchpoint/watchpoint_set_command/TestWatchLocationWithWatchSet.py @@ -60,7 +60,7 @@ class WatchLocationUsingWatchpointSetTestCase(TestBase): # with offset as 7. # The main.cpp, by design, misbehaves by not following the agreed upon # protocol of only accessing the allowable index range of [0, 6]. - self.expect("watchpoint set -w write -x 1 g_char_ptr + 7", WATCHPOINT_CREATED, + self.expect("watchpoint set -w write -x 1 -e g_char_ptr + 7", WATCHPOINT_CREATED, substrs = ['Watchpoint created', 'size = 1', 'type = w']) self.runCmd("expr unsigned val = g_char_ptr[7]; val") self.expect(self.res.GetOutput().splitlines()[0], exe=False, diff --git a/lldb/test/functionalities/watchpoint/watchpoint_set_command/TestWatchpointSetErrorCases.py b/lldb/test/functionalities/watchpoint/watchpoint_set_command/TestWatchpointSetErrorCases.py index 3ff21478460..299e2aec9b6 100644 --- a/lldb/test/functionalities/watchpoint/watchpoint_set_command/TestWatchpointSetErrorCases.py +++ b/lldb/test/functionalities/watchpoint/watchpoint_set_command/TestWatchpointSetErrorCases.py @@ -51,9 +51,13 @@ class WatchpointSetErrorTestCase(TestBase): # No argument is an error. self.expect("watchpoint set", error=True, - substrs = ['specify your target variable', - 'or expression', - 'to watch for']) + startstr = 'error: invalid combination of options for the given command') + self.runCmd("watchpoint set -v -w read_write", check=False) + + # 'watchpoint set' now takes a mandatory '-v' or '-e' option to + # indicate watching for either variable or address. + self.expect("watchpoint set -w write global", error=True, + startstr = 'error: invalid combination of options for the given command') # Wrong size parameter is an error. self.expect("watchpoint set -x -128", error=True, diff --git a/lldb/test/help/TestHelp.py b/lldb/test/help/TestHelp.py index 95bf8167a74..4fe0c77e246 100644 --- a/lldb/test/help/TestHelp.py +++ b/lldb/test/help/TestHelp.py @@ -121,6 +121,13 @@ class HelpCommandTestCase(TestBase): self.expect("help watchpt-id-list", substrs = ['<watchpt-id-list>']) + def test_help_watchpoint_set(self): + """Test that 'help watchpoint set' prints out <expr> for the '-e' option + and <variable-name> for the '-v' option.""" + self.expect("help watchpoint set", + patterns = ['watchpoint set -e.*<expr>', + 'watchpoint set -v.*<variable-name>']) + if __name__ == '__main__': import atexit |