* 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.
This commit is contained in:
Vladimir Prus 2007-09-24 07:40:32 +00:00
parent fad9eaf0de
commit ed0616c6b7
10 changed files with 667 additions and 8 deletions

View 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

View 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. */

View 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;
}

View 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;
}
@ -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)
{

View 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) */

View 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.

View File

@ -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);
}

View File

@ -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"

View File

@ -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);
}

View File

@ -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"