Skip to content

Commit 1eef550

Browse files
jiminghamHoney Goyal
authored andcommitted
Add a breakpoint add command to fix the option-madness that is breakpoint set (llvm#156067)
Someone came up with a clever idea for a new breakpoint type, but we couldn't figure out how to add it in an ergonomic way because `breakpoint set` has used up all the short-option characters. And even if they did find a way to add it, the help for `break set` is so confusing - because of the way it is implemented - that very few people would detect the addition. The basic problem is that `break set` distinguishes amongst the fundamental breakpoint types it offers by which "required option" you provide. If you pass a `-a` you are setting an address breakpoint, if `-n`, `-F`, etc. a symbol name based breakpoint. And so forth. That is however pretty hard to discern from the option grouping printing from `help break set`. `break set` also suffers from the problem that it uses common options in different ways depending on which "required" option is present, which makes documenting the various behaviors difficult. And as we run out of single letters it makes extending it difficult to impossible. This PR fixes that situation by adding a new command for adding breakpoints - `break add`. The new command specifies the "breakpoint types" as subcommands of `break add` rather than distinguishing them by their being one of the "required" options the way `break set` does. That both makes it much clearer what the breakpoint types actually are, and means that the option set can be dedicated to that particular breakpoint type, and so the help for each is less cluttered, and can be documented properly for each usage. Instead of trying to parse the meaning of: ``` (lldb) help break set Sets a breakpoint or set of breakpoints in the executable. Syntax: breakpoint set <cmd-options> Command Options Usage: breakpoint set [-DHd] -l <linenum> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-u <column>] [-f <filename>] [-m <boolean>] [-s <shlib-name>] [-K <boolean>] breakpoint set [-DHd] -a <address-expression> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-N <breakpoint-name>] [-s <shlib-name>] breakpoint set [-DHd] -n <function-name> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-f <filename>] [-L <source-language>] [-s <shlib-name>] [-K <boolean>] breakpoint set [-DHd] -F <fullname> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-f <filename>] [-L <source-language>] [-s <shlib-name>] [-K <boolean>] breakpoint set [-DHd] -S <selector> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-f <filename>] [-L <source-language>] [-s <shlib-name>] [-K <boolean>] breakpoint set [-DHd] -M <method> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-f <filename>] [-L <source-language>] [-s <shlib-name>] [-K <boolean>] breakpoint set [-DHd] -r <regular-expression> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-f <filename>] [-L <source-language>] [-s <shlib-name>] [-K <boolean>] breakpoint set [-DHd] -b <function-name> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-f <filename>] [-L <source-language>] [-s <shlib-name>] [-K <boolean>] breakpoint set [-ADHd] -p <regular-expression> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-N <breakpoint-name>] [-f <filename>] [-m <boolean>] [-s <shlib-name>] [-X <function-name>] breakpoint set [-DHd] -E <source-language> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-N <breakpoint-name>] [-h <boolean>] [-w <boolean>] breakpoint set [-DHd] -P <python-class> [-k <none>] [-v <none>] [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-N <breakpoint-name>] [-f <filename>] [-s <shlib-name>] breakpoint set [-DHd] -y <linespec> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-m <boolean>] [-s <shlib-name>] [-K <boolean>] ``` We instead offer: ``` (lldb) help break add Commands to add breakpoints of various types Syntax: breakpoint add Access the breakpoint search kernels built into lldb. Along with specifying the search kernel, each breakpoint add operation can specify a common set of "reaction" options for each breakpoint. The reaction options can also be modified after breakpoint creation using the "breakpoint modify" command. The following subcommands are supported: address -- Add breakpoints by raw address exception -- Add breakpoints on language exceptions. If no language is specified, break on exceptions for all supported languages file -- Add breakpoints on lines in specified source files name -- Add breakpoints matching function or symbol names pattern -- Add breakpoints matching patterns in the source text Expects 'raw' input (see 'help raw-input'.) scripted -- Add breakpoints using a scripted breakpoint resolver. For more help on any particular subcommand, type 'help <command> <subcommand>'. ``` The individual subcommand helps are also easier to read. They are still a little too verbose because they all repeat the options for the `reactions`. A general fix for our help system would be when a command incorporates an OptionGroup whole into the command options, help would show the option group name - which you could separately look up. But even without that: ``` (lldb) help br a a Add breakpoints by raw address Syntax: breakpoint add address <cmd-options> <address> [<address> [...]] Command Options Usage: breakpoint add address [-DHde] [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-N <breakpoint-name>] [-s <shlib-name>] <address> [<address> [...]] -C <command> ( --command <command> ) A command to run when the breakpoint is hit, can be provided more than once, the commands will be run in left-to-right order. -D ( --dummy-breakpoints ) Act on Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets. -G <boolean> ( --auto-continue <boolean> ) The breakpoint will auto-continue after running its commands. -H ( --hardware ) Require the breakpoint to use hardware breakpoints. -N <breakpoint-name> ( --breakpoint-name <breakpoint-name> ) Adds this name to the list of names for this breakpoint. Can be specified more than once. -T <thread-name> ( --thread-name <thread-name> ) The breakpoint stops only for the thread whose thread name matches this argument. -Y <source-language> ( --condition-language <source-language> ) Specifies the Language to use when executing the breakpoint's condition expression. -c <expr> ( --condition <expr> ) The breakpoint stops only if this condition expression evaluates to true. -d ( --disable ) Disable the breakpoint. -e ( --enable ) Enable the breakpoint. -i <count> ( --ignore-count <count> ) Set the number of times this breakpoint is skipped before stopping. -o <boolean> ( --one-shot <boolean> ) The breakpoint is deleted the first time it stop causes a stop. -q <queue-name> ( --queue-name <queue-name> ) The breakpoint stops only for threads in the queue whose name is given by this argument. -s <shlib-name> ( --shlib <shlib-name> ) Set the breakpoint at an address relative to sections in this shared library. -t <thread-id> ( --thread-id <thread-id> ) The breakpoint stops only for the thread whose TID matches this argument. The token 'current' resolves to the current thread's ID. -x <thread-index> ( --thread-index <thread-index> ) The breakpoint stops only for the thread whose index matches this argument. This command takes options and free-form arguments. If your arguments resemble option specifiers (i.e., they start with a - or --), you must use ' -- ' between the end of the command options and the beginning of the arguments. ``` is pretty readable.
1 parent c4fda42 commit 1eef550

File tree

13 files changed

+1613
-149
lines changed

13 files changed

+1613
-149
lines changed

lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,27 @@ static constexpr OptionEnumValueElement g_running_mode[] = {
154154
"Run only this thread while stepping"},
155155
};
156156

157+
static constexpr OptionEnumValueElement g_exception_stage[] = {
158+
{lldb::eExceptionStageThrow, "throw", "Stop when the exception is thrown."},
159+
{lldb::eExceptionStageReThrow, "re-throw",
160+
"Stop when the exception is re-thrown."},
161+
{lldb::eExceptionStageCatch, "catch", "Stop when the exception is caught."},
162+
};
163+
164+
static constexpr OptionEnumValueElement g_name_match_style[] = {
165+
{lldb::eNameMatchStyleAuto, "auto",
166+
"Match against the leaf nodes of the identifier, or against methods or "
167+
"selectors."},
168+
{lldb::eNameMatchStyleFull, "full", "Match the full identifier name."},
169+
{lldb::eNameMatchStyleBase, "base",
170+
"Match against the leaf node of the identifier."},
171+
{lldb::eNameMatchStyleMethod, "method", "Match only against method names."},
172+
{lldb::eNameMatchStyleSelector, "selector",
173+
"Match only against selector names."},
174+
{lldb::eNameMatchStyleRegex, "regex",
175+
"Match the identifier using a regular expression."},
176+
};
177+
157178
static constexpr OptionEnumValueElement g_completion_type[] = {
158179
{lldb::eNoCompletion, "none", "No completion."},
159180
{lldb::eSourceFileCompletion, "source-file", "Completes to a source file."},
@@ -316,6 +337,8 @@ static constexpr CommandObject::ArgumentTableEntry g_argument_table[] = {
316337
{ lldb::eArgTypeCPUFeatures, "cpu-features", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "The CPU feature string." },
317338
{ lldb::eArgTypeManagedPlugin, "managed-plugin", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "Plugins managed by the PluginManager" },
318339
{ lldb::eArgTypeProtocol, "protocol", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "The name of the protocol." },
340+
{ lldb::eArgTypeExceptionStage, "exception-stage", lldb::CompletionType::eNoCompletion, g_exception_stage, { nullptr, false }, "Specify at which stage of the exception raise to stop." },
341+
{ lldb::eArgTypeNameMatchStyle, "match-style", lldb::CompletionType::eNoCompletion, g_name_match_style, { nullptr, false }, "Specify the kind of match to use when looking up names." },
319342
// clang-format on
320343
};
321344

lldb/include/lldb/Interpreter/OptionValueFileColonLine.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ class OptionValueFileColonLine :
4141
m_column_number = LLDB_INVALID_COLUMN_NUMBER;
4242
}
4343

44+
void SetFile(const FileSpec &file_spec) { m_file_spec = file_spec; }
45+
void SetLine(uint32_t line) { m_line_number = line; }
46+
void SetColumn(uint32_t column) { m_column_number = column; }
47+
4448
void AutoComplete(CommandInterpreter &interpreter,
4549
CompletionRequest &request) override;
4650

lldb/include/lldb/Target/Language.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ class Language : public PluginInterface {
165165
static Language *FindPlugin(lldb::LanguageType language,
166166
llvm::StringRef file_path);
167167

168+
static llvm::Expected<lldb::LanguageType>
169+
GetExceptionLanguageForLanguage(llvm::StringRef lang_name);
168170
// return false from callback to stop iterating
169171
static void ForEach(llvm::function_ref<IterationAction(Language *)> callback);
170172

lldb/include/lldb/lldb-enumerations.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,8 @@ enum CommandArgumentType {
671671
eArgTypeCPUFeatures,
672672
eArgTypeManagedPlugin,
673673
eArgTypeProtocol,
674+
eArgTypeExceptionStage,
675+
eArgTypeNameMatchStyle,
674676
eArgTypeLastArg // Always keep this entry as the last entry in this
675677
// enumeration!!
676678
};
@@ -1402,6 +1404,22 @@ enum StopDisassemblyType {
14021404
eStopDisassemblyTypeAlways
14031405
};
14041406

1407+
enum ExceptionStage {
1408+
eExceptionStageCreate = (1 << 0),
1409+
eExceptionStageThrow = (1 << 1),
1410+
eExceptionStageReThrow = (1 << 2),
1411+
eExceptionStageCatch = (1 << 3)
1412+
};
1413+
1414+
enum NameMatchStyle {
1415+
eNameMatchStyleAuto = eFunctionNameTypeAuto,
1416+
eNameMatchStyleFull = eFunctionNameTypeFull,
1417+
eNameMatchStyleBase = eFunctionNameTypeBase,
1418+
eNameMatchStyleMethod = eFunctionNameTypeMethod,
1419+
eNameMatchStyleSelector = eFunctionNameTypeSelector,
1420+
eNameMatchStyleRegex = eFunctionNameTypeSelector << 1
1421+
};
1422+
14051423
} // namespace lldb
14061424

14071425
#endif // LLDB_LLDB_ENUMERATIONS_H

lldb/packages/Python/lldbsuite/test/lldbutil.py

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -319,21 +319,34 @@ def sort_stopped_threads(
319319
# Utility functions for setting breakpoints
320320
# ==================================================
321321

322+
g_use_break_add = True
323+
324+
325+
def set_use_break_add(use_it):
326+
global g_use_break_add
327+
g_use_break_add = use_it
328+
329+
330+
def get_use_break_add():
331+
global g_use_break_add
332+
return g_use_break_add
322333

323334
def run_break_set_by_script(
324335
test, class_name, extra_options=None, num_expected_locations=1
325336
):
326337
"""Set a scripted breakpoint. Check that it got the right number of locations."""
327338
test.assertTrue(class_name is not None, "Must pass in a class name.")
328-
command = "breakpoint set -P " + class_name
339+
if get_use_break_add():
340+
command = f"breakpoint add scripted -P {class_name}"
341+
else:
342+
command = "breakpoint set -P " + class_name
329343
if extra_options is not None:
330344
command += " " + extra_options
331345

332346
break_results = run_break_set_command(test, command)
333347
check_breakpoint_result(test, break_results, num_locations=num_expected_locations)
334348
return get_bpno_from_match(break_results)
335349

336-
337350
def run_break_set_by_file_and_line(
338351
test,
339352
file_name,
@@ -353,10 +366,16 @@ def run_break_set_by_file_and_line(
353366
If loc_exact is true, we check that there is one location, and that location must be at the input file and line number.
354367
"""
355368

356-
if file_name is None:
357-
command = "breakpoint set -l %d" % (line_number)
369+
if get_use_break_add():
370+
if file_name is None:
371+
command = f"breakpoint add file {line_number} "
372+
else:
373+
command = f"breakpoint add file -f {file_name} -l {line_number} "
358374
else:
359-
command = 'breakpoint set -f "%s" -l %d' % (file_name, line_number)
375+
if file_name is None:
376+
command = "breakpoint set -l %d" % (line_number)
377+
else:
378+
command = 'breakpoint set -f "%s" -l %d' % (file_name, line_number)
360379

361380
if module_name:
362381
command += " --shlib '%s'" % (module_name)
@@ -395,14 +414,20 @@ def run_break_set_by_symbol(
395414
396415
If sym_exact is true, then the output symbol must match the input exactly, otherwise we do a substring match.
397416
"""
398-
command = 'breakpoint set -n "%s"' % (symbol)
417+
if get_use_break_add():
418+
command = f"breakpoint add name"
419+
else:
420+
command = 'breakpoint set -n "%s"' % (symbol)
399421

400422
if module_name:
401423
command += " --shlib '%s'" % (module_name)
402424

403425
if extra_options:
404426
command += " " + extra_options
405427

428+
if get_use_break_add():
429+
command += f" -- '{symbol}'"
430+
406431
break_results = run_break_set_command(test, command)
407432

408433
if num_expected_locations == 1 and sym_exact:
@@ -426,7 +451,10 @@ def run_break_set_by_selector(
426451
):
427452
"""Set a breakpoint by selector. Common options are the same as run_break_set_by_file_and_line."""
428453

429-
command = 'breakpoint set -S "%s"' % (selector)
454+
if get_use_break_add():
455+
command = f"breakpoint add name --match-style selector '{selector}'"
456+
else:
457+
command = 'breakpoint set -S "%s"' % (selector)
430458

431459
if module_name:
432460
command += ' --shlib "%s"' % (module_name)
@@ -458,7 +486,10 @@ def run_break_set_by_regexp(
458486
):
459487
"""Set a breakpoint by regular expression match on symbol name. Common options are the same as run_break_set_by_file_and_line."""
460488

461-
command = 'breakpoint set -r "%s"' % (regexp)
489+
if get_use_break_add():
490+
command = f"breakpoint add name --match-style regex '{regexp}'"
491+
else:
492+
command = 'breakpoint set -r "%s"' % (regexp)
462493
if extra_options:
463494
command += " " + extra_options
464495

@@ -473,10 +504,16 @@ def run_break_set_by_source_regexp(
473504
test, regexp, extra_options=None, num_expected_locations=-1
474505
):
475506
"""Set a breakpoint by source regular expression. Common options are the same as run_break_set_by_file_and_line."""
476-
command = 'breakpoint set -p "%s"' % (regexp)
507+
if get_use_break_add():
508+
command = "breakpoint add pattern"
509+
else:
510+
command = 'breakpoint set -p "%s"' % (regexp)
477511
if extra_options:
478512
command += " " + extra_options
479513

514+
if get_use_break_add():
515+
command += f" -- {regexp}"
516+
480517
break_results = run_break_set_command(test, command)
481518

482519
check_breakpoint_result(test, break_results, num_locations=num_expected_locations)
@@ -493,7 +530,11 @@ def run_break_set_by_file_colon_line(
493530
extra_options=None,
494531
num_expected_locations=-1,
495532
):
496-
command = 'breakpoint set -y "%s"' % (specifier)
533+
if get_use_break_add():
534+
command = f"breakpoint add file '{specifier}'"
535+
else:
536+
command = 'breakpoint set -y "%s"' % (specifier)
537+
497538
if extra_options:
498539
command += " " + extra_options
499540

0 commit comments

Comments
 (0)