Index: lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/scripted_bkpt/TestScriptedResolver.py =================================================================== --- lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/scripted_bkpt/TestScriptedResolver.py +++ lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/scripted_bkpt/TestScriptedResolver.py @@ -38,6 +38,12 @@ self.build() self.do_test_cli() + def test_bad_command_lines(self): + """Make sure we get appropriate errors when we give invalid key/value + options""" + self.build() + self.do_test_bad_options() + def setUp(self): # Call super's setUp(). TestBase.setUp(self) @@ -195,3 +201,15 @@ target = self.make_target_and_import() lldbutil.run_break_set_by_script(self, "resolver.Resolver", extra_options="-k symbol -v break_on_me") + + def do_test_bad_options(self): + target = self.make_target_and_import() + + self.expect("break set -P resolver.Resolver -k a_key", error = True, msg="Missing value at end", + substrs=['Key: "a_key" missing value']) + self.expect("break set -P resolver.Resolver -v a_value", error = True, msg="Missing key at end", + substrs=['Value: "a_value" missing matching key']) + self.expect("break set -P resolver.Resolver -v a_value -k a_key -v another_value", error = True, msg="Missing key among args", + substrs=['Value: "a_value" missing matching key']) + self.expect("break set -P resolver.Resolver -k a_key -k a_key -v another_value", error = True, msg="Missing value among args", + substrs=['Key: "a_key" missing value']) Index: lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/scripted_bkpt/resolver.py =================================================================== --- lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/scripted_bkpt/resolver.py +++ lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/scripted_bkpt/resolver.py @@ -7,6 +7,7 @@ def __init__(self, bkpt, extra_args, dict): self.bkpt = bkpt self.extra_args = extra_args + Resolver.func_list = [] Resolver.got_files = 0 @@ -15,6 +16,8 @@ sym_item = self.extra_args.GetValueForKey("symbol") if sym_item.IsValid(): sym_name = sym_item.GetStringValue(1000) + else: + print("Didn't have a value for key 'symbol'") if sym_ctx.compile_unit.IsValid(): Resolver.got_files = 1 Index: lldb/source/Commands/CommandObjectBreakpoint.cpp =================================================================== --- lldb/source/Commands/CommandObjectBreakpoint.cpp +++ lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -16,6 +16,7 @@ #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h" #include "lldb/Interpreter/OptionValueBoolean.h" #include "lldb/Interpreter/OptionValueString.h" #include "lldb/Interpreter/OptionValueUInt64.h" @@ -241,12 +242,16 @@ interpreter, "breakpoint set", "Sets a breakpoint or set of breakpoints in the executable.", "breakpoint set "), + m_python_class_options("scripted breakpoint", 'P'), m_bp_opts(), m_options() { // We're picking up all the normal options, commands and disable. + m_all_options.Append(&m_python_class_options, LLDB_OPT_SET_1, + LLDB_OPT_SET_11); m_all_options.Append(&m_bp_opts, LLDB_OPT_SET_1 | LLDB_OPT_SET_3 | LLDB_OPT_SET_4, LLDB_OPT_SET_ALL); - m_all_options.Append(&m_dummy_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL); + m_all_options.Append(&m_dummy_options, LLDB_OPT_SET_1, + LLDB_OPT_SET_ALL); m_all_options.Append(&m_options); m_all_options.Finalize(); } @@ -352,14 +357,6 @@ m_hardware = true; break; - case 'k': { - if (m_current_key.empty()) - m_current_key.assign(option_arg); - else - error.SetErrorStringWithFormat("Key: %s missing value.", - m_current_key.c_str()); - - } break; case 'K': { bool success; bool value; @@ -441,10 +438,6 @@ m_source_text_regexp.assign(option_arg); break; - case 'P': - m_python_class.assign(option_arg); - break; - case 'r': m_func_regexp.assign(option_arg); break; @@ -458,16 +451,6 @@ m_func_name_type_mask |= eFunctionNameTypeSelector; break; - case 'v': { - if (!m_current_key.empty()) { - m_extra_args_sp->AddStringItem(m_current_key, option_arg); - m_current_key.clear(); - } - else - error.SetErrorStringWithFormat("Value \"%s\" missing matching key.", - option_arg.str().c_str()); - } break; - case 'w': { bool success; m_throw_bp = OptionArgParser::ToBoolean(option_arg, true, &success); @@ -510,8 +493,6 @@ m_exception_extra_args.Clear(); m_move_to_nearest_code = eLazyBoolCalculate; m_source_regex_func_names.clear(); - m_python_class.clear(); - m_extra_args_sp = std::make_shared(); m_current_key.clear(); } @@ -543,8 +524,6 @@ Args m_exception_extra_args; LazyBool m_move_to_nearest_code; std::unordered_set m_source_regex_func_names; - std::string m_python_class; - StructuredData::DictionarySP m_extra_args_sp; std::string m_current_key; }; @@ -565,7 +544,7 @@ BreakpointSetType break_type = eSetTypeInvalid; - if (!m_options.m_python_class.empty()) + if (!m_python_class_options.GetClassName().empty()) break_type = eSetTypeScripted; else if (m_options.m_line_num != 0) break_type = eSetTypeFileAndLine; @@ -721,9 +700,9 @@ Status error; bp_sp = target.CreateScriptedBreakpoint( - m_options.m_python_class, &(m_options.m_modules), + m_python_class_options.GetClassName().c_str(), &(m_options.m_modules), &(m_options.m_filenames), false, m_options.m_hardware, - m_options.m_extra_args_sp, &error); + m_python_class_options.GetStructuredData(), &error); if (error.Fail()) { result.AppendErrorWithFormat( "Error setting extra exception arguments: %s", @@ -818,6 +797,7 @@ BreakpointOptionGroup m_bp_opts; BreakpointDummyOptionGroup m_dummy_options; + OptionGroupPythonClassWithDict m_python_class_options; CommandOptions m_options; OptionGroupOptions m_all_options; }; Index: lldb/source/Commands/Options.td =================================================================== --- lldb/source/Commands/Options.td +++ lldb/source/Commands/Options.td @@ -163,17 +163,6 @@ "match against all source files, pass the \"--all-files\" option.">; def breakpoint_set_all_files : Option<"all-files", "A">, Group<9>, Desc<"All files are searched for source pattern matches.">; - def breakpoint_set_python_class : Option<"python-class", "P">, Group<11>, - Arg<"PythonClass">, Required, Desc<"The name of the class that implement " - "a scripted breakpoint.">; - def breakpoint_set_python_class_key : Option<"python-class-key", "k">, - Group<11>, Arg<"None">, - Desc<"The key for a key/value pair passed to the class that implements a " - "scripted breakpoint. Can be specified more than once.">; - def breakpoint_set_python_class_value : Option<"python-class-value", "v">, - Group<11>, Arg<"None">, Desc<"The value for the previous key in the pair " - "passed to the class that implements a scripted breakpoint. " - "Can be specified more than once.">; def breakpoint_set_language_exception : Option<"language-exception", "E">, Group<10>, Arg<"Language">, Required, Desc<"Set the breakpoint on exceptions thrown by the specified language " Index: lldb/source/Interpreter/CMakeLists.txt =================================================================== --- lldb/source/Interpreter/CMakeLists.txt +++ lldb/source/Interpreter/CMakeLists.txt @@ -20,6 +20,7 @@ OptionGroupBoolean.cpp OptionGroupFile.cpp OptionGroupFormat.cpp + OptionGroupPythonClassWithDict.cpp OptionGroupOutputFile.cpp OptionGroupPlatform.cpp OptionGroupString.cpp Index: lldb/source/Interpreter/Options.cpp =================================================================== --- lldb/source/Interpreter/Options.cpp +++ lldb/source/Interpreter/Options.cpp @@ -1380,6 +1380,10 @@ ? nullptr : OptionParser::GetOptionArgument(), execution_context); + // If the Option setting returned an error, we should stop parsing + // and return the error. + if (error.Fail()) + break; } else { error.SetErrorStringWithFormat("invalid option with value '%i'", val); }