Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Patrick Palka <patrick@parcs.ath.cx>
To: gdb-patches@sourceware.org
Cc: Patrick Palka <patrick@parcs.ath.cx>
Subject: Re: [PATCH v2] Fix PR12526: -location watchpoints for bitfield arguments
Date: Wed, 03 Sep 2014 13:18:00 -0000	[thread overview]
Message-ID: <CA+C-WL_c9CO=xksbn9R=iC2hZOH+8cJRtSCQd4yhbRVgXKC+Bw@mail.gmail.com> (raw)
In-Reply-To: <CA+C-WL-4K6am80shgYrV-jWaVqrY0rs-Av41DkGXnCgUbg392g@mail.gmail.com>

On Wed, Aug 27, 2014 at 10:28 AM, Patrick Palka <patrick@parcs.ath.cx> wrote:
> On Wed, Aug 20, 2014 at 11:32 PM, Patrick Palka <patrick@parcs.ath.cx> wrote:
>> { v2: Here is my crude attempt at adding a testcase for this changeset.
>>   I also fixed the bug that I alluded to earlier. }
>>
>> PR 12526 reports that -location watchpoints against bitfield arguments
>> trigger false positives when bits around the bitfield, but not the
>> bitfield itself, are modified.
>>
>> This happens because -location watchpoints naturally operate at the byte
>> level, not at the bit level.  When the address of a bitfield lvalue is
>> taken, information about the bitfield (i.e. its offset and size) is lost
>> in the process.
>>
>> This information must first be retained throughout the lifetime of the
>> -location watchpoint.  This patch achieves this by adding two new fields
>> to the watchpoint struct: val_bitpos and val_bitsize.  These fields are
>> set when a watchpoint is first defined in watch_command_1().  They are
>> both equal to zero if the watchpoint is not a -location watchpoint or if
>> the argument is not a bitfield.
>>
>> Then these bitfield parameters are used inside update_watchpoint() and
>> watchpoint_check() to extract the actual value of the bitfield from the
>> watchpoint address, with the help of a local helper function
>> extract_bitfield_from_watchpoint_value().
>>
>> Finally when creating a HW breakpoint pointing to a bitfield, we
>> optimize the address and length of the breakpoint.  By skipping over the
>> bytes that don't cover the bitfield, this step reduces the frequency at
>> which a read watchpoint for the bitfield is triggered.  It also reduces
>> the number of times a false-positive call to check_watchpoint() is
>> triggered for a write watchpoint.
>>
>> gdb/
>>         PR breakpoints/12526
>>         * breakpoint.h (struct watchpoint): New fields val_bitpos and
>>         val_bitsize.
>>         * breakpoint.c (watch_command_1): Use these fields to retain
>>         bitfield information.
>>         (extract_bitfield_from_watchpoint_value): New function.
>>         (watchpoint_check): Use it.
>>         (update_watchpoint): Use it.  Optimize the address and length
>>         of a HW watchpoint pointing to a bitfield.
>>
>> gdb/testsuite/
>>         PR breakpoints/12526
>>         * gdb.base/pr12526.exp: New file.
>>         * gdb.base/pr12526.c: New file.
>> ---
>>  gdb/breakpoint.c                   | 67 +++++++++++++++++++++++++++++++++++++-
>>  gdb/breakpoint.h                   |  5 +++
>>  gdb/testsuite/gdb.base/pr12526.c   | 54 ++++++++++++++++++++++++++++++
>>  gdb/testsuite/gdb.base/pr12526.exp | 47 ++++++++++++++++++++++++++
>>  4 files changed, 172 insertions(+), 1 deletion(-)
>>  create mode 100644 gdb/testsuite/gdb.base/pr12526.c
>>  create mode 100644 gdb/testsuite/gdb.base/pr12526.exp
>>
>> diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
>> index 683ed2b..7b7c74b 100644
>> --- a/gdb/breakpoint.c
>> +++ b/gdb/breakpoint.c
>> @@ -1703,6 +1703,31 @@ watchpoint_del_at_next_stop (struct watchpoint *w)
>>    b->disposition = disp_del_at_next_stop;
>>  }
>>
>> +/* Extract a bitfield value from value VAL using the bit parameters contained in
>> +   watchpoint W.  */
>> +
>> +static struct value *
>> +extract_bitfield_from_watchpoint_value (struct watchpoint *w, struct value *val)
>> +{
>> +  LONGEST bit_val;
>> +  int ok;
>> +
>> +  if (val == NULL)
>> +    return NULL;
>> +
>> +  ok = unpack_value_bits_as_long (value_type (val),
>> +                                 value_contents_for_printing (val),
>> +                                 value_offset (val),
>> +                                 w->val_bitpos,
>> +                                 w->val_bitsize,
>> +                                 val,
>> +                                 &bit_val);
>> +  if (ok)
>> +    return value_from_longest (value_type (val), bit_val);
>> +
>> +  return NULL;
>> +}
>> +
>>  /* Assuming that B is a watchpoint:
>>     - Reparse watchpoint expression, if REPARSE is non-zero
>>     - Evaluate expression and store the result in B->val
>> @@ -1877,6 +1902,12 @@ update_watchpoint (struct watchpoint *b, int reparse)
>>          watchpoints.  */
>>        if (!b->val_valid && !is_masked_watchpoint (&b->base))
>>         {
>> +         if (b->val_bitsize)
>> +           {
>> +             v = extract_bitfield_from_watchpoint_value (b, v);
>> +             if (v)
>> +               release_value (v);
>> +           }
>>           b->val = v;
>>           b->val_valid = 1;
>>         }
>> @@ -1906,8 +1937,24 @@ update_watchpoint (struct watchpoint *b, int reparse)
>>                   CORE_ADDR addr;
>>                   int type;
>>                   struct bp_location *loc, **tmp;
>> +                 int bitpos = 0, bitsize = 0;
>> +
>> +                 if (value_bitsize (v))
>> +                   {
>> +                     bitpos = value_bitpos (v);
>> +                     bitsize = value_bitsize (v);
>> +                   }
>> +                 else if (v == result && b->val_bitsize)
>> +                   {
>> +                     bitpos = b->val_bitpos;
>> +                     bitsize = b->val_bitsize;
>> +                   }
>>
>>                   addr = value_address (v);
>> +                 if (bitsize)
>> +                   /* Skip the bytes that don't contain the bitfield.  */
>> +                   addr += bitpos / 8;
>> +
>>                   type = hw_write;
>>                   if (b->base.type == bp_read_watchpoint)
>>                     type = hw_read;
>> @@ -1922,7 +1969,13 @@ update_watchpoint (struct watchpoint *b, int reparse)
>>
>>                   loc->pspace = frame_pspace;
>>                   loc->address = addr;
>> -                 loc->length = TYPE_LENGTH (value_type (v));
>> +
>> +                 if (bitsize)
>> +                   /* Just cover the bytes that make up the bitfield.  */
>> +                   loc->length = ((bitpos % 8) + bitsize + 7) / 8;
>> +                 else
>> +                   loc->length = TYPE_LENGTH (value_type (v));
>> +
>>                   loc->watchpoint_type = type;
>>                 }
>>             }
>> @@ -5039,6 +5092,9 @@ watchpoint_check (void *p)
>>        mark = value_mark ();
>>        fetch_subexp_value (b->exp, &pc, &new_val, NULL, NULL, 0);
>>
>> +      if (b->val_bitsize)
>> +       new_val = extract_bitfield_from_watchpoint_value (b, new_val);
>> +
>>        /* We use value_equal_contents instead of value_equal because
>>          the latter coerces an array to a pointer, thus comparing just
>>          the address of the array instead of its contents.  This is
>> @@ -11172,6 +11228,7 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
>>    struct expression *exp;
>>    const struct block *exp_valid_block = NULL, *cond_exp_valid_block = NULL;
>>    struct value *val, *mark, *result;
>> +  int saved_bitpos = 0, saved_bitsize = 0;
>>    struct frame_info *frame;
>>    const char *exp_start = NULL;
>>    const char *exp_end = NULL;
>> @@ -11305,6 +11362,12 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
>>    mark = value_mark ();
>>    fetch_subexp_value (exp, &pc, &val, &result, NULL, just_location);
>>
>> +  if (val && just_location)
>> +    {
>> +      saved_bitpos = value_bitpos (val);
>> +      saved_bitsize = value_bitsize (val);
>> +    }
>> +
>>    if (just_location)
>>      {
>>        int ret;
>> @@ -11440,6 +11503,8 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
>>    else
>>      {
>>        w->val = val;
>> +      w->val_bitpos = saved_bitpos;
>> +      w->val_bitsize = saved_bitsize;
>>        w->val_valid = 1;
>>      }
>>
>> diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
>> index f6d06ce..2b80af1 100644
>> --- a/gdb/breakpoint.h
>> +++ b/gdb/breakpoint.h
>> @@ -779,6 +779,11 @@ struct watchpoint
>>       then an error occurred reading the value.  */
>>    int val_valid;
>>
>> +  /* When watching the location of a bitfield, contains the offset and size of
>> +     the bitfield.  Otherwise contains 0.  */
>> +  int val_bitpos;
>> +  int val_bitsize;
>> +
>>    /* Holds the frame address which identifies the frame this
>>       watchpoint should be evaluated in, or `null' if the watchpoint
>>       should be evaluated on the outermost frame.  */
>> diff --git a/gdb/testsuite/gdb.base/pr12526.c b/gdb/testsuite/gdb.base/pr12526.c
>> new file mode 100644
>> index 0000000..b51926d
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.base/pr12526.c
>> @@ -0,0 +1,54 @@
>> +/* This testcase is part of GDB, the GNU debugger.
>> +
>> +   Copyright 2014 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/>.  */
>> +
>> +struct foo
>> +{
>> +  unsigned long a:1;
>> +  unsigned char b:2;
>> +  unsigned long c:3;
>> +  char d:4;
>> +  int e:5;
>> +  char f:6;
>> +  int g:7;
>> +  long h:8;
>> +} q = { 0 };
>> +
>> +int
>> +main (void)
>> +{
>> +  q.a = 1;
>> +  q.b = 2;
>> +  q.c = 3;
>> +  q.d = 4;
>> +  q.e = 5;
>> +  q.f = 6;
>> +  q.g = -7;
>> +  q.h = -8;
>> +
>> +  q.a--;
>> +  q.b--;
>> +  q.e--;
>> +  q.d--;
>> +  q.c--;
>> +
>> +  q.f--;
>> +  q.g--;
>> +  q.h--;
>> +
>> +
>> +  return 0;
>> +}
>> diff --git a/gdb/testsuite/gdb.base/pr12526.exp b/gdb/testsuite/gdb.base/pr12526.exp
>> new file mode 100644
>> index 0000000..ed99bdc
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.base/pr12526.exp
>> @@ -0,0 +1,47 @@
>> +# Copyright 2014 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 is part of the gdb testsuite
>> +
>> +standard_testfile
>> +
>> +if {[prepare_for_testing $testfile.exp $testfile $srcfile debug]} {
>> +    return -1
>> +}
>> +
>> +if {![runto_main]} {
>> +    return -1
>> +}
>> +
>> +# Test correctness of watchpoints on bitfields.
>> +
>> +gdb_test "watch -l q.c"
>> +gdb_test "cont" "q\.c.*Old value = 0.*New value = 3.*"
>> +gdb_test "watch -l q.d"
>> +gdb_test "cont" "q\.d.*Old value = 0.*New value = 4.*"
>> +gdb_test "watch q.e"
>> +gdb_test "cont" "q\.e.*Old value = 0.*New value = 5.*"
>> +
>> +gdb_test "cont" "q\.e.*Old value = 5.*New value = 4.*"
>> +gdb_test "cont" "q\.d.*Old value = 4.*New value = 3.*"
>> +gdb_test "cont" "q\.c.*Old value = 3.*New value = 2.*"
>> +
>> +delete_breakpoints
>> +gdb_test "watch q.f"
>> +gdb_test "watch q.g"
>> +gdb_test "watch -l q.h"
>> +gdb_test "cont" "q\.f.*Old value = 6.*New value = 5.*"
>> +gdb_test "cont" "q\.g.*Old value = -7.*New value = -8.*"
>> +gdb_test "cont" "q\.h.*Old value = -8.*New value = -9.*"
>> --
>> 2.1.0
>>
>
> Ping.

Ping.


  reply	other threads:[~2014-09-03 13:18 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-08-20 19:40 [PATCH] " Patrick Palka
2014-08-20 20:59 ` Patrick Palka
2014-08-21  3:32 ` [PATCH v2] " Patrick Palka
2014-08-27 14:28   ` Patrick Palka
2014-09-03 13:18     ` Patrick Palka [this message]
2014-09-04 15:04   ` Pedro Alves
2014-09-07 18:36     ` Patrick Palka
2014-09-07 18:37       ` [PATCH] " Patrick Palka
2014-09-16 16:46         ` Pedro Alves
2014-09-07 23:57       ` [PATCH v2] " Sergio Durigan Junior
2014-09-08  0:10         ` Sergio Durigan Junior
2014-09-16 16:54           ` Pedro Alves
2014-09-16 17:05             ` Sergio Durigan Junior
2014-09-16 17:09               ` Pedro Alves
2014-09-17 13:00                 ` Patrick Palka

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CA+C-WL_c9CO=xksbn9R=iC2hZOH+8cJRtSCQd4yhbRVgXKC+Bw@mail.gmail.com' \
    --to=patrick@parcs.ath.cx \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox