* breakpoint.c (remove_sal): New.
authorVladimir Prus <vladimir@codesourcery.com>
Mon, 24 Sep 2007 07:40:32 +0000 (07:40 +0000)
committerVladimir Prus <vladimir@codesourcery.com>
Mon, 24 Sep 2007 07:40:32 +0000 (07:40 +0000)
(expand_line_sal_maybe): New.
(create_breakpoints): Call expand_line_sal_maybe.
(clear_command): Add comment.
(breakpoint_re_set_one): Call expand_line_sal_maybe.
* linespec.c (decode_indirect): Set explicit_pc to 1.
(decode_all_digits): Set explicit_line to 1.
(append_expanded_sal): New.
(expand_line_sal): New.
* linespec.h (expand_line_sal): Declare.
* symtab.c (init_sal): Initialize explicit_pc
and explicit_line.
* symtab.h (struct symtab_and_line): New fields
explicit_pc and explicit_line.

gdb/ChangeLog
gdb/breakpoint.c
gdb/linespec.c
gdb/symtab.c
gdb/symtab.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.cp/mb-ctor.cc [new file with mode: 0644]
gdb/testsuite/gdb.cp/mb-ctor.exp [new file with mode: 0644]
gdb/testsuite/gdb.cp/mb-templates.cc [new file with mode: 0644]
gdb/testsuite/gdb.cp/mb-templates.exp [new file with mode: 0644]

index 3c553583c8e94fd508df6b2ffc580a254fe075a8..5e7fd7726e406d4bfdb128de53c1ce118585c0d3 100644 (file)
@@ -1,3 +1,20 @@
+2007-09-24  Vladimir Prus  <vladimir@codesourcery.com>
+       
+       * breakpoint.c (remove_sal): New.
+       (expand_line_sal_maybe): New.
+       (create_breakpoints): Call expand_line_sal_maybe.
+       (clear_command): Add comment.
+       (breakpoint_re_set_one): Call expand_line_sal_maybe.
+       * linespec.c (decode_indirect): Set explicit_pc to 1.
+       (decode_all_digits): Set explicit_line to 1.
+       (append_expanded_sal): New.
+       (expand_line_sal): New.
+       * linespec.h (expand_line_sal): Declare.
+       * symtab.c (init_sal): Initialize explicit_pc 
+       and explicit_line.
+       * symtab.h (struct symtab_and_line): New fields
+       explicit_pc and explicit_line.  
+
 2007-09-23  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * infcall.c (call_function_by_hand): Handle language-specific
index e5ab6b346b981b1255a5d472857f1052094ee072..3bf87d59506f73c605fedbdf76e35c758ae2db51 100644 (file)
@@ -5184,6 +5184,128 @@ create_breakpoint (struct symtabs_and_lines sals, char *addr_string,
   mention (b);
 }
 
+/* Remove element at INDEX_TO_REMOVE from SAL, shifting other
+   elements to fill the void space.  */
+static void remove_sal (struct symtabs_and_lines *sal, int index_to_remove)
+{
+  int i = index_to_remove+1;
+  int last_index = sal->nelts-1;
+
+  for (;i <= last_index; ++i)
+    sal->sals[i-1] = sal->sals[i];
+
+  --(sal->nelts);
+}
+
+/* If appropriate, obtains all sals that correspond
+   to the same file and line as SAL.  This is done
+   only if SAL does not have explicit PC and has
+   line and file information.  If we got just a single
+   expanded sal, return the original.
+
+   Otherwise, if SAL.explicit_line is not set, filter out 
+   all sals for which the name of enclosing function 
+   is different from SAL. This makes sure that if we have
+   breakpoint originally set in template instantiation, say
+   foo<int>(), we won't expand SAL to locations at the same
+   line in all existing instantiations of 'foo'.
+
+*/
+struct symtabs_and_lines
+expand_line_sal_maybe (struct symtab_and_line sal)
+{
+  struct symtabs_and_lines expanded;
+  CORE_ADDR original_pc = sal.pc;
+  char *original_function = NULL;
+  int found;
+  int i;
+
+  /* If we have explicit pc, don't expand.
+     If we have no line number, we can't expand.  */
+  if (sal.explicit_pc || sal.line == 0 || sal.symtab == NULL)
+    {
+      expanded.nelts = 1;
+      expanded.sals = xmalloc (sizeof (struct symtab_and_line));
+      expanded.sals[0] = sal;
+      return expanded;
+    }
+
+  sal.pc = 0;
+  find_pc_partial_function (original_pc, &original_function, NULL, NULL);
+  
+  expanded = expand_line_sal (sal);
+  if (expanded.nelts == 1)
+    {
+      /* We had one sal, we got one sal.  Without futher
+        processing, just return the original sal.  */
+      xfree (expanded.sals);
+      expanded.nelts = 1;
+      expanded.sals = xmalloc (sizeof (struct symtab_and_line));
+      sal.pc = original_pc;
+      expanded.sals[0] = sal;
+      return expanded;      
+    }
+
+  if (!sal.explicit_line)
+    {
+      CORE_ADDR func_addr, func_end;
+      for (i = 0; i < expanded.nelts; ++i)
+       {
+         CORE_ADDR pc = expanded.sals[i].pc;
+         char *this_function;
+         if (find_pc_partial_function (pc, &this_function, 
+                                       &func_addr, &func_end))
+           {
+             if (this_function && 
+                 strcmp (this_function, original_function) != 0)
+               {
+                 remove_sal (&expanded, i);
+                 --i;
+               }
+             else if (func_addr == pc)     
+               {            
+                 /* We're at beginning of a function, and should
+                    skip prologue.  */
+                 struct symbol *sym = find_pc_function (pc);
+                 if (sym)
+                   expanded.sals[i] = find_function_start_sal (sym, 1);
+                 else
+                   expanded.sals[i].pc 
+                     = gdbarch_skip_prologue (current_gdbarch, pc);
+               }
+           }
+       }
+    }
+
+  
+  if (expanded.nelts <= 1)
+    {
+      /* This is un ugly workaround. If we get zero
+       expanded sals then something is really wrong.
+      Fix that by returnign the original sal. */
+      xfree (expanded.sals);
+      expanded.nelts = 1;
+      expanded.sals = xmalloc (sizeof (struct symtab_and_line));
+      sal.pc = original_pc;
+      expanded.sals[0] = sal;
+      return expanded;      
+    }
+
+  if (original_pc)
+    {
+      found = 0;
+      for (i = 0; i < expanded.nelts; ++i)
+       if (expanded.sals[i].pc == original_pc)
+         {
+           found = 1;
+           break;
+         }
+      gdb_assert (found);
+    }
+
+  return expanded;
+}
+
 /* Add SALS.nelts breakpoints to the breakpoint table.  For each
    SALS.sal[i] breakpoint, include the corresponding ADDR_STRING[i]
    value.  COND_STRING, if not NULL, specified the condition to be
@@ -5214,11 +5336,10 @@ create_breakpoints (struct symtabs_and_lines sals, char **addr_string,
   int i;
   for (i = 0; i < sals.nelts; ++i)
     {
-      struct symtabs_and_lines sals2;
-      sals2.sals = sals.sals + i;
-      sals2.nelts = 1;
+      struct symtabs_and_lines expanded = 
+       expand_line_sal_maybe (sals.sals[i]);
 
-      create_breakpoint (sals2, addr_string[i],
+      create_breakpoint (expanded, addr_string[i],
                         cond_string, type, disposition,
                         thread, ignore_count, from_tty,
                         pending_bp);
@@ -6889,6 +7010,23 @@ clear_command (char *arg, int from_tty)
       default_match = 1;
     }
 
+  /* We don't call resolve_sal_pc here. That's not
+     as bad as it seems, because all existing breakpoints
+     typically have both file/line and pc set.  So, if
+     clear is given file/line, we can match this to existing
+     breakpoint without obtaining pc at all.
+
+     We only support clearing given the address explicitly 
+     present in breakpoint table.  Say, we've set breakpoint 
+     at file:line. There were several PC values for that file:line,
+     due to optimization, all in one block.
+     We've picked one PC value. If "clear" is issued with another
+     PC corresponding to the same file:line, the breakpoint won't
+     be cleared.  We probably can still clear the breakpoint, but 
+     since the other PC value is never presented to user, user
+     can only find it by guessing, and it does not seem important
+     to support that.  */
+
   /* For each line spec given, delete bps which correspond
      to it.  Do it in two passes, solely to preserve the current
      behavior that from_tty is forced true if we delete more than
@@ -7404,8 +7542,12 @@ update_breakpoint_locations (struct breakpoint *b,
       }
   }
 
-  if (existing_locations)
-    free_bp_location (existing_locations);
+  while (existing_locations)
+    {
+      struct bp_location *next = existing_locations->next;
+      free_bp_location (existing_locations);
+      existing_locations = next;
+    }
 }
 
 
@@ -7423,6 +7565,7 @@ breakpoint_re_set_one (void *bint)
   int not_found = 0;
   int *not_found_ptr = &not_found;
   struct symtabs_and_lines sals = {};
+  struct symtabs_and_lines expanded;
   char *s;
   enum enable_state save_enable;
   struct gdb_exception e;
@@ -7497,8 +7640,8 @@ breakpoint_re_set_one (void *bint)
          b->thread = thread;
          b->condition_not_parsed = 0;
        }
-
-      update_breakpoint_locations (b, sals);
+      expanded = expand_line_sal_maybe (sals.sals[0]);
+      update_breakpoint_locations (b, expanded);
 
       /* Now that this is re-enabled, check_duplicates
         can be used. */
index 5c6f756a99aef32a1c72a4de22feed0a7f9cd8a9..b2ffcdee4be2b6ae300828c4aebbe9e64e97cba1 100644 (file)
@@ -963,6 +963,7 @@ decode_indirect (char **argptr)
   values.sals[0] = find_pc_line (pc, 0);
   values.sals[0].pc = pc;
   values.sals[0].section = find_pc_overlay (pc);
+  values.sals[0].explicit_pc = 1;
 
   return values;
 }
@@ -1633,6 +1634,7 @@ decode_all_digits (char **argptr, struct symtab *default_symtab,
   values.nelts = 1;
   if (need_canonical)
     build_canonical_line_spec (values.sals, NULL, canonical);
+  values.sals[0].explicit_line = 1;
   return values;
 }
 
index b4743faaceca3f109728f8c6421e57d44b3e85bd..c2726d4666b7ddcded0edf16f70764aa4ef7fa03 100644 (file)
@@ -691,6 +691,8 @@ init_sal (struct symtab_and_line *sal)
   sal->line = 0;
   sal->pc = 0;
   sal->end = 0;
+  sal->explicit_pc = 0;
+  sal->explicit_line = 0;
 }
 \f
 
@@ -4172,6 +4174,166 @@ symtab_observer_executable_changed (void *unused)
   set_main_name (NULL);
 }
 
+/* Helper to expand_line_sal below.  Appends new sal to SAL,
+   initializing it from SYMTAB, LINENO and PC.  */
+static void
+append_expanded_sal (struct symtabs_and_lines *sal,
+                    struct symtab *symtab,
+                    int lineno, CORE_ADDR pc)
+{
+  CORE_ADDR func_addr, func_end;
+  
+  sal->sals = xrealloc (sal->sals, 
+                       sizeof (sal->sals[0]) 
+                       * (sal->nelts + 1));
+  init_sal (sal->sals + sal->nelts);
+  sal->sals[sal->nelts].symtab = symtab;
+  sal->sals[sal->nelts].section = NULL;
+  sal->sals[sal->nelts].end = 0;
+  sal->sals[sal->nelts].line = lineno;  
+  sal->sals[sal->nelts].pc = pc;
+  ++sal->nelts;      
+}
+
+/* Compute a set of all sals in
+   the entire program that correspond to same file
+   and line as SAL and return those.  If there
+   are several sals that belong to the same block,
+   only one sal for the block is included in results.  */
+   
+struct symtabs_and_lines
+expand_line_sal (struct symtab_and_line sal)
+{
+  struct symtabs_and_lines ret, this_line;
+  int i, j;
+  struct objfile *objfile;
+  struct partial_symtab *psymtab;
+  struct symtab *symtab;
+  int lineno;
+  int deleted = 0;
+  struct block **blocks = NULL;
+  int *filter;
+
+  ret.nelts = 0;
+  ret.sals = NULL;
+
+  if (sal.symtab == NULL || sal.line == 0 || sal.pc != 0)
+    {
+      ret.sals = xmalloc (sizeof (struct symtab_and_line));
+      ret.sals[0] = sal;
+      ret.nelts = 1;
+      return ret;
+    }
+  else
+    {
+      struct linetable_entry *best_item = 0;
+      struct symtab *best_symtab = 0;
+      int exact = 0;
+
+      lineno = sal.line;
+
+      /* We meed to find all symtabs for a file which name
+        is described by sal. We cannot just directly 
+        iterate over symtabs, since a symtab might not be
+        yet created. We also cannot iterate over psymtabs,
+        calling PSYMTAB_TO_SYMTAB and working on that symtab,
+        since PSYMTAB_TO_SYMTAB will return NULL for psymtab
+        corresponding to an included file. Therefore, we do
+        first pass over psymtabs, reading in those with
+        the right name.  Then, we iterate over symtabs, knowing
+        that all symtabs we're interested in are loaded.  */
+
+      ALL_PSYMTABS (objfile, psymtab)
+       {
+         if (strcmp (sal.symtab->filename,
+                     psymtab->filename) == 0)
+           PSYMTAB_TO_SYMTAB (psymtab);
+       }
+
+        
+      /* For each symtab, we add all pcs to ret.sals. I'm actually
+        not sure what to do if we have exact match in one symtab,
+        and non-exact match on another symtab.
+      */
+      ALL_SYMTABS (objfile, symtab)
+       {
+         if (strcmp (sal.symtab->filename,
+                     symtab->filename) == 0)
+           {
+             struct linetable *l;
+             int len;
+             l = LINETABLE (symtab);
+             if (!l)
+               continue;
+             len = l->nitems;
+
+             for (j = 0; j < len; j++)
+               {
+                 struct linetable_entry *item = &(l->item[j]);
+
+                 if (item->line == lineno)
+                   {
+                     exact = 1;
+                     append_expanded_sal (&ret, symtab, lineno, item->pc);
+                   }      
+                 else if (!exact && item->line > lineno
+                          && (best_item == NULL || item->line < best_item->line))
+                 
+                   {
+                     best_item = item;
+                     best_symtab = symtab;
+                   }
+               }
+           }
+       }
+      if (!exact && best_item)
+       append_expanded_sal (&ret, best_symtab, lineno, best_item->pc);
+    }
+
+  /* For optimized code, compiler can scatter one source line accross
+     disjoint ranges of PC values, even when no duplicate functions
+     or inline functions are involved.  For example, 'for (;;)' inside
+     non-template non-inline non-ctor-or-dtor function can result
+     in two PC ranges.  In this case, we don't want to set breakpoint
+     on first PC of each range.  To filter such cases, we use containing
+     blocks -- for each PC found above we see if there are other PCs
+     that are in the same block.  If yes, the other PCs are filtered out.  */  
+
+  filter = xmalloc (ret.nelts * sizeof (int));
+  blocks = xmalloc (ret.nelts * sizeof (struct block *));
+  for (i = 0; i < ret.nelts; ++i)
+    {
+      filter[i] = 1;
+      blocks[i] = block_for_pc (ret.sals[i].pc);
+    }
+
+  for (i = 0; i < ret.nelts; ++i)
+    if (blocks[i] != NULL)
+      for (j = i+1; j < ret.nelts; ++j)
+       if (blocks[j] == blocks[i])
+         {
+           filter[j] = 0;
+           ++deleted;
+           break;
+         }
+  
+  {
+    struct symtab_and_line *final = 
+      xmalloc (sizeof (struct symtab_and_line) * (ret.nelts-deleted));
+    
+    for (i = 0, j = 0; i < ret.nelts; ++i)
+      if (filter[i])
+       final[j++] = ret.sals[i];
+    
+    ret.nelts -= deleted;
+    xfree (ret.sals);
+    ret.sals = final;
+  }
+
+  return ret;
+}
+
+
 void
 _initialize_symtab (void)
 {
index dece0a3507da984e8c9b3e5d2dedb898c8c4f958..c50f087a751308f253507b7ae1deb020010f7e53 100644 (file)
@@ -1213,6 +1213,8 @@ struct symtab_and_line
 
   CORE_ADDR pc;
   CORE_ADDR end;
+  int explicit_pc;
+  int explicit_line;
 };
 
 extern void init_sal (struct symtab_and_line *sal);
@@ -1404,5 +1406,7 @@ struct symbol *lookup_global_symbol_from_objfile (const struct objfile *objfile,
                                                  const domain_enum domain,
                                                  struct symtab **symtab);
 
+extern struct symtabs_and_lines
+expand_line_sal (struct symtab_and_line sal);
 
 #endif /* !defined(SYMTAB_H) */
index ae563f5bfc6af919f0e75e7f805627211f17c716..4beda4853049dced05a74aca8719a5ffe7000360 100644 (file)
@@ -1,3 +1,10 @@
+2007-09-24  Vladimir Prus  <vladimir@codesourcery.com>
+
+       * gdb.cp/mb-ctor.cc: New.
+       * gdb.cp/mb-ctor.exp: New.
+       * gdb.cp/mb-templates.cc: New.
+       * gdb.cp/mb-templates.exp: New.
+
 2007-09-23  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * gdb.cp/pass-by-ref.cc, gdb.cp/pass-by-ref.exp: New files.
diff --git a/gdb/testsuite/gdb.cp/mb-ctor.cc b/gdb/testsuite/gdb.cp/mb-ctor.cc
new file mode 100644 (file)
index 0000000..48a8c5f
--- /dev/null
@@ -0,0 +1,58 @@
+
+#include <stdio.h>
+
+class Base 
+{
+public:
+  Base(int k);
+  ~Base();
+  virtual void foo() {}
+private:
+  int k;
+};
+
+Base::Base(int k)
+{
+  this->k = k;
+}
+
+Base::~Base()
+{
+    printf("~Base\n");
+}
+
+class Derived : public virtual Base
+{
+public:
+  Derived(int i);
+  ~Derived();
+private:
+  int i;
+};
+
+Derived::Derived(int i) : Base(i)
+{
+  this->i = i;
+}
+
+Derived::~Derived()
+{
+    printf("~Derived\n");
+}
+
+class DeeplyDerived : public Derived
+{
+public:
+  DeeplyDerived(int i) : Base(i), Derived(i) {}
+};
+
+int main()
+{
+  /* Invokes the Derived ctor that constructs both
+     Derived and Base.  */
+  Derived d(7);
+  /* Invokes the Derived ctor that constructs only
+     Derived. Base is constructed separately by
+     DeeplyDerived's ctor.  */
+  DeeplyDerived dd(15);
+}
diff --git a/gdb/testsuite/gdb.cp/mb-ctor.exp b/gdb/testsuite/gdb.cp/mb-ctor.exp
new file mode 100644 (file)
index 0000000..74a5582
--- /dev/null
@@ -0,0 +1,86 @@
+# Copyright 2007
+# 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/>.
+
+# Test that breakpoints on C++ constructors work, despite the
+# fact that gcc generates several versions of constructor function.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+set testfile "mb-ctor"
+set srcfile ${testfile}.cc
+set binfile ${objdir}/${subdir}/${testfile}
+
+if [get_compiler_info ${binfile} "c++"] {
+    return -1
+}
+
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {
+     untested mb-ctor.exp
+     return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+# Set a breakpoint with multiple locations
+# and a condition.
+
+gdb_test "break 'Derived::Derived(int)'" \
+    "Breakpoint.*at.* file .*$srcfile, line.*\\(2 locations\\).*" \
+    "set-breakpoint at ctor"
+
+gdb_test "break 'Derived::~Derived()'" \
+    "Breakpoint.*at.* file .*$srcfile, line.*\\(2 locations\\).*" \
+    "set-breakpoint at ctor"
+
+gdb_run_cmd
+gdb_expect {
+    -re "Breakpoint \[0-9\]+,.*Derived.*i=7.*$gdb_prompt $" {
+       pass "run to breakpoint"
+    }
+    -re "$gdb_prompt $" {
+       fail "run to breakpoint"
+    }
+    timeout {
+       fail "run to breakpoint (timeout)"
+    }
+}
+
+gdb_test "continue" \
+    ".*Breakpoint.*Derived.*i=15.*" \
+    "run to breakpoint 2"
+
+gdb_test "continue" \
+    ".*Breakpoint.*~Derived.*" \
+    "run to breakpoint 3"
+
+gdb_test "continue" \
+    ".*Breakpoint.*~Derived.*" \
+    "run to breakpoint 4"
+
+gdb_test "continue" \
+    ".*exited normally.*" \
+    "run to exit"
+
+
+
diff --git a/gdb/testsuite/gdb.cp/mb-templates.cc b/gdb/testsuite/gdb.cp/mb-templates.cc
new file mode 100644 (file)
index 0000000..a7d4e2e
--- /dev/null
@@ -0,0 +1,19 @@
+
+#include <iostream>
+using namespace std;
+
+template<class T>
+void foo(T i)
+{
+  std::cout << "hi\n"; // set breakpoint here
+}
+
+int main()
+{
+    foo<int>(0);
+    foo<double>(0);
+    foo<int>(1);
+    foo<double>(1);
+    foo<int>(2);
+    foo<double>(2);
+}
diff --git a/gdb/testsuite/gdb.cp/mb-templates.exp b/gdb/testsuite/gdb.cp/mb-templates.exp
new file mode 100644 (file)
index 0000000..c3e10a9
--- /dev/null
@@ -0,0 +1,161 @@
+# Copyright 2007
+# 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 test verifies that setting breakpoint on line in template
+# function will fire in all instantiations of that template.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+set testfile "mb-templates"
+set srcfile ${testfile}.cc
+set binfile ${objdir}/${subdir}/${testfile}
+
+if [get_compiler_info ${binfile} "c++"] {
+    return -1
+}
+
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {
+     untested mb-templates.exp
+     return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+set bp_location [gdb_get_line_number "set breakpoint here"]
+
+# Set a breakpoint with multiple locations
+# and a condition.
+
+gdb_test "break $srcfile:$bp_location if i==1" \
+    "Breakpoint.*at.* file .*$srcfile, line.*\\(2 locations\\).*" \
+    "initial condition: set breakpoint"
+
+gdb_run_cmd
+gdb_expect {
+    -re "Breakpoint \[0-9\]+,.*foo<int> \\(i=1\\).*$gdb_prompt $" {
+       pass "initial condition: run to breakpoint"
+    }
+    -re "$gdb_prompt $" {
+       fail "initial condition: run to breakpoint"
+    }
+    timeout {
+       fail "initial condition: run to breakpoint (timeout)"
+    }
+}
+
+gdb_test "continue" \
+    ".*Breakpoint.*foo<double> \\(i=1\\).*" \
+    "initial condition: run to breakpoint 2"
+
+# Set breakpoint with multiple locations.
+# Separately set the condition.
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+gdb_test "break $srcfile:$bp_location" \
+    "Breakpoint.*at.* file .*$srcfile, line.*\\(2 locations\\).*" \
+    "separate condition: set breakpoint"
+
+gdb_test "condition 1 i==1" "" \
+    "separate condition: set condition"
+    
+gdb_run_cmd
+gdb_expect {
+    -re "Breakpoint \[0-9\]+,.*foo<int> \\(i=1\\).*$gdb_prompt $" {
+       pass "separate condition: run to breakpoint"
+    }
+    -re "$gdb_prompt $" {
+       fail "separate condition: run to breakpoint"
+    }
+    timeout {
+       fail "separate condition: run to breakpoint (timeout)"
+    }
+}
+
+gdb_test "continue" \
+    ".*Breakpoint.*foo<double> \\(i=1\\).*" \
+    "separate condition: run to breakpoint 2"
+
+# Try disabling a single location. We also test
+# that at least in simple cases, the enable/disable
+# state of locations surive "run".
+gdb_test "disable 1.1" "" "disabling location: disable"
+
+gdb_run_cmd
+gdb_expect {
+    -re "Breakpoint \[0-9\]+,.*foo<double> \\(i=1\\).*$gdb_prompt $" {
+       pass "disabling location: run to breakpoint"
+    }
+    -re "$gdb_prompt $" {
+       fail "disabling location: run to breakpoint"
+    }
+    timeout {
+       fail "disabling location: run to breakpoint (timeout)"
+    }
+}
+
+# Try disabling entire breakpoint
+gdb_test "enable 1.1" "" "disabling location: enable"
+
+
+gdb_test "disable 1" "" "disable breakpoint: disable"
+
+gdb_run_cmd
+gdb_expect {
+    -re "Program exited normally.*$gdb_prompt $" {
+       pass "disable breakpoint: run to breakpoint"
+    }
+    -re "$gdb_prompt $" {
+       fail "disable breakpoint: run to breakpoint"
+    }
+    timeout {
+       fail "disable breakpoint: run to breakpoint (timeout)"
+    }
+}
+
+# Make sure breakpoint can be set on a specific instantion.
+delete_breakpoints
+gdb_test "break 'void foo<int>(int)'" ".*" \
+    "instantiation: set breakpoint"
+
+
+gdb_run_cmd
+gdb_expect {
+    -re ".*Breakpoint \[0-9\]+,.*foo<int> \\(i=0\\).*$gdb_prompt $" {
+       pass "instantiation: run to breakpoint"
+    }
+    -re "$gdb_prompt $" {
+       fail "instantiation: run to breakpoint"
+    }
+    timeout {
+       fail "instantiation: run to breakpoint (timeout)"
+    }
+}
+
+gdb_test "continue" \
+    ".*Breakpoint.*foo<int> \\(i=1\\).*" \
+    "instantiation: run to breakpoint 2"
+