Re: [Patch] Bug 8287: Skip uninteresting functions while debugging

Justin Lebar
2010-06-28T17:44:29Z

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

On Fri, Jun 25, 2010 at 2:56 PM, Tom Tromey <tromey@redhat.com> wrote:
> FWIW I think this is a very good first patch to gdb.

Thanks!

> One concern of mine is that this code will not perform well with many
> blacklists in place. It may make sense to keep a hash table indexed by
> name (function or file), to make lookups faster. What do you think?

I imagine you'd have to have a pretty large blacklist to notice a delay when
using gdb interactively, even on modest hardware. I don't know much about gdb
scripting -- this might matter more in that case. But would one single-step in
a script?

FWIW, enabling/disabling/deleting breakpoints takes time linear to the number
of breakpoints.

I'm happy to switch to a hashtable here if that's what we want.

> Another random thought is whether we want to be able to blacklist based
> on objfile name.

That seems like it might be nice. Regular expression matching on file and
function names has also been suggested.

While I'm thinking about it, one problem I've run into while using this patch
while debugging Firefox is that we somehow load multiple copies of our smart
pointer header, so I have to blacklist both ../../nsCOMPtr.h and
../../../nsCOMPtr.h. I need to investigate further exactly what about our
build system is causing this to see if it's something which should be fixed in
gdb or Mozilla's build, but my guess is that we may want to support this in gdb
somehow.

> Justin> This patch also fixes bug 11614: decode_variable() in linespec.c
> Justin> does not obey its contract
>
> It is somewhat preferred in gdb to submit things like this as separate
> patches.

I've split them out in this new set. Because you need the bug11614 patch in
order for the blacklist patch to work (and since bug11614 is a trivial fix),
I've included both attachments in this one e-mail.

> Justin> + if (sals.nelts == 0)
> Justin> + error (_("No function to blacklist.")); /* TODO can I trigger this? */
> Justin> + if (sals.nelts > 1)
> Justin> + error (_("Specify just one function at a time.")); /* TODO can I
> Justin> trigger this? */
>
> Good questions :-)
>
> It is great to be defensive here, but the comments should go before
> checkin.
>
> Maybe you can get nelts>1 with a C++ destructor? I'm not sure.

I didn't hit that error with a simple C++ destructor. Maybe some kind of
pathological destructor could hit it.

I'm mostly concerned that the error messages are meaningful if we do hit them.
I'm only guessing at what nelts == 0 and nelts > 1 mean.

> Justin> + if (opts.addressprint)
> Justin> + {
> Justin> + if (b->pc != 0)
> Justin> + ui_out_field_core_addr (uiout, "addr", b->gdbarch, b->pc); /* 4 */
> Justin> + else
> Justin> + ui_out_field_string (uiout, "addr", "n/a"); /* 4 */
> Justin> + }
>
> A small nit, but I'm not sure about the exact string "n/a" here.
> If there is an analogous situation elsewhere it would be good to just do
> whatever is done there.

We could just use the empty string if that would be clearer.

> Justin> + /* First, check whether the file or function this entry is pending on has
> Justin> + been loaded. It might be more sensible to do this on a solib load,
> Justin> + but that doesn't seem to work for some reason. */
>
> Let's figure this out.

I spent a while in #gdb with a few people (I don't recall who) and we couldn't
figure this out. I added a solib load observer, but from within it,
find_pc_partial_function always failed.

I'm not sure where to look to figure out what's going on.

> Also, since the blacklist includes a PC value, I think you have to reset
> it when re-running the inferior, and on some other state changes as well.

Hm. How do we handle this with breakpoints?

> Justin> + add_cmd ("enable", class_blacklist, blacklist_enable_command, _("\
> Justin> +Enable a blacklist entry.\n\
> Justin> +blacklist enable [NUMBER]"),
> Justin> + &blacklistlist);
>
> I think it would be more usual to make the commands "enable blacklist ..."
> instead of "blacklist enable ...". Likewise for disable and delete.

I'm not sure I like "enable blacklist". The problem is that "blacklist" is a
noun referring to the list of all functions we skip and also a verb meaning
"add an entry to the blacklist". "Enable blacklist" sounds to me like we're
enabling an entire list of blacklist entries.

Maybe we can name the thing which indicates that we shouldn't step into a
function something less confusing than a "blacklist entry"?

> Justin> -int default_breakpoint_valid;
> Justin> -CORE_ADDR default_breakpoint_address;
> Justin> -struct symtab *default_breakpoint_symtab;
> Justin> -int default_breakpoint_line;
> Justin> -struct program_space *default_breakpoint_pspace;
>
> I'm not sure I understand the rationale for this set of changes.
> Is it just to make the naming more clear?

Right: They're no longer the default breakpoint values since they're now also
the default blacklist values too. I moved them into stack.c so that
set_last_displayed_codepoint (which replaces set_default_breakpoint) could be a
static function -- it's only called in stack.c.

-Justin

[-- Attachment #2: bug8287 --]
[-- Type: application/octet-stream, Size: 47308 bytes --]

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 969f31d..3285f70 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,40 @@
+2010-06-18 Justin Lebar <justin.lebar@gmail.com>
+
+ * Makefile.in: (SFILES): Add blacklist.c.
+ (HFILES_NO_SRCDIR): Add blacklist.h.
+ (COMMON_OBS): Add blacklist.o.
+ * blacklist.h, blacklist.c: New
+ * breakpoint.h (set_default_breakpoint): removed
+ * breakpoint.c: Removed default_breakpoint_valid,
+ default_breakpoint_address, default_breakpoint_symtab,
+ default_breakpoint_line, default_breakpoint_pspace variables.
+ (set_default_breakpoint): Removed
+ (parse_breakpoint_sals, create_breakpoint, clear_command,
+ decode_line_spec_1): Removed uses of default_breakpoint variables;
+ replaced with function calls into stack.c.
+ * cli-cmds.h: Added cmd_list_element *blacklistlist.
+ * cli-cmds.c: Added blacklistlist.
+ (init_cmd_lists): Initialize blacklistlist.
+ (init_cli_cmds): Fixed comment (classes of commands appear in
+ alphabetical order).
+ * infrun.c (handle_inferior_event): Added check that we don't step
+ into a function whose pc is blacklisted.
+ * stack.c: Added last_codepoint_valid, last_codepoint_pspace,
+ last_codepoint_addr, last_codepoint_symtab, last_codepoint_line
+ variables.
+ (set_last_displayed_codepoint): New static function.
+ (print_frame_info): Switched call to set_default_breakpoint to call to
+ set_last_displayed_codepoint.
+ (clear_last_displayed_codepoint,
+ last_displayed_codepoint_is_valid, get_last_displayed_pspace,
+ get_last_displayed_addr, get_last_displayed_symtab,
+ get_last_displayed_line, set_sal_to_last_displayed_codepoint): New
+ public functions.
+ * stack.h (clear_last_displayed_codepoint,
+ last_displayed_codepoint_is_valid, get_last_displayed_pspace,
+ get_last_displayed_addr, get_last_displayed_symtab,
+ get_last_displayed_line, set_sal_to_last_displayed_codepoint): Added
+
2010-06-25 Justin Lebar <justin.lebar@gmail.com>

* linespec.c (decode_variable): Passing a non-null not_found_ptr
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index fc148fe..5d2c23e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -653,6 +653,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
auxv.c ax-general.c ax-gdb.c \
bcache.c \
bfd-target.c \
+ blacklist.c \
block.c blockframe.c breakpoint.c buildsym.c \
c-exp.y c-lang.c c-typeprint.c c-valprint.c \
charset.c cli-out.c coffread.c coff-pe-read.c \
@@ -772,7 +773,7 @@ annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \
remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \
sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h \
gdb_usleep.h jit.h xml-syscall.h ada-operator.inc microblaze-tdep.h \
-psymtab.h psympriv.h
+psymtab.h psympriv.h blacklist.h

# Header files that already have srcdir in them, or which are in objdir.

@@ -806,6 +807,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
addrmap.o \
auxv.o \
bfd-target.o \
+ blacklist.o \
blockframe.o breakpoint.o findvar.o regcache.o \
charset.o disasm.o dummy-frame.o dfp.o \
source.o value.o eval.o valops.o valarith.o valprint.o printcmd.o \
diff --git a/gdb/blacklist.c b/gdb/blacklist.c
new file mode 100644
index 0000000..6594a28
--- /dev/null
+++ b/gdb/blacklist.c
@@ -0,0 +1,562 @@
+/* Header for GDB line completion.
+ Copyright (C) 2010 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "blacklist.h"
+#include "value.h"
+#include "valprint.h"
+#include "ui-out.h"
+#include "gdb_string.h"
+#include "symtab.h"
+#include "gdbcmd.h"
+#include "command.h"
+#include "completer.h"
+#include "stack.h"
+#include "arch-utils.h"
+#include "linespec.h"
+#include "objfiles.h"
+
+struct blacklist_entry
+{
+ int number;
+
+ /* null if this isn't a blacklist entry for an entire file.
+ The blacklist entry owns this pointer. */
+ char *filename;
+
+ /* The name of the blacklisted function, if this is a blacklist entry for a
+ function. Note that this might be non-null even if the pc is 0 if the
+ entry is pending a shared library load.
+
+ The blacklist entry owns this pointer. */
+ char *function_name;
+
+ /* 0 if this is a blacklist entry for an entire file, or if this entry will
+ be on a function, pending a shared library load. */
+ CORE_ADDR pc;
+
+ /* Architecture we used to create the blacklist entry. May be null
+ if the entry is pending a shared library load. */
+ struct gdbarch *gdbarch;
+
+ int enabled;
+ int pending;
+
+ struct blacklist_entry *next;
+};
+
+static void blacklist_function_command (char *arg, int from_tty);
+static void blacklist_file_command (char *arg, int from_tty);
+static void blacklist_info (char *arg, int from_tty);
+
+static void add_blacklist_entry (struct blacklist_entry *b);
+static void blacklist_function_pc (CORE_ADDR pc, char *name,
+ struct gdbarch *arch,
+ int pending);
+static void try_resolve_pending_entry (struct blacklist_entry *b);
+static struct gdbarch *get_sal_arch (struct symtab_and_line *sal);
+
+static struct blacklist_entry *blacklist_entry_chain;
+static int blacklist_entry_count;
+
+#define ALL_BLACKLIST_ENTRIES(B) \
+ for (B = blacklist_entry_chain; B; B = B->next)
+
+static void
+blacklist_file_command (char *arg, int from_tty)
+{
+ struct blacklist_entry *b;
+ struct symtab *symtab;
+ int pending = 0;
+ char *filename = 0;
+
+ /* If no argument was given, try to default to the last
+ displayed codepoint. */
+ if (arg == 0)
+ {
+ symtab = get_last_displayed_symtab ();
+ if (symtab == 0)
+ error (_("No default blacklist file now."));
+ else
+ filename = symtab->filename;
+ }
+ else
+ {
+ symtab = lookup_symtab (arg);
+ if (symtab == 0)
+ {
+ fprintf_filtered (gdb_stderr, _("No source file named %s.\n"), arg);
+ if (!nquery (_("\
+Add file to blacklist pending future shared library load? ")))
+ return;
+
+ pending = 1;
+ filename = arg;
+ }
+ else
+ filename = symtab->filename;
+ }
+
+ b = XZALLOC (struct blacklist_entry);
+ b->filename = xstrdup (filename);
+ b->enabled = 1;
+ b->pending = pending;
+ if (symtab != 0)
+ b->gdbarch = get_objfile_arch (symtab->objfile);
+
+ add_blacklist_entry (b);
+
+ printf_filtered ("Blacklisting file %s.\n", filename);
+}
+
+static void
+blacklist_function_command (char *arg, int from_tty)
+{
+ CORE_ADDR func_pc;
+ char *name = NULL;
+
+ /* Default to the current function if no argument is given. */
+ if (arg == 0)
+ {
+ CORE_ADDR pc;
+ if (!last_displayed_codepoint_is_valid ())
+ error (_("No default blacklist function now."));
+
+ pc = get_last_displayed_addr ();
+ if (!find_pc_partial_function (pc, &name, &func_pc, 0))
+ {
+ error (_("No function found containing current program point %s."),
+ paddress (get_current_arch (), pc));
+ }
+ blacklist_function_pc (func_pc, name, get_current_arch (), 0);
+ }
+ else
+ {
+ /* Decode arg. We set funfirstline=1 so decode_line_1 will give us the
+ first line of the function specified, if it can, and so that we'll
+ reject variable names and the like. */
+
+ /* TODO maybe want something like parse_breakpoint_sals ()
+ in breakpoint.c. */
+ int i;
+ int not_found = 0;
+ int pending = 0;
+ char *orig_arg = arg; /* decode_line_1 modifies the arg pointer. */
+ struct symtabs_and_lines sals = decode_line_1 (&arg, 1, 0, 0, 0,
+ &not_found);
+
+ if (not_found)
+ {
+ fprintf_filtered (gdb_stderr,
+ _("No function found named %s.\n"), orig_arg);
+
+ if (nquery (_("\
+Add function to blacklist pending future shared library load? ")))
+ {
+ /* Add the pending blacklist entry. */
+ blacklist_function_pc (0, orig_arg, 0, 1);
+ }
+
+ return;
+ }
+
+ if (sals.nelts == 0)
+ error (_("No function to blacklist.")); /* TODO can I trigger this? */
+ if (sals.nelts > 1)
+ error (_("Specify just one function at a time.")); /* TODO can I trigger this? */
+ if (strlen (arg) != 0)
+ error (_("Junk at end of arguments."));
+
+ /* The pc decode_line_1 gives us is the first line of the function,
+ but we actually want the line before that. The call to
+ find_pc_partial_function gets us the value we actually want. */
+ {
+ struct symtab_and_line *sal = &sals.sals[0];
+ CORE_ADDR pc = sal->pc;
+ CORE_ADDR func_start = 0;
+ struct gdbarch *arch = get_sal_arch (sal);
+
+ if (!find_pc_partial_function (pc, &name, &func_start, 0))
+ {
+ error (_("No function found containing program point %s."),
+ paddress (arch, pc));
+ }
+
+ blacklist_function_pc (func_start, name, arch, 0);
+ }
+ }
+}
+
+static void
+blacklist_info (char *arg, int from_tty)
+{
+ struct blacklist_entry *b;
+ int num_printable_entries = 0;
+ int entry_num = -1;
+ int address_width = 10;
+ struct value_print_options opts;
+ struct cleanup *tbl_chain;
+
+ get_user_print_options (&opts);
+
+ if (arg != 0)
+ {
+ entry_num = parse_and_eval_long (arg);
+ }
+
+ /* Count the number of rows in the table and see if we need space for a
+ 64-bit address anywhere. */
+ ALL_BLACKLIST_ENTRIES (b)
+ if (entry_num == -1 || b->number == entry_num)
+ {
+ num_printable_entries++;
+ if (b->gdbarch && gdbarch_addr_bit (b->gdbarch) > 32)
+ address_width = 18;
+ }
+
+ if (num_printable_entries == 0)
+ {
+ if (entry_num == -1)
+ ui_out_message (uiout, 0, "Blacklist is empty.\n");
+ else
+ ui_out_message (uiout, 0,
+ "No blacklist entry numbered %d.\n", entry_num);
+
+ return;
+ }
+
+ if (opts.addressprint)
+ tbl_chain
+ = make_cleanup_ui_out_table_begin_end (uiout, 5, num_printable_entries,
+ "BlacklistTable");
+ else
+ tbl_chain
+ = make_cleanup_ui_out_table_begin_end (uiout, 4, num_printable_entries,
+ "BlacklistTable");
+
+ ui_out_table_header (uiout, 7, ui_left, "number", "Num"); /* 1 */
+ ui_out_table_header (uiout, 14, ui_left, "type", "Type"); /* 2 */
+ ui_out_table_header (uiout, 3, ui_left, "enabled", "Enb"); /* 3 */
+ if (opts.addressprint)
+ {
+ ui_out_table_header (uiout, address_width, ui_left,
+ "addr", "Address"); /* 4 */
+ }
+ ui_out_table_header (uiout, 40, ui_noalign, "what", "What"); /* 5 */
+ ui_out_table_body (uiout);
+
+ ALL_BLACKLIST_ENTRIES (b)
+ {
+ struct cleanup *entry_chain;
+
+ QUIT;
+ if (entry_num != -1 && entry_num != b->number)
+ continue;
+
+ entry_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "blklst-entry");
+ ui_out_field_int (uiout, "number", b->number); /* 1 */
+
+ if (b->function_name != 0)
+ ui_out_field_string (uiout, "type", "function"); /* 2 */
+ else if (b->filename != 0)
+ ui_out_field_string (uiout, "type", "file"); /* 2 */
+ else
+ internal_error (__FILE__, __LINE__, _("\
+Blacklist entry should have either a filename or a function name."));
+
+ if (b->enabled)
+ ui_out_field_string (uiout, "enabled", "y"); /* 3 */
+ else
+ ui_out_field_string (uiout, "enabled", "n"); /* 3 */
+
+ if (opts.addressprint)
+ {
+ if (b->pc != 0)
+ ui_out_field_core_addr (uiout, "addr", b->gdbarch, b->pc); /* 4 */
+ else
+ ui_out_field_string (uiout, "addr", "n/a"); /* 4 */
+ }
+
+ if (!b->pending && b->function_name != 0)
+ {
+ struct symbol *sym;
+ gdb_assert (b->pc != 0);
+ sym = find_pc_function (b->pc);
+ if (sym)
+ ui_out_field_fmt (uiout, "what", "%s at %s:%d",
+ sym->ginfo.name,
+ sym->symtab->filename,
+ sym->line);
+ else
+ ui_out_field_string (uiout, "what", "?");
+ }
+ else if (b->pending && b->function_name != 0)
+ {
+ ui_out_field_fmt (uiout, "what", "%s (PENDING)",
+ b->function_name);
+ }
+ else if (!b->pending && b->filename != 0)
+ ui_out_field_string (uiout, "what", b->filename);
+ else if (b->pending && b->filename != 0)
+ ui_out_field_fmt (uiout, "what", "%s (PENDING)",
+ b->filename);
+
+ ui_out_text (uiout, "\n");
+ do_cleanups (entry_chain);
+ }
+
+ do_cleanups (tbl_chain);
+}
+
+static void
+blacklist_enable_command (char *arg, int from_tty)
+{
+ struct blacklist_entry *b;
+ int entry_num = parse_and_eval_long (arg);
+ ALL_BLACKLIST_ENTRIES (b)
+ if (b->number == entry_num)
+ {
+ b->enabled = 1;
+ return;
+ }
+
+ error (_("No blacklist entry numbered %d."), entry_num);
+}
+
+static void
+blacklist_disable_command (char *arg, int from_tty)
+{
+ struct blacklist_entry *b;
+ int entry_num = parse_and_eval_long (arg);
+ ALL_BLACKLIST_ENTRIES (b)
+ if (b->number == entry_num)
+ {
+ b->enabled = 0;
+ return;
+ }
+
+ error (_("No blacklist entry numbered %d."), entry_num);
+}
+
+/* Command do delete a blacklist entry. */
+static void
+blacklist_delete_command (char *arg, int from_tty)
+{
+ struct blacklist_entry *b, *b_prev;
+ int entry_num = parse_and_eval_long (arg);
+
+ /* We don't need to use a SAFE macro here since we return as soon as we
+ remove an element from the list. */
+ b_prev = 0;
+ ALL_BLACKLIST_ENTRIES (b)
+ if (b->number == entry_num)
+ {
+ if (b_prev != 0)
+ b_prev->next = b->next;
+ else
+ blacklist_entry_chain = b->next;
+
+ xfree (b->function_name);
+ xfree (b->filename);
+ xfree (b);
+ return;
+ }
+ else
+ {
+ b_prev = b;
+ }
+
+ error (_("No blacklist entry numbered %d."), entry_num);
+}
+
+/* Create a blacklist entry for the given pc corresponding to the given
+ function name and add it to the list. */
+static void
+blacklist_function_pc (CORE_ADDR pc, char *name, struct gdbarch *arch,
+ int pending)
+{
+ struct blacklist_entry *b = XZALLOC (struct blacklist_entry);
+ b->pc = pc;
+ b->gdbarch = arch;
+ b->enabled = 1;
+ b->pending = pending;
+ b->function_name = xstrdup (name);
+
+ add_blacklist_entry (b);
+
+ if (!pending)
+ printf_filtered ("Blacklisting function %s at %s.\n",
+ name, paddress (get_current_arch (), pc));
+ else
+ printf_filtered ("Blacklisting function %s pending shared library load.\n",
+ name);
+}
+
+/* Add the given blacklist entry to our list, and set the entry's number. */
+static void
+add_blacklist_entry (struct blacklist_entry *b)
+{
+ struct blacklist_entry *b1;
+
+ b->number = ++blacklist_entry_count;
+
+ /* Add to the end of the chain so that the list of
+ blacklist entries will be in numerical order. */
+
+ b1 = blacklist_entry_chain;
+ if (b1 == 0)
+ blacklist_entry_chain = b;
+ else
+ {
+ while (b1->next)
+ b1 = b1->next;
+ b1->next = b;
+ }
+}
+
+/* Does the given pc correspond to the beginning of a blacklisted function? */
+int
+function_pc_is_blacklisted (CORE_ADDR pc)
+{
+ struct symtab_and_line sal;
+ char *filename;
+ struct blacklist_entry *b;
+
+ sal = find_pc_line (pc, 0);
+ filename = sal.symtab->filename;
+
+ ALL_BLACKLIST_ENTRIES (b)
+ {
+ /* First, check whether the file or function this entry is pending on has
+ been loaded. It might be more sensible to do this on a solib load,
+ but that doesn't seem to work for some reason. */
+ if (b->pending)
+ try_resolve_pending_entry (b);
+
+ if (b->enabled && !b->pending
+ && ((b->pc != 0 && pc == b->pc)
+ || (b->filename != 0 && filename != 0
+ && strcmp (filename, b->filename) == 0)))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Try to look up the file or function corresponding to the given blacklist
+ entry. If the file or function now exists, update the entry and unmark it
+ as pending. */
+static void
+try_resolve_pending_entry (struct blacklist_entry *b)
+{
+ if (!b->pending)
+ return;
+
+ if (b->filename != 0)
+ {
+ struct symtab *symtab = lookup_symtab (b->filename);
+ if (symtab != 0)
+ {
+ xfree (b->filename);
+ b->filename = xstrdup (symtab->filename);
+ b->gdbarch = get_objfile_arch (symtab->objfile);
+ b->pending = 0;
+ }
+ }
+ else if (b->function_name != 0)
+ {
+ int not_found = 0;
+ char *func_name = b->function_name;
+ struct symtabs_and_lines sals = decode_line_1 (&func_name, 1, 0, 0, 0,
+ &not_found);
+
+ if (!not_found && sals.nelts == 1 && strlen (func_name) == 0)
+ {
+ struct symtab_and_line *sal = &sals.sals[0];
+ CORE_ADDR pc = sal->pc;
+ CORE_ADDR func_start = 0;
+ struct gdbarch *arch = get_sal_arch (sal);
+
+ if (find_pc_partial_function (pc, &b->function_name, &func_start, 0))
+ {
+ b->pending = 0;
+ b->pc = func_start;
+ b->gdbarch = arch;
+ }
+ }
+ }
+}
+
+/* Helper function to get a gdbarch from a symtab_and_line. */
+static struct gdbarch*
+get_sal_arch (struct symtab_and_line *sal)
+{
+ if (sal->section)
+ return get_objfile_arch (sal->section->objfile);
+ if (sal->symtab)
+ return get_objfile_arch (sal->symtab->objfile);
+ return get_current_arch ();
+}
+
+void
+_initialize_step_blacklist (void)
+{
+ struct cmd_list_element *c;
+
+ blacklist_entry_chain = 0;
+ blacklist_entry_count = 0;
+
+ add_prefix_cmd ("blacklist", class_breakpoint, blacklist_function_command, _("\
+Ignore a function while stepping.\n\
+blacklist [FUNCTION NAME]\n\
+If no function name is given, blacklist the current function."),
+ &blacklistlist, "blacklist ", 1, &cmdlist);
+
+ c = add_cmd ("file", class_breakpoint, blacklist_file_command, _("\
+Ignore a file while stepping.\n\
+blacklist file [FILENAME]\n\
+If no filename is given, blacklist the current file."),
+ &blacklistlist);
+ set_cmd_completer (c, filename_completer);
+
+ c = add_cmd ("function", class_breakpoint, blacklist_function_command, _("\
+Ignore a function while stepping.\n\
+blacklist function [FUNCTION NAME]\n\
+If no function name is given, blacklist the current function."),
+ &blacklistlist);
+ set_cmd_completer (c, location_completer);
+
+ add_cmd ("enable", class_breakpoint, blacklist_enable_command, _("\
+Enable a blacklist entry.\n\
+blacklist enable [NUMBER]"),
+ &blacklistlist);
+
+ add_cmd ("disable", class_breakpoint, blacklist_disable_command, _("\
+Disable a blacklist entry.\n\
+blacklist disable [NUMBER]"),
+ &blacklistlist);
+
+ add_cmd ("delete", class_breakpoint, blacklist_delete_command, _("\
+Delete a blacklist entry.\n\
+blacklist delete [NUMBER]"),
+ &blacklistlist);
+
+ add_info ("blacklist", blacklist_info, _("\
+Status of blacklist, or of blacklist entry NUMBER.\n\
+The \"Type\" column indicates one of:\n\
+\tfile - blacklisted file\n\
+\tfunction - blacklisted function"));
+}
diff --git a/gdb/blacklist.h b/gdb/blacklist.h
new file mode 100644
index 0000000..b5b5c20
--- /dev/null
+++ b/gdb/blacklist.h
@@ -0,0 +1,27 @@
+/* Header for GDB line completion.
+ Copyright (C) 2010 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Returns 1 if the given pc was blacklisted and shouldn't be stepped into.
+ Otherwise, returns 0. */
+
+#if !defined (BlACKLIST_H)
+#define BLACKLIST_H
+
+/* Indicates whether the given pc is blacklisted and shouldn't be stepped
+ into. */
+int function_pc_is_blacklisted (CORE_ADDR pc);
+
+#endif /* !defined (BLACKLIST_H) */
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 74838e8..8f60bd4 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -61,6 +61,7 @@
#include "valprint.h"
#include "jit.h"
#include "xml-syscall.h"
+#include "stack.h"

/* readline include files */
#include "readline/readline.h"
@@ -527,19 +528,6 @@ make_cleanup_decref_counted_command_line (struct counted_command_line **cmdp)
return make_cleanup (do_cleanup_counted_command_line, cmdp);
}

-/* Default address, symtab and line to put a breakpoint at
- for "break" command with no arg.
- if default_breakpoint_valid is zero, the other three are
- not valid, and "break" with no arg is an error.
-
- This set by print_stack_frame, which calls set_default_breakpoint. */
-
-int default_breakpoint_valid;
-CORE_ADDR default_breakpoint_address;
-struct symtab *default_breakpoint_symtab;
-int default_breakpoint_line;
-struct program_space *default_breakpoint_pspace;
-
\f
/* *PP is a string denoting a breakpoint. Get the number of the breakpoint.
Advance *PP after the string and any trailing whitespace.
@@ -5174,20 +5162,6 @@ describe_other_breakpoints (struct gdbarch *gdbarch,
}
}
\f
-/* Set the default place to put a breakpoint
- for the `break' command with no arguments. */
-
-void
-set_default_breakpoint (int valid, struct program_space *pspace,
- CORE_ADDR addr, struct symtab *symtab,
- int line)
-{
- default_breakpoint_valid = valid;
- default_breakpoint_pspace = pspace;
- default_breakpoint_address = addr;
- default_breakpoint_symtab = symtab;
- default_breakpoint_line = line;
-}

/* Return true iff it is meaningful to use the address member of
BPT. For some breakpoint types, the address member is irrelevant
@@ -7120,20 +7094,23 @@ parse_breakpoint_sals (char **address,
if ((*address) == NULL
|| (strncmp ((*address), "if", 2) == 0 && isspace ((*address)[2])))
{
- if (default_breakpoint_valid)
+ /* The last displayed codepoint, if it's valid, is our default breakpoint
+ address. */
+ if (last_displayed_codepoint_is_valid ())
{
struct symtab_and_line sal;
init_sal (&sal); /* initialize to zeroes */
sals->sals = (struct symtab_and_line *)
xmalloc (sizeof (struct symtab_and_line));
- sal.pc = default_breakpoint_address;
- sal.line = default_breakpoint_line;
- sal.symtab = default_breakpoint_symtab;
- sal.pspace = default_breakpoint_pspace;
+
+ /* Set sal's pspace, pc, symtab, and line to the values corresponding
+ to the last call to print_frame_info. */
+ set_sal_to_last_displayed_codepoint (&sal);
+
sal.section = find_pc_overlay (sal.pc);

/* "break" without arguments is equivalent to "break *PC" where PC is
- the default_breakpoint_address. So make sure to set
+ the last displayed codepoint's address. So make sure to set
sal.explicit_pc to prevent GDB from trying to expand the list of
sals to include all other instances with the same symtab and line.
*/
@@ -7150,19 +7127,22 @@ parse_breakpoint_sals (char **address,
/* Force almost all breakpoints to be in terms of the
current_source_symtab (which is decode_line_1's default). This
should produce the results we want almost all of the time while
- leaving default_breakpoint_* alone.
+ leaving the last displayed codepoint pointers alone.
ObjC: However, don't match an Objective-C method name which
may have a '+' or '-' succeeded by a '[' */

struct symtab_and_line cursal = get_current_source_symtab_and_line ();

- if (default_breakpoint_valid
+ if (last_displayed_codepoint_is_valid ()
&& (!cursal.symtab
|| ((strchr ("+-", (*address)[0]) != NULL)
&& ((*address)[1] != '['))))
- *sals = decode_line_1 (address, 1, default_breakpoint_symtab,
- default_breakpoint_line, addr_string,
- not_found_ptr);
+ {
+ *sals = decode_line_1 (address, 1,
+ get_last_displayed_symtab (),
+ get_last_displayed_line (),
+ addr_string, not_found_ptr);
+ }
else
*sals = decode_line_1 (address, 1, (struct symtab *) NULL, 0,
addr_string, not_found_ptr);
@@ -8082,9 +8062,11 @@ until_break_command (char *arg, int from_tty, int anywhere)
/* Set a breakpoint where the user wants it and at return from
this function */

- if (default_breakpoint_valid)
- sals = decode_line_1 (&arg, 1, default_breakpoint_symtab,
- default_breakpoint_line, (char ***) NULL, NULL);
+ if (last_displayed_codepoint_is_valid ())
+ sals = decode_line_1 (&arg, 1,
+ get_last_displayed_symtab (),
+ get_last_displayed_line (),
+ (char ***) NULL, NULL);
else
sals = decode_line_1 (&arg, 1, (struct symtab *) NULL,
0, (char ***) NULL, NULL);
@@ -8672,10 +8654,11 @@ clear_command (char *arg, int from_tty)
xmalloc (sizeof (struct symtab_and_line));
make_cleanup (xfree, sals.sals);
init_sal (&sal); /* initialize to zeroes */
- sal.line = default_breakpoint_line;
- sal.symtab = default_breakpoint_symtab;
- sal.pc = default_breakpoint_address;
- sal.pspace = default_breakpoint_pspace;
+
+ /* Set sal's line, symtab, pc, and pspace to the values corresponding to
+ the last call to print_frame_info. If the codepoint is not valid,
+ this will set all the fields to 0. */
+ set_sal_to_last_displayed_codepoint (&sal);
if (sal.symtab == 0)
error (_("No source file specified."));

@@ -10180,7 +10163,8 @@ invalidate_bp_value_on_memory_change (CORE_ADDR addr, int len,
}
}

-/* Use default_breakpoint_'s, or nothing if they aren't valid. */
+/* Use the last displayed codepoint's values, or nothing
+ if they aren't valid. */

struct symtabs_and_lines
decode_line_spec_1 (char *string, int funfirstline)
@@ -10188,11 +10172,13 @@ decode_line_spec_1 (char *string, int funfirstline)
struct symtabs_and_lines sals;
if (string == 0)
error (_("Empty line specification."));
- if (default_breakpoint_valid)
- sals = decode_line_1 (&string, funfirstline,
- default_breakpoint_symtab,
- default_breakpoint_line,
- (char ***) NULL, NULL);
+ if (last_displayed_codepoint_is_valid ())
+ {
+ sals = decode_line_1 (&string, funfirstline,
+ get_last_displayed_symtab (),
+ get_last_displayed_line (),
+ (char ***) NULL, NULL);
+ }
else
sals = decode_line_1 (&string, funfirstline,
(struct symtab *) NULL, 0, (char ***) NULL, NULL);
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 8b7a5c6..50602b2 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -789,9 +789,6 @@ extern struct breakpoint *clone_momentary_breakpoint (struct breakpoint *bpkt);

extern void set_ignore_count (int, int, int);

-extern void set_default_breakpoint (int, struct program_space *,
- CORE_ADDR, struct symtab *, int);
-
extern void breakpoint_init_inferior (enum inf_context);

extern struct cleanup *make_cleanup_delete_breakpoint (struct breakpoint *);
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index fdeb8db..c401eed 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -185,6 +185,8 @@ struct cmd_list_element *setchecklist;

struct cmd_list_element *showchecklist;

+struct cmd_list_element *blacklistlist;
+
/* Command tracing state. */

int source_verbose = 0;
@@ -1308,6 +1310,7 @@ init_cmd_lists (void)
showprintlist = NULL;
setchecklist = NULL;
showchecklist = NULL;
+ blacklistlist = NULL;
}

static void
@@ -1372,7 +1375,7 @@ init_cli_cmds (void)
char *source_help_text;

/* Define the classes of commands.
- They will appear in the help list in the reverse of this order. */
+ They will appear in the help list in alphabetical order. */

add_cmd ("internals", class_maintenance, NULL, _("\
Maintenance commands.\n\
diff --git a/gdb/gdbcmd.h b/gdb/gdbcmd.h
index 6a230c0..d561e73 100644
--- a/gdb/gdbcmd.h
+++ b/gdb/gdbcmd.h
@@ -124,6 +124,8 @@ extern struct cmd_list_element *setchecklist;

extern struct cmd_list_element *showchecklist;

+extern struct cmd_list_element *blacklistlist;
+
extern void execute_command (char *, int);

enum command_control_type execute_control_command (struct command_line *);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 5f58759..7402fee 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -52,6 +52,7 @@
#include "inline-frame.h"
#include "jit.h"
#include "tracepoint.h"
+#include "blacklist.h"

/* Prototypes for local functions */

@@ -4490,7 +4491,8 @@ infrun: not switching back to stepped thread, it has vanished\n");
}

/* If we have line number information for the function we are
- thinking of stepping into, step into it.
+ thinking of stepping into and the function isn't blacklisted,
+ step into it.

If there are several symtabs at that PC (e.g. with include
files), just want to know whether *any* of them have line
@@ -4500,7 +4502,8 @@ infrun: not switching back to stepped thread, it has vanished\n");

tmp_sal = find_pc_line (ecs->stop_func_start, 0);
tmp_sal.pspace = get_frame_program_space (frame);
- if (tmp_sal.line != 0)
+ if (tmp_sal.line != 0 &&
+ !function_pc_is_blacklisted (ecs->stop_func_start))
{
if (execution_direction == EXEC_REVERSE)
handle_step_into_function_backward (gdbarch, ecs);
diff --git a/gdb/stack.c b/gdb/stack.c
index 53d4aeb..14febd9 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -73,6 +73,12 @@ static void print_frame (struct frame_info *frame, int print_level,
enum print_what print_what, int print_args,
struct symtab_and_line sal);

+static void set_last_displayed_codepoint (int valid,
+ struct program_space *pspace,
+ CORE_ADDR addr,
+ struct symtab *symtab,
+ int line);
+
/* Zero means do things normally; we are interacting directly with the
user. One means print the full filename and linenumber when a
frame is printed, and do so in a format emacs18/emacs19.22 can
@@ -80,6 +86,14 @@ static void print_frame (struct frame_info *frame, int print_level,
cases and in a slightly different syntax. */

int annotation_level = 0;
+
+/* These variables hold the last codepoint we displayed to the user. This is
+ where we insert a breakpoint or a blacklist entry by default. */
+static int last_codepoint_valid = 0;
+static struct program_space *last_codepoint_pspace = 0;
+static CORE_ADDR last_codepoint_addr = 0;
+static struct symtab *last_codepoint_symtab = 0;
+static int last_codepoint_line = 0;
\f

struct print_stack_frame_args
@@ -650,14 +664,96 @@ print_frame_info (struct frame_info *frame, int print_level,
}

if (print_what != LOCATION)
- set_default_breakpoint (1, sal.pspace,
- get_frame_pc (frame), sal.symtab, sal.line);
+ set_last_displayed_codepoint (1, sal.pspace,
+ get_frame_pc (frame), sal.symtab,
+ sal.line);

annotate_frame_end ();

gdb_flush (gdb_stdout);
}

+/* Remember the last codepoint we displayed, which we use e.g. as the place to
+ put a breakpoint when the `break' command is invoked with no arguments. */
+static void
+set_last_displayed_codepoint (int valid, struct program_space *pspace,
+ CORE_ADDR addr, struct symtab *symtab,
+ int line)
+{
+ last_codepoint_valid = valid;
+ last_codepoint_pspace = pspace;
+ last_codepoint_addr = addr;
+ last_codepoint_symtab = symtab;
+ last_codepoint_line = line;
+}
+
+void
+clear_last_displayed_codepoint ()
+{
+ last_codepoint_valid = 0;
+ last_codepoint_pspace = 0;
+ last_codepoint_addr = 0;
+ last_codepoint_symtab = 0;
+ last_codepoint_line = 0;
+}
+
+int
+last_displayed_codepoint_is_valid ()
+{
+ return last_codepoint_valid;
+}
+
+struct program_space*
+get_last_displayed_pspace ()
+{
+ if (last_codepoint_valid)
+ return last_codepoint_pspace;
+ return 0;
+}
+
+CORE_ADDR
+get_last_displayed_addr ()
+{
+ if (last_codepoint_valid)
+ return last_codepoint_addr;
+ return 0;
+}
+
+struct symtab*
+get_last_displayed_symtab ()
+{
+ if (last_codepoint_valid)
+ return last_codepoint_symtab;
+ return 0;
+}
+
+int
+get_last_displayed_line ()
+{
+ if (last_codepoint_valid)
+ return last_codepoint_line;
+ return 0;
+}
+
+void
+set_sal_to_last_displayed_codepoint (struct symtab_and_line *sal)
+{
+ if (last_codepoint_valid)
+ {
+ sal->pspace = last_codepoint_pspace;
+ sal->pc = last_codepoint_addr;
+ sal->symtab = last_codepoint_symtab;
+ sal->line = last_codepoint_line;
+ }
+ else
+ {
+ sal->pspace = 0;
+ sal->pc = 0;
+ sal->symtab = 0;
+ sal->line = 0;
+ }
+}
+
/* Attempt to obtain the FUNNAME and FUNLANG of the function corresponding
to FRAME. */
void
diff --git a/gdb/stack.h b/gdb/stack.h
index 5e874b4..fa08035 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -39,4 +39,14 @@ void iterate_over_block_local_vars (struct block *block,
iterate_over_block_arg_local_vars_cb cb,
void *cb_data);

+/* Get or set the last displayed codepoint, which is, e.g. where we set a
+ breakpoint when `break' is supplied with no arguments. */
+void clear_last_displayed_codepoint ();
+int last_displayed_codepoint_is_valid ();
+struct program_space* get_last_displayed_pspace ();
+CORE_ADDR get_last_displayed_addr ();
+struct symtab* get_last_displayed_symtab ();
+int get_last_displayed_line ();
+void set_sal_to_last_displayed_codepoint (struct symtab_and_line *sal);
+
#endif /* #ifndef STACK_H */
diff --git a/gdb/symfile.c b/gdb/symfile.c
index eda26cc..b56badf 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -56,6 +56,7 @@
#include "elf-bfd.h"
#include "solib.h"
#include "remote.h"
+#include "stack.h"

#include <sys/types.h>
#include <fcntl.h>
@@ -2709,7 +2710,7 @@ clear_symtab_users (void)

clear_displays ();
breakpoint_re_set ();
- set_default_breakpoint (0, NULL, 0, 0, 0);
+ clear_last_displayed_codepoint ();
clear_pc_function_cache ();
observer_notify_new_objfile (NULL);

diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 024054d..d62ff07 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,13 @@
+2010-06-18 Justin Lebar <justin.lebar@gmail.com>
+
+ testsuite/gdb.base/blacklist-solib-lib.c: New
+ testsuite/gdb.base/blacklist-solib-main.c: New
+ testsuite/gdb.base/blacklist-solib.exp: New
+ testsuite/gdb.base/blacklist.c: New
+ testsuite/gdb.base/blacklist.exp: New
+ testsuite/gdb.base/blacklist1.c: New
+ testsuite/gdb.base/Makefile.in: Adding new files.
+
2010-04-23 Jan Kratochvil <jan.kratochvil@redhat.com>

Fix deadlock on looped list of loaded shared objects.
diff --git a/gdb/testsuite/gdb.base/Makefile.in b/gdb/testsuite/gdb.base/Makefile.in
index 5e8e385..bd54184 100644
--- a/gdb/testsuite/gdb.base/Makefile.in
+++ b/gdb/testsuite/gdb.base/Makefile.in
@@ -1,7 +1,7 @@
VPATH = @srcdir@
srcdir = @srcdir@

-EXECUTABLES = all-types annota1 bitfields break \
+EXECUTABLES = all-types annota1 bitfields blacklist blacklist-solib break \
call-ar-st call-rt-st call-strs callfuncs callfwmall \
chng-syms commands compiler condbreak constvars coremaker \
dbx-test display ending-run execd-prog exprs \
diff --git a/gdb/testsuite/gdb.base/blacklist-solib-lib.c b/gdb/testsuite/gdb.base/blacklist-solib-lib.c
new file mode 100644
index 0000000..792cd01
--- /dev/null
+++ b/gdb/testsuite/gdb.base/blacklist-solib-lib.c
@@ -0,0 +1,11 @@
+/* Simple shared library */
+
+int square(int num)
+{
+ return multiply(num, num);
+}
+
+int multiply(int a, int b)
+{
+ return a * b;
+}
diff --git a/gdb/testsuite/gdb.base/blacklist-solib-main.c b/gdb/testsuite/gdb.base/blacklist-solib-main.c
new file mode 100644
index 0000000..746bb5f
--- /dev/null
+++ b/gdb/testsuite/gdb.base/blacklist-solib-main.c
@@ -0,0 +1,6 @@
+int square(int num);
+
+int main()
+{
+ return square(0);
+}
diff --git a/gdb/testsuite/gdb.base/blacklist-solib.exp b/gdb/testsuite/gdb.base/blacklist-solib.exp
new file mode 100644
index 0000000..d6b8bb5
--- /dev/null
+++ b/gdb/testsuite/gdb.base/blacklist-solib.exp
@@ -0,0 +1,129 @@
+# Copyright 2010 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This file was written by Justin Lebar. (justin.lebar@gmail.com)
+
+#
+# Tests blacklisting shared libraries.
+#
+
+# This only works on GNU/Linux.
+if { ![isnative] || [is_remote host] || ![istarget *-linux*] || [skip_shlib_tests]} {
+ continue
+}
+
+set test "blacklist-solib"
+set srcfile_main "${test}-main.c"
+set binfile_main "${objdir}/${subdir}/${test}-test"
+set srcfile_lib "${test}-lib.c"
+set libname "lib${test}"
+set binfile_lib ${objdir}/${subdir}/${libname}.so
+
+#
+# Compile our program under test. The main program references a shared library
+# libblacklist-solib.so, which contains two functions, square(), which is
+# referenced by the main program, and multiply(), which is not referenced by
+# the main program.
+#
+
+if {[gdb_compile_shlib ${srcdir}/${subdir}/${srcfile_lib} ${binfile_lib} [list debug additional_flags=-fPIC -Wl,-soname,${libname}.so]] != ""} {
+ return -1
+}
+
+if {[gdb_compile "${srcdir}/${subdir}/${srcfile_main}" "${binfile_main}.o" object debug] != ""} {
+ return -1
+}
+
+if {[gdb_compile "${binfile_main}.o" "${binfile_main}" executable \
+ [list debug "additional_flags=-L${objdir}/${subdir} -l${test} \
+ -Wl,-rpath=${objdir}/${subdir}"]] != ""} {
+ return -1
+}
+
+gdb_start
+gdb_load ${binfile_main}
+
+#
+# At this point, if we try to blacklist the file ${srcfile_lib} or the function
+# multiply(), we should get a prompt asking us if we want to enable the
+# blacklist entry pending a shared library load.
+#
+
+gdb_test "blacklist file ${srcfile_lib}" \
+"Blacklisting file ${srcfile_lib}." \
+"blacklisting file in solib" \
+"No source file named ${srcfile_lib}.*
+Add file to blacklist pending future shared library load.*"\
+"y"
+
+#
+# Does info blacklist list this entry as pending?
+#
+gdb_test "info blacklist" \
+"Num\\s+Type\\s+Enb\\s+Address\\s+What\\s*
+1\\s+file\\s+y\\s+n/a\\s+${srcfile_lib} \\(PENDING\\)\\s*" \
+"info blacklist with pending file"
+
+if ![runto_main] { fail "blacklist tests suppressed" }
+
+#
+# We shouldn't step into square(), since we blacklisted blacklist-solib-lib.c.
+#
+gdb_test "step" ""
+gdb_test "bt" "#0\\s+main.*" "step after blacklisting solib file."
+
+#
+# Our entry should no longer be pending. Note that we unfortunately need to do
+# at least one step before the entry will be unmarked as pending.
+#
+gdb_test "info blacklist" \
+"Num\\s+Type\\s+Enb\\s+Address\\s+What\\s*
+1\\s+file\\s+y\\s+n/a\\s+.*${srcfile_lib}\\s*" \
+"info blacklist with pending file"
+
+#
+# Now restart gdb and testing blacklisting of a function inside a solib.
+#
+gdb_exit
+gdb_start
+gdb_load ${binfile_main}
+
+gdb_test "blacklist function multiply" \
+"Blacklisting function multiply pending shared library load." \
+"blacklisting function in solib" \
+"No function found named multiply..*
+Add function to blacklist pending future shared library load.*"\
+"y"
+
+if ![runto_main] { fail "blacklist tests suppressed" }
+
+#
+# Our first step should take us into square.
+#
+gdb_test "step" "square.*"
+
+#
+# Now our entry should no longer be pending.
+#
+gdb_test "info blacklist" \
+"Num\\s+Type\\s+Enb\\s+Address\\s+What\\s*
+1\\s+function\\s+y\\s+0x\[0-9a-f\]+\\s+multiply at .*${srcfile_lib}:.*\\s*" \
+
+#
+# This step shouldn't go into multiply -- we should skip it and go on to the
+# last line of square.
+#
+gdb_test "step" ""
+gdb_test "bt" "#0\\s+square.*"
diff --git a/gdb/testsuite/gdb.base/blacklist.c b/gdb/testsuite/gdb.base/blacklist.c
new file mode 100644
index 0000000..565ba93
--- /dev/null
+++ b/gdb/testsuite/gdb.base/blacklist.c
@@ -0,0 +1,13 @@
+int foo();
+int bar();
+int baz(int, int);
+
+int main()
+{
+ return baz(foo(), bar());
+}
+
+int foo()
+{
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.base/blacklist.exp b/gdb/testsuite/gdb.base/blacklist.exp
new file mode 100644
index 0000000..ed1afb8
--- /dev/null
+++ b/gdb/testsuite/gdb.base/blacklist.exp
@@ -0,0 +1,140 @@
+# Copyright 2010 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This file was written by Justin Lebar. (justin.lebar@gmail.com)
+
+if { [prepare_for_testing blacklist.exp "blacklist" \
+ {blacklist.c blacklist1.c } \
+ {debug nowarnings}] } {
+ return -1
+}
+
+set srcfile blacklist.c
+set srcfile1 blacklist1.c
+
+#
+# Right after we start gdb, there's no default file or function to blacklist.
+#
+gdb_test "blacklist file" "No default blacklist file now."
+gdb_test "blacklist function" "No default blacklist function now."
+gdb_test "blacklist" "No default blacklist function now."
+
+if ![runto_main] { fail "blacklist tests suppressed" }
+
+#
+# Test |info blacklist| with an empty blacklist.
+#
+gdb_test "info blacklist" "Blacklist is empty." "info blacklist empty"
+
+#
+# Create a blacklist entry for the current file and function.
+#
+gdb_test "blacklist file" "Blacklisting file .*$srcfile."
+gdb_test "blacklist" "Blacklisting function main() at .*\."
+
+#
+# Create a blacklist entry for a specified file and function.
+#
+gdb_test "blacklist file blacklist1.c" "Blacklisting file .*$srcfile1."
+gdb_test "blacklist function baz" "Blacklisting function baz at .*"
+
+#
+# Test bad blacklist entry modification commands
+#
+gdb_test "blacklist enable 999" "No blacklist entry numbered 999."
+gdb_test "blacklist disable 999" "No blacklist entry numbered 999."
+gdb_test "blacklist delete 999" "No blacklist entry numbered 999."
+gdb_test "blacklist enable" "Argument required \\(expression to compute\\)."
+gdb_test "blacklist disable" "Argument required \\(expression to compute\\)."
+gdb_test "blacklist delete" "Argument required \\(expression to compute\\)."
+gdb_test "blacklist enable a" "No symbol \"a\" in current context."
+gdb_test "blacklist disable a" "No symbol \"a\" in current context."
+gdb_test "blacklist delete a" "No symbol \"a\" in current context."
+
+#
+# Test that blacklist function doesn't allow extra characters at the end of its
+# arguments list.
+#
+gdb_test "blacklist function foo bar" "Junk at end of arguments."
+
+#
+# Ask for info on a blacklist entry which doesn't exist.
+#
+gdb_test "info blacklist 999" "No blacklist entry numbered 999."
+
+#
+# Does |info blacklist| look right?
+#
+gdb_test "info blacklist" \
+"Num\\s+Type\\s+Enb\\s+Address\\s+What\\s*
+1\\s+file\\s+y\\s+n/a.*$srcfile\\s*
+2\\s+function\\s+y\\s+0x\[0-9a-f\]+ main at .*$srcfile:\[0-9\]+\\s*
+3\\s+file\\s+y\\s+n/a.*$srcfile1\\s*
+4\\s+function\\s+y\\s+0x\[0-9a-f\]+ baz at .*$srcfile1:\[0-9\]+\\s*"
+
+#
+# Right now, we have an outstanding blacklist on both source files, so when we
+# step into the first line in main(), we should step right over it and go to
+# the second line of main().
+#
+if ![runto_main] { fail "blacklist tests suppressed" }
+send_gdb "step\n"
+gdb_test "bt" "#0\\s+main.*" "step after all blacklisted"
+
+#
+# Now remove blacklist.c from the blacklist. Our first step should take us
+# into foo(), and our second step should take us to the next line in main().
+#
+send_gdb "blacklist delete 1\n"
+# Check that entry 1 is missing from |info blacklist|
+gdb_test "info blacklist" \
+"Num\\s+Type\\s+Enb\\s+Address\\s+What\\s*
+2\\s+function\\s+y\\s+0x\[0-9a-f\]+ main at .*$srcfile:\[0-9\]+\\s*
+3\\s+file\\s+y\\s+n/a.*$srcfile1\\s*
+4\\s+function\\s+y\\s+0x\[0-9a-f\]+ baz at .*$srcfile1:\[0-9\]+\\s*"
+
+if ![runto_main] { fail "blacklist tests suppressed" }
+gdb_test "step" "foo \\(\\) at.*" "step after deleting 1 (1)"
+send_gdb "step\n"; # Return from foo()
+gdb_test "step" "main \\(\\) at.*" "step after deleting 1 (2)"
+
+#
+# Now disable our blacklisting of blacklist1.c. We should now step into foo(),
+# then into bar(), but not into baz().
+#
+send_gdb "blacklist disable 3\n"
+# Is entry 3 disabled in |info blacklist|?
+gdb_test "info blacklist 3" ".*\\n3\\s+file\\s+n.*" \
+ "info blacklist shows entry as disabled"
+
+if ![runto_main] { fail "blacklist tests suppressed" }
+gdb_test "step" "bar \\(\\) at.*" "step after disabling 3 (1)"
+send_gdb "step\n"; # Return from foo()
+gdb_test "step" "foo \\(\\) at.*" "step after disabling 3 (2)"
+send_gdb "step\n"; # Return from bar()
+gdb_test "step" "main \\(\\) at.*" "step after disabling 3 (3)"
+
+#
+# Enable blacklist entry 3 and make sure we step over it like before.
+#
+send_gdb "blacklist enable 3\n"
+# Is entry 3 enabled in |info blacklist|?
+gdb_test "info blacklist 3" ".*\\n3\\s+file\\s+y.*" \
+ "info blacklist shows entry as enabled"
+if ![runto_main] { fail "blacklist tests suppressed" }
+gdb_test "step" "foo \\(\\) at.*" "step after deleting 1 (1)"
+send_gdb "step\n"; # Return from foo()
+gdb_test "step" "main \\(\\) at.*" "step after deleting 1 (2)"
+
diff --git a/gdb/testsuite/gdb.base/blacklist1.c b/gdb/testsuite/gdb.base/blacklist1.c
new file mode 100644
index 0000000..2dab5c3
--- /dev/null
+++ b/gdb/testsuite/gdb.base/blacklist1.c
@@ -0,0 +1,9 @@
+int bar()
+{
+ return 1;
+}
+
+int baz(int a, int b)
+{
+ return a + b;
+}

[-- Attachment #3: bug11614 --]
[-- Type: application/octet-stream, Size: 1806 bytes --]

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 148219e..969f31d 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,11 @@
+2010-06-25 Justin Lebar <justin.lebar@gmail.com>
+
+ * linespec.c (decode_variable): Passing a non-null not_found_ptr
+ prevents an error from being thrown if the function cannot be found.
+ * breakpoint.c (create_breakpoint): Pass a null not_found_ptr to
+ decode_variable so we get an exception when a function cannot be
+ found, as desired.
+
2010-04-24 Pedro Alves <pedro@codesourcery.com>

* defs.h: Adjust comment.
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 3dca17e..74838e8 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -7336,7 +7336,6 @@ create_breakpoint (struct gdbarch *gdbarch,
struct captured_parse_breakpoint_args parse_args;
int i;
int pending = 0;
- int not_found = 0;
enum bptype type_wanted;
int task = 0;
int prev_bkpt_count = breakpoint_count;
@@ -7348,7 +7347,7 @@ create_breakpoint (struct gdbarch *gdbarch,
parse_args.arg_p = &arg;
parse_args.sals_p = &sals;
parse_args.addr_string_p = &addr_string;
- parse_args.not_found_ptr = &not_found;
+ parse_args.not_found_ptr = 0;

e = catch_exception (uiout, do_captured_parse_breakpoint,
&parse_args, RETURN_MASK_ALL);
diff --git a/gdb/linespec.c b/gdb/linespec.c
index c5ea28a..19727b5 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -1857,7 +1857,13 @@ decode_variable (char *copy, int funfirstline, char ***canonical,
return minsym_found (funfirstline, msymbol);

if (not_found_ptr)
- *not_found_ptr = 1;
+ {
+ struct symtabs_and_lines sals;
+ *not_found_ptr = 1;
+ sals.sals = 0;
+ sals.nelts = 0;
+ return sals;
+ }

if (!have_full_symbols ()
&& !have_partial_symbols ()