[RFA 3/5] Explicit linespecs - implementation

Keith Seitz
2012-07-27T03:48:23Z

[-- Attachment #1: Type: text/plain, Size: 2081 bytes --]

Hi,

Ok, so here's what everyone has been waiting for. This patch adds the
implementation of explicit linespecs, including CLI and MI support.

Reminder: You must apply the previous patch (breakpoint-api) before
applying this patch.

Questions/comments/concerns?
Keith

ChangeLog
2012-07-26 Keith Seitz <keiths@redhat.com>

* breakpoint.h (struct breakpoint): Add EXPLICIT.
* breakpoint.c (update_breakpoints_after_exec):
Don't delete the breakpoint if B->EXPLICIT is non-NULL.
(print_breakpoint_location): For pending explicit breakpoints,
use explicit_linespec_to_string to print out an appropriate
address string.
(explicit_linespec_unsupported): New function.
(init_breakpoint_sal): If ELS in non-NULL, copy it into
the struct breakpoint.
(parse_breakpoint_sals): If ELS is non-NULL, call
decode_explicit_linespec instead of decode_line_full.
(create_breakpoint_1): Deal with explicit linespecs.
(break_command_1): Check for an explicit linespec.
(say_where): For pending explicit breakpoints, use
explicit_linepsec_to_string to print out an address string.
(base_breakpoint_dtor): Free any explicit_linespec associated
with the breakpoint.
(bkpt_re_set): Don't delete the breakpoint if B->EXPLICIT
is non-NULL.
(strace_marker_decode_linespec): Error if explicit_linespec
is non-NULL.
(decode_linespec_default): Call decode_explicit_linespec if
explicit_linespec is non-NULL.
* linespec.h (struct linespec_result): Add EXPLICIT.
(new_explicit_linespec, free_explicit_linespec,
copy_explicit_linespec, explicit_linespec_to_string,
string_to_explicit_linespec,
decode_explicit_linespec): Declare.
* linespec.c (canonicalize_linespec): Build an explicit linespec
representation, too.
(destroy_linespec_result): Free the explicit_linespec.
(new_explicit_linespec, free_explicit_linespec,
explicit_lex_one, string_to_explicit_linespec,
copy_explicit_linespec, explicit_linespec_to_string):
New functions.
* mi/mi-cmd-break.c: Include linespec.h.
(mi_cmd_break_insert): Add options for explicit
linespecs and pass them to create_breakpoint.

[-- Attachment #2: els-explicit-linespec.patch --]
[-- Type: text/x-patch, Size: 31854 bytes --]

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 35d55ba..1058a35 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -3486,7 +3486,7 @@ update_breakpoints_after_exec (void)
/* Without a symbolic address, we have little hope of the
pre-exec() address meaning the same thing in the post-exec()
a.out. */
- if (b->addr_string == NULL)
+ if (b->addr_string == NULL && b->explicit == NULL)
{
delete_breakpoint (b);
continue;
@@ -5688,7 +5688,19 @@ print_breakpoint_location (struct breakpoint *b,
do_cleanups (stb_chain);
}
else
- ui_out_field_string (uiout, "pending", b->addr_string);
+ {
+ char *where = b->addr_string;
+ struct cleanup *free_where = make_cleanup (null_cleanup, NULL);
+
+ if (b->explicit != NULL)
+ {
+ where = explicit_linespec_to_string (b->explicit, b->addr_string);
+ make_cleanup (xfree, where);
+ }
+
+ ui_out_field_string (uiout, "pending", where);
+ do_cleanups (free_where);
+ }

if (loc && is_breakpoint (b)
&& breakpoint_condition_evaluation_mode () == condition_evaluation_target
@@ -5757,6 +5769,14 @@ bptype_string (enum bptype type)
return bptypes[(int) type].description;
}

+/* Throw an exception for unsupported explicit linespec. */
+
+static void ATTRIBUTE_NORETURN
+explicit_linespec_unsupported (enum bptype type)
+{
+ error (_("explicit linespec not supported for %s"), bptype_string (type));
+}
+
/* Print B to gdb_stdout. */

static void
@@ -9009,6 +9029,8 @@ init_breakpoint_sal (struct breakpoint *b, struct gdbarch *gdbarch,
me. */
b->addr_string
= xstrprintf ("*%s", paddress (b->loc->gdbarch, b->loc->address));
+ if (els != NULL)
+ b->explicit = copy_explicit_linespec (els);
b->filter = filter;
}

@@ -9040,7 +9062,7 @@ create_breakpoint_sal (struct gdbarch *gdbarch,
old_chain = make_cleanup (xfree, b);

init_breakpoint_sal (b, gdbarch,
- sals, NULL, addr_string,
+ sals, els, addr_string,
filter, cond_string, extra_string,
type, disposition,
thread, task, ignore_count,
@@ -9094,7 +9116,7 @@ create_breakpoints_sal (struct gdbarch *gdbarch,

make_cleanup (xfree, filter_string);
create_breakpoint_sal (gdbarch, lsal->sals,
- NULL, addr_string,
+ canonical->explicit, addr_string,
filter_string,
cond_string, extra_string,
type, disposition,
@@ -9121,8 +9143,9 @@ parse_breakpoint_sals (explicit_linespec *els, char **address,

/* If no arg given, or if first arg is 'if ', use the default
breakpoint. */
- if ((*address) == NULL
- || (strncmp ((*address), "if", 2) == 0 && isspace ((*address)[2])))
+ if (els == NULL
+ && ((*address) == NULL
+ || (strncmp ((*address), "if", 2) == 0 && isspace ((*address)[2]))))
{
/* The last displayed codepoint, if it's valid, is our default breakpoint
address. */
@@ -9167,17 +9190,33 @@ parse_breakpoint_sals (explicit_linespec *els, char **address,

ObjC: However, don't match an Objective-C method name which
may have a '+' or '-' succeeded by a '['. */
- if (last_displayed_sal_is_valid ()
- && (!cursal.symtab
- || ((strchr ("+-", (*address)[0]) != NULL)
- && ((*address)[1] != '['))))
- decode_line_full (address, DECODE_LINE_FUNFIRSTLINE,
- get_last_displayed_symtab (),
- get_last_displayed_line (),
- canonical, NULL, NULL);
+ if (els != NULL)
+ {
+ if (last_displayed_sal_is_valid ())
+ decode_explicit_linespec (els, DECODE_LINE_FUNFIRSTLINE,
+ get_last_displayed_symtab (),
+ get_last_displayed_line (),
+ canonical, NULL, NULL);
+ else
+ decode_explicit_linespec (els, DECODE_LINE_FUNFIRSTLINE,
+ NULL, 0, canonical,
+ NULL, NULL);
+ }
else
- decode_line_full (address, DECODE_LINE_FUNFIRSTLINE,
- cursal.symtab, cursal.line, canonical, NULL, NULL);
+ {
+ if (last_displayed_sal_is_valid ()
+ && (!cursal.symtab
+ || ((strchr ("+-", (*address)[0]) != NULL)
+ && ((*address)[1] != '['))))
+ decode_line_full (address, DECODE_LINE_FUNFIRSTLINE,
+ get_last_displayed_symtab (),
+ get_last_displayed_line (),
+ canonical, NULL, NULL);
+ else
+ decode_line_full (address, DECODE_LINE_FUNFIRSTLINE,
+ cursal.symtab, cursal.line, canonical,
+ NULL, NULL);
+ }
}
}

@@ -9536,6 +9575,8 @@ create_breakpoint_1 (struct gdbarch *gdbarch,

init_raw_breakpoint_without_location (b, gdbarch, type_wanted, ops);

+ if (canonical->explicit != NULL)
+ b->explicit = copy_explicit_linespec (canonical->explicit);
if (canonical->addr_string)
b->addr_string = xstrdup (canonical->addr_string);
if (parse_condition_and_thread)
@@ -9636,10 +9677,12 @@ create_breakpoint (struct gdbarch *gdbarch, explicit_linespec *els,

back_to = make_cleanup_destroy_linespec_result (&canonical);

- if (canonical.addr_string == NULL)
+ if (canonical.explicit == NULL)
{
if (addr_start)
canonical.addr_string = xstrdup (addr_start);
+ if (els != NULL)
+ canonical.explicit = copy_explicit_linespec (els);
}

result = create_breakpoint_1 (gdbarch, &canonical, arg, cond_string,
@@ -9666,7 +9709,9 @@ break_command_1 (char *arg, int flag, int from_tty)
enum bptype type_wanted = (flag & BP_HARDWAREFLAG
? bp_hardware_breakpoint
: bp_breakpoint);
+ explicit_linespec *els;
struct breakpoint_ops *ops;
+ struct cleanup *back_to;
const char *arg_cp = arg;

/* Matching breakpoints on probes. */
@@ -9675,8 +9720,11 @@ break_command_1 (char *arg, int flag, int from_tty)
else
ops = &bkpt_breakpoint_ops;

+ /* Check for an explicit linespec. */
+ els = string_to_explicit_linespec (&arg);
+ back_to = make_cleanup (free_explicit_linespec, els);
create_breakpoint (get_current_arch (),
- NULL, arg,
+ els, arg,
NULL, 0, NULL, 1 /* parse arg */,
tempflag, type_wanted,
0 /* Ignore count */,
@@ -9686,6 +9734,7 @@ break_command_1 (char *arg, int flag, int from_tty)
1 /* enabled */,
0 /* internal */,
0);
+ do_cleanups (back_to);
}

/* Helper function for break_command_1 and disassemble_command. */
@@ -12639,7 +12688,17 @@ say_where (struct breakpoint *b)
single string. */
if (b->loc == NULL)
{
- printf_filtered (_(" (%s) pending."), b->addr_string);
+ char *where = b->addr_string;
+ struct cleanup *free_where = make_cleanup (null_cleanup, NULL);
+
+ if (b->explicit != NULL)
+ {
+ where = explicit_linespec_to_string (b->explicit, b->addr_string);
+ make_cleanup (xfree, where);
+ }
+
+ printf_filtered (_(" (%s) pending."), where);
+ do_cleanups (free_where);
}
else
{
@@ -12702,6 +12761,7 @@ base_breakpoint_dtor (struct breakpoint *self)
xfree (self->addr_string);
xfree (self->filter);
xfree (self->addr_string_range_end);
+ free_explicit_linespec (self->explicit);
}

static struct bp_location *
@@ -12854,7 +12914,7 @@ static void
bkpt_re_set (struct breakpoint *b)
{
/* FIXME: is this still reachable? */
- if (b->addr_string == NULL)
+ if (b->addr_string == NULL && b->explicit == NULL)
{
/* Anything without a string can't be re-set. */
delete_breakpoint (b);
@@ -13237,6 +13297,9 @@ bkpt_probe_create_sals_from_address (explicit_linespec *els, char **arg,
{
struct linespec_sals lsal;

+ if (els != NULL)
+ explicit_linespec_unsupported (type_wanted);
+
lsal.sals = parse_probes (arg, canonical);

*copy_arg = xstrdup (canonical->addr_string);
@@ -13249,6 +13312,9 @@ static void
bkpt_probe_decode_linespec (struct breakpoint *b, explicit_linespec *els,
char **s, struct symtabs_and_lines *sals)
{
+ if (els != NULL)
+ explicit_linespec_unsupported (b->type);
+
*sals = parse_probes (s, NULL);
if (!sals->sals)
error (_("probe not found"));
@@ -13461,7 +13527,7 @@ strace_marker_create_breakpoints_sal (struct gdbarch *gdbarch,

tp = XCNEW (struct tracepoint);
init_breakpoint_sal (&tp->base, gdbarch, expanded,
- NULL, addr_string, NULL,
+ canonical->explicit, addr_string, NULL,
cond_string, extra_string,
type_wanted, disposition,
thread, task, ignore_count, ops,
@@ -13487,6 +13553,9 @@ strace_marker_decode_linespec (struct breakpoint *b, explicit_linespec *els,
{
struct tracepoint *tp = (struct tracepoint *) b;

+ if (els != NULL)
+ explicit_linespec_unsupported (b->type);
+
*sals = decode_static_tracepoint_spec (s);
if (sals->nelts > tp->static_trace_marker_id_idx)
{
@@ -14127,7 +14196,7 @@ breakpoint_re_set_default (struct breakpoint *b)
struct symtabs_and_lines expanded = {0};
struct symtabs_and_lines expanded_end = {0};

- sals = addr_string_to_sals (b, NULL, b->addr_string, &found);
+ sals = addr_string_to_sals (b, b->explicit, b->addr_string, &found);

if (found)
{
@@ -14137,7 +14206,7 @@ breakpoint_re_set_default (struct breakpoint *b)

if (b->addr_string_range_end)
{
- sals_end = addr_string_to_sals (b, NULL, b->addr_string_range_end,
+ sals_end = addr_string_to_sals (b, b->explicit, b->addr_string_range_end,
&found);
if (found)
{
@@ -14196,10 +14265,15 @@ decode_linespec_default (struct breakpoint *b, explicit_linespec *els,
struct linespec_result canonical;

init_linespec_result (&canonical);
- decode_line_full (s, DECODE_LINE_FUNFIRSTLINE,
- (struct symtab *) NULL, 0,
- &canonical, multiple_symbols_all,
- b->filter);
+ if (els == NULL)
+ decode_line_full (s, DECODE_LINE_FUNFIRSTLINE,
+ (struct symtab *) NULL, 0,
+ &canonical, multiple_symbols_all,
+ b->filter);
+ else
+ decode_explicit_linespec (els, DECODE_LINE_FUNFIRSTLINE,
+ NULL, 0, &canonical, multiple_symbols_all,
+ b->filter);

/* We should get 0 or 1 resulting SALs. */
gdb_assert (VEC_length (linespec_sals, canonical.sals) < 2);
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 43be84a..3212f4f 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -683,6 +683,11 @@ struct breakpoint
/* String we used to set the breakpoint (malloc'd). */
char *addr_string;

+ /* The explicit linespec used to set the breakpoint (malloc'd).
+ This may be computed by decode_line_full/decode_explicit_linespec
+ or set by the user. */
+ struct explicit_linespec *explicit;
+
/* The filter that should be passed to decode_line_full when
re-setting this breakpoint. This may be NULL, but otherwise is
allocated with xmalloc. */
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 2b84515..fcbf3dd 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -1664,9 +1664,14 @@ canonicalize_linespec (struct linespec_state *state, linespec_p ls)
if (!state->canonical)
return;

+ state->canonical->explicit = new_explicit_linespec ();
+
/* Shortcut expressions, which can only appear by themselves. */
if (ls->expression != NULL)
- state->canonical->addr_string = xstrdup (ls->expression);
+ {
+ state->canonical->addr_string = xstrdup (ls->expression);
+ state->canonical->explicit->expression = xstrdup (ls->expression);
+ }
else
{
struct ui_file *buf;
@@ -1676,6 +1681,8 @@ canonicalize_linespec (struct linespec_state *state, linespec_p ls)
if (ls->source_filename)
{
fputs_unfiltered (ls->source_filename, buf);
+ state->canonical->explicit->source_filename
+ = xstrdup (ls->source_filename);
need_colon = 1;
}

@@ -1684,6 +1691,8 @@ canonicalize_linespec (struct linespec_state *state, linespec_p ls)
if (need_colon)
fputc_unfiltered (':', buf);
fputs_unfiltered (ls->function_name, buf);
+ state->canonical->explicit->function_name
+ = xstrdup (ls->function_name);
need_colon = 1;
}

@@ -1706,6 +1715,7 @@ canonicalize_linespec (struct linespec_state *state, linespec_p ls)
}

fputs_unfiltered (ls->label_name, buf);
+ state->canonical->explicit->label_name = xstrdup (ls->label_name);
need_colon = 1;
state->canonical->special_display = 1;
}
@@ -1714,11 +1724,13 @@ canonicalize_linespec (struct linespec_state *state, linespec_p ls)
{
if (need_colon)
fputc_unfiltered (':', buf);
- fprintf_filtered (buf, "%s%d",
- (ls->line_offset.sign == LINE_OFFSET_NONE ? ""
- : (ls->line_offset.sign
- == LINE_OFFSET_PLUS ? "+" : "-")),
- ls->line_offset.offset);
+ state->canonical->explicit->offset
+ = xstrprintf ("%s%d",
+ (ls->line_offset.sign == LINE_OFFSET_NONE ? ""
+ : (ls->line_offset.sign
+ == LINE_OFFSET_PLUS ? "+" : "-")),
+ ls->line_offset.offset);
+ fputs_filtered (state->canonical->explicit->offset, buf);
}

state->canonical->addr_string = ui_file_xstrdup (buf, NULL);
@@ -2385,6 +2397,125 @@ decode_line_full (char **argptr, int flags,
do_cleanups (cleanups);
}

+/* A parsing function for explicit linespecs. */
+
+static struct symtabs_and_lines
+parse_explicit_linespec (linespec_parser *parser, void *data)
+{
+ VEC (symbolp) *symbols, *labels;
+ VEC (minsym_and_objfile_d) *minimal_symbols;
+ explicit_linespec *els = (explicit_linespec *) data;
+
+ if (els->expression != NULL)
+ {
+ char *copy = xstrdup (els->expression);
+ struct cleanup *cleanup = make_cleanup (xfree, copy);
+
+ /* Convert the expression to a PC and save the result. */
+ PARSER_RESULT (parser)->expr_pc = linespec_expression_to_pc (&copy);
+ PARSER_RESULT (parser)->expression = xstrdup (els->expression);
+ do_cleanups (cleanup);
+ }
+ else
+ {
+ if (els->source_filename != NULL)
+ {
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ERROR)
+ {
+ PARSER_RESULT (parser)->file_symtabs
+ = symtabs_from_filename (els->source_filename);
+ }
+
+ if (except.reason < 0
+ || PARSER_RESULT (parser)->file_symtabs == NULL)
+ source_file_not_found_error (els->source_filename);
+
+ /* Save the source filename in the parser's result. */
+ PARSER_RESULT (parser)->source_filename
+ = xstrdup (els->source_filename);
+ }
+ else
+ {
+ /* A NULL entry means to use the default symtab. */
+ VEC_safe_push (symtab_p, PARSER_RESULT (parser)->file_symtabs, NULL);
+ }
+
+ if (els->function_name != NULL)
+ {
+ find_linespec_symbols (PARSER_STATE (parser),
+ PARSER_RESULT (parser)->file_symtabs,
+ els->function_name, &symbols,
+ &minimal_symbols);
+
+ if (symbols == NULL && minimal_symbols == NULL)
+ symbol_not_found_error (els->function_name,
+ PARSER_RESULT (parser)->source_filename);
+
+ PARSER_RESULT (parser)->function_name = xstrdup (els->function_name);
+ PARSER_RESULT (parser)->function_symbols = symbols;
+ PARSER_RESULT (parser)->minimal_symbols = minimal_symbols;
+ }
+
+ if (els->label_name != NULL)
+ {
+ symbols = NULL;
+ labels = find_label_symbols (PARSER_STATE (parser),
+ PARSER_RESULT (parser)->function_symbols,
+ &symbols, els->label_name);
+
+ if (labels == NULL)
+ undefined_label_error (PARSER_RESULT (parser)->function_name,
+ els->label_name);
+
+ PARSER_RESULT (parser)->label_name = xstrdup (els->label_name);
+ PARSER_RESULT (parser)->labels.label_symbols = labels;
+ PARSER_RESULT (parser)->labels.function_symbols = symbols;
+ }
+
+ if (els->offset != NULL)
+ PARSER_RESULT (parser)->line_offset
+ = linespec_parse_line_offset (els->offset);
+
+ /* One special error check: If SOURCE_FILENAME was given without
+ OFFSET, FUNCTION_NAME, or LABEL_NAME, issue an error. */
+ if (PARSER_RESULT (parser)->source_filename != NULL
+ && PARSER_RESULT (parser)->function_name == NULL
+ && PARSER_RESULT (parser)->label_name == NULL
+ && PARSER_RESULT (parser)->line_offset.sign == LINE_OFFSET_UNKNOWN)
+ error (_("Source filename requires function, label, or offset."));
+ }
+
+ /* Convert the "parse" to SALs. */
+ return convert_linespec_to_sals (PARSER_STATE (parser),
+ PARSER_RESULT (parser));
+}
+
+/* Decode the explicit linespec in ELS. The other parameters are
+ the same as decode_line_full. */
+
+void
+decode_explicit_linespec (explicit_linespec *els, int flags,
+ struct symtab *default_symtab,
+ int default_line, struct linespec_result *canonical,
+ const char *select_mode,
+ const char *filter)
+{
+ linespec_parser parser;
+ struct cleanup *cleanups;
+
+ gdb_assert (canonical != NULL);
+ gdb_assert ((flags & DECODE_LINE_LIST_MODE) == 0);
+
+ linespec_parser_new (&parser, parse_explicit_linespec, flags,
+ current_language, default_symtab,
+ default_line, canonical);
+ cleanups = make_cleanup (linespec_parser_delete, &parser);
+ do_decode_line (&parser, els, select_mode, filter);
+ do_cleanups (cleanups);
+}
+
/* See linespec.h. */

struct symtabs_and_lines
@@ -2487,7 +2618,8 @@ linespec_expression_to_pc (char **exp_ptr)
throw_error (NOT_FOUND_ERROR, _("cannot evaluate expressions while "
"program space is in startup"));

- (*exp_ptr)++;
+ if (**exp_ptr == '*')
+ (*exp_ptr)++;
return value_as_address (parse_to_comma_and_eval (exp_ptr));
}

@@ -3573,6 +3705,7 @@ destroy_linespec_result (struct linespec_result *ls)
struct linespec_sals *lsal;

xfree (ls->addr_string);
+ free_explicit_linespec (ls->explicit);
for (i = 0; VEC_iterate (linespec_sals, ls->sals, i, lsal); ++i)
{
xfree (lsal->canonical);
@@ -3596,3 +3729,299 @@ make_cleanup_destroy_linespec_result (struct linespec_result *ls)
{
return make_cleanup (cleanup_linespec_result, ls);
}
+
+/* Allocate and initialize a new explicit_linespec. */
+
+explicit_linespec *
+new_explicit_linespec (void)
+{
+ return XCNEW (explicit_linespec);
+}
+
+/* Free the explicit_linespec represented by OBJ. */
+
+void
+free_explicit_linespec (void *obj)
+{
+ explicit_linespec *els = (explicit_linespec *) obj;
+
+ if (els != NULL)
+ {
+ xfree ((char *) els->source_filename);
+ xfree ((char *) els->function_name);
+ xfree ((char *) els->label_name);
+ xfree ((char *) els->offset);
+ xfree ((char *) els->expression);
+ xfree (els);
+ }
+}
+
+/* A lexer for explicit linespecs. This function will advance INP past
+ any strings that it returns. Returns a malloc'd copy of the
+ lexed string or NULL if no lexing was done. */
+
+static char *
+explicit_lex_one (char **inp)
+{
+ char *start = *inp;
+
+ if (*start != '\0')
+ {
+ int lexing_number = 0;
+
+ /* If quoted, skip to the ending quote. */
+ if (strchr (linespec_quote_characters, *start))
+ {
+ char quote_char = *start;
+
+ /* If the input is not an ada operator, skip to the matching
+ closing quote and return the string. */
+ if (!(current_language->la_language == language_ada
+ && quote_char == '\"' && is_ada_operator (start)))
+ {
+ const char *end = skip_quote_char (start + 1, quote_char);
+
+ if (end == NULL)
+ error (_("unmatched quote"));
+ *inp = (char *) end + 1;
+ return savestring (start + 1, *inp - start - 2);
+ }
+ }
+
+ /* Are we lexing a number? */
+ if ((start[0] == '-' || start[0] == '+')
+ && start[1] && isdigit (start[1]))
+ {
+ /* The input is a relative offset. */
+ lexing_number = 1;
+
+ /* Skip the +/-. */
+ ++(*inp);
+ }
+ else if (isdigit (*start))
+ lexing_number = 1;
+
+ /* If the input starts with '-', the string ends with the next
+ whitespace. */
+ if (*start == '-')
+ {
+ while ((*inp)[0] && !isspace ((*inp)[0]))
+ ++(*inp);
+ }
+ /* If lexing a number, stop at the first non-digit character. */
+ else if (lexing_number)
+ {
+ while ((*inp)[0] && isdigit ((*inp)[0]))
+ ++(*inp);
+ }
+ else
+ {
+ /* Otherwise, stop at the next occurrence of "SPACE -", '\0', or
+ keyword. */
+ while ((*inp)[0]
+ && !(isspace ((*inp)[0])
+ && ((*inp)[1] == '-'
+ || linespec_lexer_lex_keyword (&(*inp)[1]))))
+ ++(*inp);
+ }
+ }
+
+ if (*inp - start > 0)
+ return savestring (start, *inp - start);
+
+ return NULL;
+}
+
+/* Attempt to convert the address string in *ARGP into an explicit_linespec.
+ Returns the explicit linespec (which must be free'd by the caller) and
+ advances ARGP over all processed input. Returns NULL if *ARGP does
+ not describe an explicit linespec.
+
+ This function may call error() if the *ARGP looks like properly formed
+ input, e.g., if it is called with missing argument parameters or
+ invalid options. */
+
+explicit_linespec *
+string_to_explicit_linespec (char **argp)
+{
+
+ /* It is assumed that linespecs beginning with '-' and a non-digit
+ character is an explicit linespec. */
+ if (argp != NULL && *argp != NULL)
+ {
+ const char *copy = *argp;
+
+ if (probe_linespec_to_ops (&copy) != NULL)
+ {
+ /* Explicit linespecs with probes is not supported. */
+ return NULL;
+ }
+
+ if ((*argp)[0] == '-' && isalpha ((*argp)[1]))
+ {
+ explicit_linespec *els;
+ struct cleanup *cleanup;
+
+ els = new_explicit_linespec ();
+ cleanup = make_cleanup (free_explicit_linespec, els);
+
+ /* Process option/argument pairs. */
+ while ((*argp)[0] != '\0')
+ {
+ int len;
+ char *opt, *oarg, *start;
+ struct cleanup *inner;
+
+ /* If *ARGP starts with a keyword, stop processing
+ options. */
+ if (linespec_lexer_lex_keyword (*argp) != NULL)
+ break;
+
+ /* Mark the start of the string in case we need to rewind. */
+ start = *argp;
+
+ /* Get the option string. */
+ opt = explicit_lex_one (argp);
+ inner = make_cleanup (xfree, opt);
+
+ /* Skip any whitespace. */
+ *argp = skip_spaces (*argp);
+
+ /* Get the argument string. */
+ oarg = explicit_lex_one (argp);
+ make_cleanup (xfree, oarg);
+
+ /* Skip any whitespace. */
+ *argp = skip_spaces (*argp);
+
+ /* Use the length of the option to allow abbreviations. */
+ len = strlen (opt);
+
+ /* All options have a required argument. */
+ if (strncmp (opt, "-source", len) == 0)
+ els->source_filename = oarg;
+ else if (strncmp (opt, "-function", len) == 0)
+ els->function_name = oarg;
+ else if (strncmp (opt, "-label", len) == 0)
+ els->label_name = oarg;
+ else if (strncmp (opt, "-offset", len) == 0)
+ els->offset = oarg;
+ else if (strncmp (opt, "-address", len) == 0)
+ els->expression = oarg;
+ /* Only emit an "invalid argument" error for options
+ that look like option strings. */
+ else if (opt[0] == '-' && !isdigit (opt[1]))
+ error (_("invalid linespec argument, \"%s\""), opt);
+ else
+ {
+ /* Trailing garbage. This will be handled by
+ one of the callers. */
+ *argp = start;
+ break;
+ }
+
+ /* It's a little lame to error after the fact, but in this
+ case, it provides a much better user experience to issue
+ the "invalid linespec argument" error before any missing
+ argument error. */
+ if (oarg == NULL)
+ error (_("missing argument for \"%s\""), opt);
+
+ /* The option/argument pair was successfully processed,
+ that memory now belongs to the explicit_linespec. */
+ discard_cleanups (inner);
+ }
+
+ /* A valid explicit linespec was constructed. */
+ discard_cleanups (cleanup);
+ return els;
+ }
+ }
+
+ /* Not an explicit linespec. */
+ return NULL;
+}
+
+/* Deep copy the explicit linespec given by SRC and return
+ the copy. */
+
+explicit_linespec *
+copy_explicit_linespec (explicit_linespec *src)
+{
+ explicit_linespec *copy;
+
+ copy = XCNEW (explicit_linespec);
+#define STRDUP_IF(MBR) \
+ do { \
+ if (src->MBR != NULL) \
+ copy->MBR = xstrdup (src->MBR); \
+ } \
+ while (0)
+ STRDUP_IF (source_filename);
+ STRDUP_IF (function_name);
+ STRDUP_IF (label_name);
+ STRDUP_IF (offset);
+ STRDUP_IF (expression);
+#undef STRDUP_IF
+ return copy;
+}
+
+/* Return a string representation of the explicit linespec
+ ELS, appending ARG (conditional, thread, task).
+ The result is malloc'd and must be free'd by the caller. */
+
+char *
+explicit_linespec_to_string (explicit_linespec *els, const char *arg)
+{
+ char *string;
+ struct ui_file *buf;
+ int need_colon = 0;
+
+ buf = mem_fileopen ();
+
+ if (els->expression)
+ fprintf_unfiltered (buf, "*%s", els->expression);
+ else
+ {
+ if (els->source_filename != NULL)
+ {
+ fputs_unfiltered (els->source_filename, buf);
+ need_colon = 1;
+ }
+
+ if (els->function_name != NULL)
+ {
+ if (need_colon)
+ fputc_unfiltered (':', buf);
+ fputs_unfiltered (els->function_name, buf);
+ need_colon = 1;
+ }
+
+ if (els->label_name != NULL)
+ {
+ if (need_colon)
+ fputc_unfiltered (':', buf);
+ fputs_unfiltered (els->label_name, buf);
+ need_colon = 1;
+ }
+
+ if (els->offset != NULL)
+ {
+ if (need_colon)
+ fputc_unfiltered (':', buf);
+ fputs_unfiltered (els->offset, buf);
+ }
+ }
+
+ /* Append any non-explicit text added by user. */
+ if (arg != NULL)
+ {
+ fputc_unfiltered (' ', buf);
+ fputs_unfiltered (arg, buf);
+ }
+
+ /* Covert to a string a return. */
+ string = ui_file_xstrdup (buf, NULL);
+ ui_file_delete (buf);
+ return string;
+}
diff --git a/gdb/linespec.h b/gdb/linespec.h
index 51b111b..95da829 100644
--- a/gdb/linespec.h
+++ b/gdb/linespec.h
@@ -96,6 +96,10 @@ struct linespec_result
by the user. This will be freed by destroy_linespec_result. */
char *addr_string;

+ /* The explicit linespec returned by the parser or NULL if the
+ input could not be parsed. */
+ struct explicit_linespec *explicit;
+
/* The sals. The vector will be freed by
destroy_linespec_result. */
VEC (linespec_sals) *sals;
@@ -114,6 +118,27 @@ extern void destroy_linespec_result (struct linespec_result *);
extern struct cleanup *
make_cleanup_destroy_linespec_result (struct linespec_result *);

+/* Allocate and initialize a new explicit_linespec. */
+extern explicit_linespec *new_explicit_linespec (void);
+
+/* Free the explicit linespec represented by OBJ. */
+extern void free_explicit_linespec (void *obj);
+
+/* Copy the given explicit linespec. Free memory with
+ free_explicit_linespec. */
+extern explicit_linespec *copy_explicit_linespec (explicit_linespec *src);
+
+/* Return a string representation of the explicit linespec ELS,
+ appending ARG (conditional/thread/task). */
+extern char *explicit_linespec_to_string (explicit_linespec *els,
+ const char *arg);
+
+/* Attempt to convert the string in *ARGP into an explicit_linespec.
+ Returns the explicit linespec (which must be free'd by the caller) and
+ advacnes ARGP over all processed input. Returns NULL if *ARGP does
+ not describe an explicit linespec. */
+explicit_linespec *string_to_explicit_linespec (char **argp);
+
/* Decode a linespec using the provided default symtab and line. */

extern struct symtabs_and_lines
@@ -162,6 +187,13 @@ extern void decode_line_full (char **argptr, int flags,
const char *select_mode,
const char *filter);

+extern void decode_explicit_linespec (struct explicit_linespec *els, int flags,
+ struct symtab *default_symtab,
+ int default_line,
+ struct linespec_result *canonical,
+ const char *select_mode,
+ const char *filter);
+
/* Given a string, return the line specified by it, using the current
source symtab and line as defaults.
This is for commands like "list" and "breakpoint". */
diff --git a/gdb/mi/mi-cmd-break.c b/gdb/mi/mi-cmd-break.c
index 52ea90c..b96074d 100644
--- a/gdb/mi/mi-cmd-break.c
+++ b/gdb/mi/mi-cmd-break.c
@@ -23,6 +23,7 @@
#include "ui-out.h"
#include "mi-out.h"
#include "breakpoint.h"
+#include "linespec.h"
#include "gdb_string.h"
#include "mi-getopt.h"
#include "gdb.h"
@@ -74,14 +75,18 @@ mi_cmd_break_insert (char *command, char **argv, int argc)
int pending = 0;
int enabled = 1;
int tracepoint = 0;
+ int explicit = 0;
struct cleanup *back_to;
enum bptype type_wanted;
+ explicit_linespec *els;

enum opt
{
HARDWARE_OPT, TEMP_OPT, CONDITION_OPT,
IGNORE_COUNT_OPT, THREAD_OPT, PENDING_OPT, DISABLE_OPT,
TRACEPOINT_OPT,
+ EXPLICIT_EXPR_OPT, EXPLICIT_SOURCE_OPT, EXPLICIT_FUNC_OPT,
+ EXPLICIT_LABEL_OPT, EXPLICIT_OFFSET_OPT
};
static const struct mi_opt opts[] =
{
@@ -93,6 +98,11 @@ mi_cmd_break_insert (char *command, char **argv, int argc)
{"f", PENDING_OPT, 0},
{"d", DISABLE_OPT, 0},
{"a", TRACEPOINT_OPT, 0},
+ {"e", EXPLICIT_EXPR_OPT, 1},
+ {"s", EXPLICIT_SOURCE_OPT, 1},
+ {"m", EXPLICIT_FUNC_OPT, 1},
+ {"l", EXPLICIT_LABEL_OPT, 1},
+ {"o", EXPLICIT_OFFSET_OPT, 1},
{ 0, 0, 0 }
};

@@ -101,6 +111,8 @@ mi_cmd_break_insert (char *command, char **argv, int argc)
int oind = 0;
char *oarg;

+ els = new_explicit_linespec ();
+ back_to = make_cleanup (free_explicit_linespec, els);
while (1)
{
int opt = mi_getopt ("-break-insert", argc, argv,
@@ -133,14 +145,46 @@ mi_cmd_break_insert (char *command, char **argv, int argc)
case TRACEPOINT_OPT:
tracepoint = 1;
break;
+ case EXPLICIT_EXPR_OPT:
+ explicit = 1;
+ els->expression = xstrdup (oarg);
+ break;
+ case EXPLICIT_SOURCE_OPT:
+ explicit = 1;
+ els->source_filename = xstrdup (oarg);
+ break;
+ case EXPLICIT_FUNC_OPT:
+ explicit = 1;
+ els->function_name = xstrdup (oarg);
+ break;
+ case EXPLICIT_LABEL_OPT:
+ explicit = 1;
+ els->label_name = xstrdup (oarg);
+ break;
+ case EXPLICIT_OFFSET_OPT:
+ explicit = 1;
+ els->offset = xstrdup (oarg);
+ break;
}
}

- if (oind >= argc)
+ if (oind >= argc && !explicit)
error (_("-break-insert: Missing <location>"));
- if (oind < argc - 1)
- error (_("-break-insert: Garbage following <location>"));
- address = argv[oind];
+ if (explicit)
+ {
+ if (oind < argc)
+ error (_("-break-insert: Garbage following explicit linesepc"));
+ }
+ else
+ {
+ if (oind < argc - 1)
+ error (_("-break-insert: Garbage following <location>"));
+ address = argv[oind];
+
+ /* An explicit linespec was not specified -- reset ELS to NULL.
+ [It will be free'd in a cleanup later.] */
+ els = NULL;
+ }

/* Now we have what we need, let's insert the breakpoint! */
if (! mi_breakpoint_observers_installed)
@@ -149,7 +193,7 @@ mi_cmd_break_insert (char *command, char **argv, int argc)
mi_breakpoint_observers_installed = 1;
}

- back_to = make_cleanup_restore_integer (&mi_can_breakpoint_notify);
+ make_cleanup_restore_integer (&mi_can_breakpoint_notify);
mi_can_breakpoint_notify = 1;

/* Note that to request a fast tracepoint, the client uses the
@@ -163,7 +207,7 @@ mi_cmd_break_insert (char *command, char **argv, int argc)
? (hardware ? bp_fast_tracepoint : bp_tracepoint)
: (hardware ? bp_hardware_breakpoint : bp_breakpoint));

- create_breakpoint (get_current_arch (), NULL, address, condition, thread,
+ create_breakpoint (get_current_arch (), els, address, condition, thread,
NULL /* extra_string */,
0 /* condition and thread are valid. */,
temp_p, type_wanted,