[RFC/RFA] handle function homonyms in breakpoint expressions

Joel Brobecker brobecker@adacore.com
Tue Jan 1 19:47:00 GMT 2008


Hello,

The problem I am trying to fix is the following. Imagine we have
the following two functions defined:

    procedure Next (I : in out Integer);
    procedure Next (F : in out Float);

If the user tries to break on "Next", then the expression is ambiguous
and GDB should present a multiple-choice menu and ask the user to choose:

    (gdb) b next
    [0] cancel
    [1] all
    [2] pck.next at pck.adb:5
    [3] pck.next at pck.adb:10
    >

This is a part of the Ada support that we haven't contributed yet.
The way we've done it is still perfectible, hence the RFC, which
I hope will turn into a RFA ;-). Right now, the debugger simply
breaks on one function chosen at random.

Paul Hilfinger is actually the engineers who knows this code best,
but it seems to me that the breakpoint expression parsing for Ada
needs to do a lot of specific things. We have some issues related
to homonyms, which is the object of this patch, but also with
runtime functions, internally-generated routines (by the expander),
etc. As a result, I think that the chances of merging the Ada-specific
part inside the general linespec.c code are pretty slim.

So, my conclusion when I looked at the code is that this patch looks
like a good starting point to introduce the feature above. The idea
is to short-circuit the end of decode_line_1 and call the Ada-specific
part instead, like so:

   else if (current_language->la_language == language_ada)
     {
       return ada_finish_decode_line_1 (argptr, file_symtab, funfirstline,
                                        canonical, not_found_ptr);
     }

The only part that I am questioning a bit is when the user selects
"all", because it results in the insertion of several breakpoints.
We do not end up with one logical breakpoint with several physical
locations, but instead with several logical breakpoints. This is because
we have been doing it this way in the AdaCore debugger. We will change
it eventually to better match the general model, but I think this is a
minor detail that can wait while we quickly bring the Ada support up to
par with AdaCore's debugger. At least the users now have access to the
functionality.

So, if the idea is approved, I can apply the attached patch, and
work on making a testcase for it.

2008-01-01  Joel Brobecker  <brobecker@adacore.com>

        * ada-lang.c (get_selections): Set prompt explicitly when calling
        command_line_input, or it will print the GDB prompt.
        (can_use_symbol_on_breakpoint, discard_non_breakpoint_matches)
        (ada_finish_decode_line_1, find_sal_from_funcs_and_line)
        (find_line_in_linetable, nearest_line_number_in_linetable)
        (find_next_line_in_linetable, is_plausible_func_for_line)
        (read_all_symtabs, ada_sals_for_line, extended_canonical_line_spec)
        (adjust_pc_past_prologue): New functions.
        * ada-lang.h (ada_finish_decode_line_1): Update profile.
        * linespec.c: #include ada-lang.h.
        (decode_line_1): Add call to ada_finish_decode_line_1.
        * Makefile.in (linespec.o): Update dependencies.

Tested on x86-linux. No regression.
Thoughts?

Thanks,
-- 
Joel

-------------- next part --------------
Index: ada-lang.c
===================================================================
--- ada-lang.c	(revision 33)
+++ ada-lang.c	(revision 34)
@@ -219,6 +219,21 @@ static int is_name_suffix (const char *)
 
 static int wild_match (const char *, int, const char *);
 
+static char *extended_canonical_line_spec (struct symtab_and_line,
+                                           const char *);
+static struct symtabs_and_lines
+  find_sal_from_funcs_and_line (const char *, int,
+                                struct ada_symbol_info *, int);
+
+static int find_line_in_linetable (struct linetable *, int,
+                                   struct ada_symbol_info *, int, int *);
+
+static int find_next_line_in_linetable (struct linetable *, int, int, int);
+
+static void read_all_symtabs (const char *);
+
+static int is_plausible_func_for_line (struct symbol *, int);
+
 static struct value *ada_coerce_ref (struct value *);
 
 static LONGEST pos_atr (struct value *);
@@ -244,6 +259,8 @@ static struct value *ada_to_fixed_value_
 
 static struct value *ada_to_fixed_value (struct value *);
 
+static void adjust_pc_past_prologue (CORE_ADDR *);
+
 static int ada_resolve_function (struct ada_symbol_info *, int,
                                  struct value **, int, const char *,
                                  struct type *);
@@ -3459,18 +3476,14 @@ get_selections (int *choices, int n_choi
                 int is_all_choice, char *annotation_suffix)
 {
   char *args;
-  const char *prompt;
+  char *prompt;
   int n_chosen;
   int first_choice = is_all_choice ? 2 : 1;
 
   prompt = getenv ("PS2");
   if (prompt == NULL)
-    prompt = ">";
-
-  printf_unfiltered (("%s "), prompt);
-  gdb_flush (gdb_stdout);
-
-  args = command_line_input ((char *) NULL, 0, annotation_suffix);
+    prompt = "> ";
+  args = command_line_input (prompt, 0, annotation_suffix);
 
   if (args == NULL)
     error_no_arg (_("one or more choice numbers"));
@@ -5714,6 +5727,761 @@ ada_make_symbol_completion_list (char *t
   return (result.array);
 }
 
+                                /* Breakpoint-related */
+
+/* Return non-zero if INFO refers to a symbol that can be used
+   to insert a breakpoint.  */
+
+static int
+can_use_symbol_on_breakpoint (struct ada_symbol_info info)
+{
+  /* We can break on symbols that represent code blocks such as
+     functions for instance.  */
+
+  if (SYMBOL_CLASS (info.sym) == LOC_BLOCK)
+    return 1;
+
+  /* brobecker/2005-11-11: There are probably many other classes
+     of symbols that can be excluded in this function.  But it is
+     preferable for this function to ere on the cautious side, to
+     make sure we do not remove a choice that's legitimate.  It is
+     better to present the user with more choice, even if some
+     of them do not make sense, rather than deprive him of some
+     of the legitimate option.  */
+
+  /* If a line number is associated to it, then it probably means
+     that we can use this symbol as a breakpoint location.  */
+
+  if (SYMBOL_LINE (info.sym) != 0)
+    return 1;
+
+  /* All other symbols are not suitable for use in a breakpoint
+     location expression.  */
+
+  return 0;
+}
+
+/* Remove from SYMS any symbol that cannot be used to insert a breakpoint.
+   Return the new number of symbols in SYMS.  */
+
+static int
+discard_non_breakpoint_matches (struct ada_symbol_info *syms, int nsyms)
+{
+  int i = 0;
+
+  while (i < nsyms)
+    {
+      if (!can_use_symbol_on_breakpoint (syms[i]))
+        {
+          int j;
+
+          for (j = i; j < nsyms - 1; j++)
+            syms[j] = syms[j+1];
+          nsyms--;
+        }
+      i++;
+    }
+
+  return nsyms;
+}
+
+/* *SPEC points to a function and line number spec (as in a break
+   command), following any initial file name specification.
+
+   Return all symbol table/line specfications (sals) consistent with the
+   information in *SPEC and FILE_TABLE in the following sense:
+     + FILE_TABLE is null, or the sal refers to a line in the file
+       named by FILE_TABLE.
+     + If *SPEC points to an argument with a trailing ':LINENUM',
+       then the sal refers to that line (or one following it as closely as
+       possible).
+     + If *SPEC does not start with '*', the sal is in a function with
+       that name.
+
+   Returns with 0 elements if no matching non-minimal symbols found.
+
+   If *SPEC begins with a function name of the form <NAME>, then NAME
+   is taken as a literal name; otherwise the function name is subject
+   to the usual encoding.
+
+   *SPEC is updated to point after the function/line number specification.
+
+   FUNFIRSTLINE is non-zero if we desire the first line of real code
+   in each function.
+
+   If CANONICAL is non-NULL, and if any of the sals require a
+   'canonical line spec', then *CANONICAL is set to point to an array
+   of strings, corresponding to and equal in length to the returned
+   list of sals, such that (*CANONICAL)[i] is non-null and contains a
+   canonical line spec for the ith returned sal, if needed.  If no
+   canonical line specs are required and CANONICAL is non-null,
+   *CANONICAL is set to NULL.
+
+   A 'canonical line spec' is simply a name (in the format of the
+   breakpoint command) that uniquely identifies a breakpoint position,
+   with no further contextual information or user selection.  It is
+   needed whenever the file name, function name, and line number
+   information supplied is insufficient for this unique
+   identification.  Currently overloaded functions, the name '*',
+   or static functions without a filename yield a canonical line spec.
+   The array and the line spec strings are allocated on the heap; it
+   is the caller's responsibility to free them.  */
+
+struct symtabs_and_lines
+ada_finish_decode_line_1 (char **spec, struct symtab *file_table,
+                          int funfirstline, char ***canonical,
+                          int *not_found_ptr)
+{
+  struct ada_symbol_info *symbols;
+  const struct block *block;
+  int n_matches, i, line_num;
+  struct symtabs_and_lines selected;
+  struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
+  char *name;
+  int is_quoted;
+
+  int len;
+  char *lower_name;
+  char *unquoted_name;
+
+  if (file_table == NULL)
+    block = block_static_block (get_selected_block (0));
+  else
+    block = BLOCKVECTOR_BLOCK (BLOCKVECTOR (file_table), STATIC_BLOCK);
+
+  if (canonical != NULL)
+    *canonical = (char **) NULL;
+
+  is_quoted = (**spec && strchr (get_gdb_completer_quote_characters (),
+                                 **spec) != NULL);
+
+  name = *spec;
+  if (**spec == '*')
+    *spec += 1;
+  else
+    {
+      if (is_quoted)
+        *spec = skip_quoted (*spec);
+      while (**spec != '\000'
+             && !strchr (ada_completer_word_break_characters, **spec))
+        *spec += 1;
+    }
+  len = *spec - name;
+
+  line_num = -1;
+  if (file_table != NULL && (*spec)[0] == ':' && isdigit ((*spec)[1]))
+    {
+      line_num = strtol (*spec + 1, spec, 10);
+      while (**spec == ' ' || **spec == '\t')
+        *spec += 1;
+    }
+
+  if (name[0] == '*')
+    {
+      if (line_num == -1)
+        error (_("Wild-card function with no line number or file name."));
+
+      return ada_sals_for_line (file_table->filename, line_num,
+                                funfirstline, canonical, 0);
+    }
+
+  if (name[0] == '\'')
+    {
+      name += 1;
+      len -= 2;
+    }
+
+  if (name[0] == '<')
+    {
+      unquoted_name = (char *) alloca (len - 1);
+      memcpy (unquoted_name, name + 1, len - 2);
+      unquoted_name[len - 2] = '\000';
+      lower_name = NULL;
+    }
+  else
+    {
+      unquoted_name = (char *) alloca (len + 1);
+      memcpy (unquoted_name, name, len);
+      unquoted_name[len] = '\000';
+      lower_name = (char *) alloca (len + 1);
+      for (i = 0; i < len; i += 1)
+        lower_name[i] = tolower (name[i]);
+      lower_name[len] = '\000';
+    }
+
+  n_matches = 0;
+  if (lower_name != NULL)
+    n_matches = ada_lookup_symbol_list (ada_encode (lower_name), block,
+                                        VAR_DOMAIN, &symbols);
+  if (n_matches == 0)
+    n_matches = ada_lookup_symbol_list (unquoted_name, block,
+                                        VAR_DOMAIN, &symbols);
+  if (n_matches == 0 && line_num >= 0)
+    error (_("No line number information found for %s."), unquoted_name);
+  else if (n_matches == 0)
+    {
+      struct symtab_and_line val;
+      struct minimal_symbol *msymbol;
+
+      init_sal (&val);
+
+      msymbol = NULL;
+      if (lower_name != NULL)
+        msymbol = ada_lookup_simple_minsym (ada_encode (lower_name));
+      if (msymbol == NULL)
+        msymbol = ada_lookup_simple_minsym (unquoted_name);
+      if (msymbol != NULL)
+        {
+          val.pc = SYMBOL_VALUE_ADDRESS (msymbol);
+          val.section = SYMBOL_BFD_SECTION (msymbol);
+          if (funfirstline)
+            {
+              val.pc = gdbarch_convert_from_func_ptr_addr (current_gdbarch,
+							   val.pc,
+							   &current_target);
+              gdbarch_skip_prologue (current_gdbarch, val.pc);
+            }
+          selected.sals = (struct symtab_and_line *)
+            xmalloc (sizeof (struct symtab_and_line));
+          selected.sals[0] = val;
+          selected.nelts = 1;
+          return selected;
+        }
+
+      if (!have_full_symbols ()
+          && !have_partial_symbols () && !have_minimal_symbols ())
+        error (_("No symbol table is loaded.  Use the \"file\" command."));
+      if (not_found_ptr)
+	*not_found_ptr = 1;
+      throw_error (NOT_FOUND_ERROR, _("Function \"%s\" not defined."), 
+                   unquoted_name);
+      return selected;          /* for lint */
+    }
+
+  if (line_num >= 0)
+    {
+      struct symtabs_and_lines best_sal =
+        find_sal_from_funcs_and_line (file_table->filename, line_num,
+                                      symbols, n_matches);
+      if (funfirstline)
+        adjust_pc_past_prologue (&best_sal.sals[0].pc);
+      return best_sal;
+    }
+  else
+    {
+      n_matches = discard_non_breakpoint_matches (symbols, n_matches);
+      selected.nelts = user_select_syms (symbols, n_matches, n_matches);
+    }
+
+  selected.sals = (struct symtab_and_line *)
+    xmalloc (sizeof (struct symtab_and_line) * selected.nelts);
+  memset (selected.sals, 0, selected.nelts * sizeof (selected.sals[i]));
+  make_cleanup (xfree, selected.sals);
+
+  i = 0;
+  while (i < selected.nelts)
+    {
+      if (SYMBOL_CLASS (symbols[i].sym) == LOC_BLOCK)
+        selected.sals[i]
+          = find_function_start_sal (symbols[i].sym, funfirstline);
+      else if (SYMBOL_LINE (symbols[i].sym) != 0)
+        {
+          selected.sals[i].symtab =
+            symbols[i].symtab
+            ? symbols[i].symtab : symtab_for_sym (symbols[i].sym);
+          selected.sals[i].line = SYMBOL_LINE (symbols[i].sym);
+        }
+      else if (line_num >= 0)
+        {
+          /* Ignore this choice */
+          symbols[i] = symbols[selected.nelts - 1];
+          selected.nelts -= 1;
+          continue;
+        }
+      else
+        error (_("Line number not known for symbol \"%s\""), unquoted_name);
+      i += 1;
+    }
+
+  if (canonical != NULL && (line_num >= 0 || n_matches > 1))
+    {
+      *canonical = (char **) xmalloc (sizeof (char *) * selected.nelts);
+      for (i = 0; i < selected.nelts; i += 1)
+        (*canonical)[i] =
+          extended_canonical_line_spec (selected.sals[i],
+                                        SYMBOL_PRINT_NAME (symbols[i].sym));
+    }
+
+  discard_cleanups (old_chain);
+  return selected;
+}
+
+/* The (single) sal corresponding to line LINE_NUM in a symbol table
+   with file name FILENAME that occurs in one of the functions listed
+   in the symbol fields of SYMBOLS[0 .. NSYMS-1].  */
+
+static struct symtabs_and_lines
+find_sal_from_funcs_and_line (const char *filename, int line_num,
+                              struct ada_symbol_info *symbols, int nsyms)
+{
+  struct symtabs_and_lines sals;
+  int best_index, best;
+  struct linetable *best_linetable;
+  struct objfile *objfile;
+  struct symtab *s;
+  struct symtab *best_symtab;
+
+  read_all_symtabs (filename);
+
+  best_index = 0;
+  best_linetable = NULL;
+  best_symtab = NULL;
+  best = 0;
+  ALL_SYMTABS (objfile, s)
+  {
+    struct linetable *l;
+    int ind, exact;
+
+    QUIT;
+
+    if (strcmp (filename, s->filename) != 0)
+      continue;
+    l = LINETABLE (s);
+    ind = find_line_in_linetable (l, line_num, symbols, nsyms, &exact);
+    if (ind >= 0)
+      {
+        if (exact)
+          {
+            best_index = ind;
+            best_linetable = l;
+            best_symtab = s;
+            goto done;
+          }
+        if (best == 0 || l->item[ind].line < best)
+          {
+            best = l->item[ind].line;
+            best_index = ind;
+            best_linetable = l;
+            best_symtab = s;
+          }
+      }
+  }
+
+  if (best == 0)
+    error (_("Line number not found in designated function."));
+
+done:
+
+  sals.nelts = 1;
+  sals.sals = (struct symtab_and_line *) xmalloc (sizeof (sals.sals[0]));
+
+  init_sal (&sals.sals[0]);
+
+  sals.sals[0].line = best_linetable->item[best_index].line;
+  sals.sals[0].pc = best_linetable->item[best_index].pc;
+  sals.sals[0].symtab = best_symtab;
+
+  return sals;
+}
+
+/* Return the index in LINETABLE of the best match for LINE_NUM whose
+   pc falls within one of the functions denoted by the symbol fields
+   of SYMBOLS[0..NSYMS-1].  Set *EXACTP to 1 if the match is exact, 
+   and 0 otherwise.  */
+
+static int
+find_line_in_linetable (struct linetable *linetable, int line_num,
+                        struct ada_symbol_info *symbols, int nsyms,
+                        int *exactp)
+{
+  int i, len, best_index, best;
+
+  if (line_num <= 0 || linetable == NULL)
+    return -1;
+
+  len = linetable->nitems;
+  for (i = 0, best_index = -1, best = 0; i < len; i += 1)
+    {
+      int k;
+      struct linetable_entry *item = &(linetable->item[i]);
+
+      for (k = 0; k < nsyms; k += 1)
+        {
+          if (symbols[k].sym != NULL
+              && SYMBOL_CLASS (symbols[k].sym) == LOC_BLOCK
+              && item->pc >= BLOCK_START (SYMBOL_BLOCK_VALUE (symbols[k].sym))
+              && item->pc < BLOCK_END (SYMBOL_BLOCK_VALUE (symbols[k].sym)))
+            goto candidate;
+        }
+      continue;
+
+    candidate:
+
+      if (item->line == line_num)
+        {
+          *exactp = 1;
+          return i;
+        }
+
+      if (item->line > line_num && (best == 0 || item->line < best))
+        {
+          best = item->line;
+          best_index = i;
+        }
+    }
+
+  *exactp = 0;
+  return best_index;
+}
+
+/* Find the smallest k >= LINE_NUM such that k is a line number in
+   LINETABLE, and k falls strictly within a named function that begins at
+   or before LINE_NUM.  Return -1 if there is no such k.  */
+
+static int
+nearest_line_number_in_linetable (struct linetable *linetable, int line_num)
+{
+  int i, len, best;
+
+  if (line_num <= 0 || linetable == NULL || linetable->nitems == 0)
+    return -1;
+  len = linetable->nitems;
+
+  i = 0;
+  best = INT_MAX;
+  while (i < len)
+    {
+      struct linetable_entry *item = &(linetable->item[i]);
+
+      if (item->line >= line_num && item->line < best)
+        {
+          char *func_name;
+          CORE_ADDR start, end;
+
+          func_name = NULL;
+          find_pc_partial_function (item->pc, &func_name, &start, &end);
+
+          if (func_name != NULL && item->pc < end)
+            {
+              if (item->line == line_num)
+                return line_num;
+              else
+                {
+                  struct symbol *sym =
+                    standard_lookup (func_name, NULL, VAR_DOMAIN);
+                  if (is_plausible_func_for_line (sym, line_num))
+                    best = item->line;
+                  else
+                    {
+                      do
+                        i += 1;
+                      while (i < len && linetable->item[i].pc < end);
+                      continue;
+                    }
+                }
+            }
+        }
+
+      i += 1;
+    }
+
+  return (best == INT_MAX) ? -1 : best;
+}
+
+
+/* Return the next higher index, k, into LINETABLE such that k > IND,
+   entry k in LINETABLE has a line number equal to LINE_NUM, k
+   corresponds to a PC that is in a function different from that
+   corresponding to IND, and falls strictly within a named function
+   that begins at a line at or preceding STARTING_LINE.
+   Return -1 if there is no such k.
+   IND == -1 corresponds to no function.  */
+
+static int
+find_next_line_in_linetable (struct linetable *linetable, int line_num,
+                             int starting_line, int ind)
+{
+  int i, len;
+
+  if (line_num <= 0 || linetable == NULL || ind >= linetable->nitems)
+    return -1;
+  len = linetable->nitems;
+
+  if (ind >= 0)
+    {
+      CORE_ADDR start, end;
+
+      if (find_pc_partial_function (linetable->item[ind].pc,
+                                    (char **) NULL, &start, &end))
+        {
+          while (ind < len && linetable->item[ind].pc < end)
+            ind += 1;
+        }
+      else
+        ind += 1;
+    }
+  else
+    ind = 0;
+
+  i = ind;
+  while (i < len)
+    {
+      struct linetable_entry *item = &(linetable->item[i]);
+
+      if (item->line >= line_num)
+        {
+          char *func_name;
+          CORE_ADDR start, end;
+
+          func_name = NULL;
+          find_pc_partial_function (item->pc, &func_name, &start, &end);
+
+          if (func_name != NULL && item->pc < end)
+            {
+              if (item->line == line_num)
+                {
+                  struct symbol *sym =
+                    standard_lookup (func_name, NULL, VAR_DOMAIN);
+                  if (is_plausible_func_for_line (sym, starting_line))
+                    return i;
+                  else
+                    {
+                      while ((i + 1) < len && linetable->item[i + 1].pc < end)
+                        i += 1;
+                    }
+                }
+            }
+        }
+      i += 1;
+    }
+
+  return -1;
+}
+
+/* True iff function symbol SYM starts somewhere at or before line #
+   LINE_NUM.  */
+
+static int
+is_plausible_func_for_line (struct symbol *sym, int line_num)
+{
+  struct symtab_and_line start_sal;
+
+  if (sym == NULL)
+    return 0;
+
+  start_sal = find_function_start_sal (sym, 0);
+
+  return (start_sal.line != 0 && line_num >= start_sal.line);
+}
+
+/* Read in all symbol tables corresponding to partial symbol tables
+   with file name FILENAME.  */
+
+static void
+read_all_symtabs (const char *filename)
+{
+  struct partial_symtab *ps;
+  struct objfile *objfile;
+
+  ALL_PSYMTABS (objfile, ps)
+  {
+    QUIT;
+
+    if (strcmp (filename, ps->filename) == 0)
+      PSYMTAB_TO_SYMTAB (ps);
+  }
+}
+
+/* All sals corresponding to line LINE_NUM in a symbol table from file
+   FILENAME, as filtered by the user.  Filter out any lines that
+   reside in functions with "suppressed" names (not corresponding to
+   explicit Ada functions), if there is at least one in a function
+   with a non-suppressed name.  If CANONICAL is not null, set
+   it to a corresponding array of canonical line specs.
+   If ONE_LOCATION_ONLY is set and several matches are found for
+   the given location, then automatically select the first match found
+   instead of asking the user which instance should be returned.  */
+
+struct symtabs_and_lines
+ada_sals_for_line (const char *filename, int line_num,
+                   int funfirstline, char ***canonical, int one_location_only)
+{
+  struct symtabs_and_lines result;
+  struct objfile *objfile;
+  struct symtab *s;
+  struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
+  size_t len;
+
+  read_all_symtabs (filename);
+
+  result.sals =
+    (struct symtab_and_line *) xmalloc (4 * sizeof (result.sals[0]));
+  result.nelts = 0;
+  len = 4;
+  make_cleanup (free_current_contents, &result.sals);
+
+  ALL_SYMTABS (objfile, s)
+  {
+    int ind, target_line_num;
+
+    QUIT;
+
+    if (strcmp (s->filename, filename) != 0)
+      continue;
+
+    target_line_num =
+      nearest_line_number_in_linetable (LINETABLE (s), line_num);
+    if (target_line_num == -1)
+      continue;
+
+    ind = -1;
+    while (1)
+      {
+        ind =
+          find_next_line_in_linetable (LINETABLE (s),
+                                       target_line_num, line_num, ind);
+
+        if (ind < 0)
+          break;
+
+        GROW_VECT (result.sals, len, result.nelts + 1);
+        init_sal (&result.sals[result.nelts]);
+        result.sals[result.nelts].line = line_num;
+        result.sals[result.nelts].pc = LINETABLE (s)->item[ind].pc;
+        result.sals[result.nelts].symtab = s;
+
+        if (funfirstline)
+          adjust_pc_past_prologue (&result.sals[result.nelts].pc);
+
+        result.nelts += 1;
+      }
+  }
+
+  if (canonical != NULL || result.nelts > 1)
+    {
+      int k, j, n;
+      char **func_names = (char **) alloca (result.nelts * sizeof (char *));
+      int first_choice = (result.nelts > 1) ? 2 : 1;
+      int *choices = (int *) alloca (result.nelts * sizeof (int));
+
+      for (k = 0; k < result.nelts; k += 1)
+        {
+          find_pc_partial_function (result.sals[k].pc, &func_names[k],
+                                    (CORE_ADDR *) NULL, (CORE_ADDR *) NULL);
+          if (func_names[k] == NULL)
+            error (_("Could not find function for one or more breakpoints."));
+        }
+
+      /* Remove suppressed names, unless all are suppressed.  */
+      for (j = 0; j < result.nelts; j += 1)
+        if (!is_suppressed_name (func_names[j]))
+          {
+            /* At least one name is unsuppressed, so remove all
+               suppressed names.  */
+            for (k = n = 0; k < result.nelts; k += 1)
+              if (!is_suppressed_name (func_names[k]))
+                {
+                  func_names[n] = func_names[k];
+                  result.sals[n] = result.sals[k];
+                  n += 1;
+                }
+            result.nelts = n;
+            break;
+          }
+
+      if (result.nelts > 1)
+        {
+          if (one_location_only)
+            {
+              /* Automatically select the first of all possible choices.  */
+              n = 1;
+              choices[0] = 0;
+            }
+          else
+            {
+              printf_unfiltered ("[0] cancel\n");
+              if (result.nelts > 1)
+                printf_unfiltered ("[1] all\n");
+              for (k = 0; k < result.nelts; k += 1)
+                printf_unfiltered ("[%d] %s\n", k + first_choice,
+                                   ada_decode (func_names[k]));
+
+              n = get_selections (choices, result.nelts, result.nelts,
+                                  result.nelts > 1, "instance-choice");
+            }
+
+          for (k = 0; k < n; k += 1)
+            {
+              result.sals[k] = result.sals[choices[k]];
+              func_names[k] = func_names[choices[k]];
+            }
+          result.nelts = n;
+        }
+
+      if (canonical != NULL && result.nelts == 0)
+        *canonical = NULL;
+      else if (canonical != NULL)
+        {
+          *canonical = (char **) xmalloc (result.nelts * sizeof (char **));
+          make_cleanup (xfree, *canonical);
+          for (k = 0; k < result.nelts; k += 1)
+            {
+              (*canonical)[k] =
+                extended_canonical_line_spec (result.sals[k], func_names[k]);
+              if ((*canonical)[k] == NULL)
+                error (_("Could not locate one or more breakpoints."));
+              make_cleanup (xfree, (*canonical)[k]);
+            }
+        }
+    }
+
+  if (result.nelts == 0)
+    {
+      do_cleanups (old_chain);
+      result.sals = NULL;
+    }
+  else
+    discard_cleanups (old_chain);
+  return result;
+}
+
+
+/* A canonical line specification of the form FILE:NAME:LINENUM for
+   symbol table and line data SAL.  NULL if insufficient
+   information.  The caller is responsible for releasing any space
+   allocated.
+   
+   The cannonical form return is enclosed inside double-quotes in order
+   to protect us against spaces embedded inside the file path name.  */
+
+static char *
+extended_canonical_line_spec (struct symtab_and_line sal, const char *name)
+{
+  if (sal.symtab == NULL || sal.symtab->filename == NULL || sal.line <= 0)
+    return NULL;
+
+  
+  return (xstrprintf ("\"%s:'%s':%d\"", sal.symtab->filename, name, sal.line));
+}
+
+/* If the PC is pointing inside a function prologue, then re-adjust it
+   past this prologue.  */
+
+static void
+adjust_pc_past_prologue (CORE_ADDR *pc)
+{
+  struct symbol *func_sym = find_pc_function (*pc);
+
+  if (func_sym)
+    {
+      const struct symtab_and_line sal =
+        find_function_start_sal (func_sym, 1);
+
+      if (*pc <= sal.pc)
+        *pc = sal.pc;
+    }
+}
+
                                 /* Field Access */
 
 /* Return non-zero if TYPE is a pointer to the GNAT dispatch table used
Index: ada-lang.h
===================================================================
--- ada-lang.h	(revision 33)
+++ ada-lang.h	(revision 34)
@@ -337,7 +337,8 @@ extern char *ada_start_decode_line_1 (ch
 
 extern struct symtabs_and_lines ada_finish_decode_line_1 (char **,
                                                           struct symtab *,
-                                                          int, char ***);
+                                                          int, char ***,
+                                                          int *);
 
 extern struct symtabs_and_lines ada_sals_for_line (const char*, int,
 						   int, char***, int);
Index: ChangeLog.GNAT
===================================================================
--- ChangeLog.GNAT	(revision 33)
+++ ChangeLog.GNAT	(revision 34)
@@ -1,5 +1,20 @@
 2008-01-01  Joel Brobecker  <brobecker@adacore.com>
 
+	* ada-lang.c (get_selections): Set prompt explicitly when calling
+	command_line_input, or it will print the GDB prompt.
+	(can_use_symbol_on_breakpoint, discard_non_breakpoint_matches)
+	(ada_finish_decode_line_1, find_sal_from_funcs_and_line)
+	(find_line_in_linetable, nearest_line_number_in_linetable)
+	(find_next_line_in_linetable, is_plausible_func_for_line)
+	(read_all_symtabs, ada_sals_for_line, extended_canonical_line_spec)
+	(adjust_pc_past_prologue): New functions.
+	* ada-lang.h (ada_finish_decode_line_1): Update profile.
+	* linespec.c: #include ada-lang.h.
+	(decode_line_1): Add call to ada_finish_decode_line_1.
+	* Makefile.in (linespec.o): Update dependencies.
+
+2008-01-01  Joel Brobecker  <brobecker@adacore.com>
+
 	* ada-lang.c (auto_select_off, auto_select_all, auto_select_cancel)
 	(auto_select_choices, auto_select_choice): New static global variables.
 	(user_select_syms): Add handling of new multiple-choice-auto-select
Index: linespec.c
===================================================================
--- linespec.c	(revision 33)
+++ linespec.c	(revision 34)
@@ -36,6 +36,7 @@
 #include "linespec.h"
 #include "exceptions.h"
 #include "language.h"
+#include "ada-lang.h"
 
 /* We share this one with symtab.c, but it is not exported widely. */
 
@@ -821,6 +822,11 @@ decode_line_1 (char **argptr, int funfir
       /* allow word separators in method names for Obj-C */
       p = skip_quoted_chars (*argptr, NULL, "");
     }
+  else if (current_language->la_language == language_ada)
+    {
+      return ada_finish_decode_line_1 (argptr, file_symtab, funfirstline,
+                                       canonical, not_found_ptr);
+    }
   else if (paren_pointer != NULL)
     {
       p = paren_pointer + 1;
Index: Makefile.in
===================================================================
--- Makefile.in	(revision 33)
+++ Makefile.in	(revision 34)
@@ -2331,7 +2331,8 @@ libunwind-frame.o: libunwind-frame.c $(d
 linespec.o: linespec.c $(defs_h) $(symtab_h) $(frame_h) $(command_h) \
 	$(symfile_h) $(objfiles_h) $(source_h) $(demangle_h) $(value_h) \
 	$(completer_h) $(cp_abi_h) $(parser_defs_h) $(block_h) \
-	$(objc_lang_h) $(linespec_h) $(exceptions_h) $(language_h)
+	$(objc_lang_h) $(linespec_h) $(exceptions_h) $(language_h) \
+	$(ada_lang_h)
 linux-fork.o: linux-fork.c $(defs_h) $(inferior_h) $(regcache_h) $(gdbcmd_h) \
 	$(infcall_h) $(gdb_assert_h) $(gdb_string_h) $(linux_fork_h) \
 	$(linux_nat_h) $(gdb_wait_h) $(gdb_dirent_h)


More information about the Gdb-patches mailing list