From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 1228 invoked by alias); 9 Jun 2011 22:16:51 -0000 Received: (qmail 1219 invoked by uid 22791); 9 Jun 2011 22:16:50 -0000 X-SWARE-Spam-Status: No, hits=-1.2 required=5.0 tests=AWL,BAYES_00,RCVD_IN_DNSWL_NONE X-Spam-Check-By: sourceware.org Received: from mailrelay001.isp.belgacom.be (HELO mailrelay001.isp.belgacom.be) (195.238.6.51) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 09 Jun 2011 22:16:34 +0000 X-Belgacom-Dynamic: yes X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: Av0EANFE8U1tgBGc/2dsb2JhbABTihecKHiIage/Uw6GFQSgfg Received: from 156.17-128-109.adsl-dyn.isp.belgacom.be (HELO soleil) ([109.128.17.156]) by relay.skynet.be with SMTP; 10 Jun 2011 00:16:32 +0200 Message-ID: From: "Philippe Waroquiers" To: "Pedro Alves" Cc: , References: <201106011534.50994.pedro@codesourcery.com> <1B06384E85DD4533B1F1C59A5EE8383D@soleil> <201106090059.42380.pedro@codesourcery.com> Subject: Re: x86 watchpoints bug (Re: ping: Re: PATCH : allow to set length of hw watchpoints (e.g. for Valgrind gdbserver)) Date: Thu, 09 Jun 2011 22:16:00 -0000 MIME-Version: 1.0 Content-Type: text/plain; format=flowed; charset="iso-8859-15"; reply-type=original Content-Transfer-Encoding: 7bit X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2011-06/txt/msg00137.txt.bz2 Thanks for the feedback, behaviour looks to be better when adding the missing assignment. But I suspect I found another bug in the area of "high level" to "low level" to "hw level" watchpoints. >> I suspect the problem might be in the following piece of code: >> static void >> update_inferior (struct i386_debug_reg_state *inf_state, >> struct i386_debug_reg_state *new_state) >> { >> int i; >> >> ALL_DEBUG_REGISTERS (i) >> { >> if (new_state->dr_mirror[i] != inf_state->dr_mirror[i] >> || (new_state->dr_ref_count[i] != 0 >> && inf_state->dr_ref_count[i] == 0)) >> { >> >> The dr_mirror is the address being watched. >> But if address being watched is 0x0, then a 'busy' register >> watching 0x0 and a non-busy register will have equal dr_mirror. >> Then the || condition is bizarre as the ref.count will be updated >> only if the current inf_state ref.count is 0. > > Not the ref.count. The address to watch, DR[0-3]. Without the *inf_state = *new_state, I had some difficulties to understand the above code. >From what I understand now, the idea of this piece of code is (only) to change the real value of the hw register. But if inf_state->dr_mirror properly mirrors the value of the hw register, then the inequality of the dr_mirror[i] should be good enough to detect the need to change the hw register. And if setting the address is only to be done when activating the watchpoint, then the inequality on the ref count should be good enough (and the assert new_state->dr_ref_count[i] == 1 should hold when changing the hw addr value). Well it seems I still have difficulty to understand the code :). But even if I do not understand the code, after adding the *inf_state = *new_state, the behaviour looks better. >> +# registers were available to cover a single (low level) watchpoint. > watchpoint. So the comment was correct if you think of high and > low level watchpoints like I was thinking. Maybe you were thinking > of a high level watchpoint as what the target sees? Yes, I was interpreting "high level" being a Z2 packet, and "low level" being the "hw" watchpoint. Now, I understand that we have: high level watchpoints = "user defined watchpoints in gdb" = watched expressions low level watchpoints = "memory region watchpoints needed to implement the high level" = Z2 packets hw level watchpoints = "hw watchpoint(s) needed to implement the low level watchpoint(s)" Thanks for the clarification Doing some additional checks, I found something else slightly strange, but it seems to be wrong at the mapping between "high level watchpoints" and "low level watchpoints". In the below, you see that 3 identical watchpoints results in 1 single Z2 packet, but disabling the 3 watchpoints gives 3 z2 packets (sent when the last watchpoint is disabled). The reason for this assymetry looks not very clear to me, but that might just be an implementation detail. I however suspect there is still a bug, as after, when sharing a debug register between two non-identical watchpoints, we are losing a part of the to be watched zone : we still have a user level watchpoint of 16 bytes at 0x0, but the hw registers are only watching 8 bytes at 0x8. (the below is done with a patched gdb/gdbserver containing the "dr busy" fix + the missing assignment + the "set length" patch to allow watching 16 bytes with gdbserver). Note that the bug seems also present in native debugging (see native gdb session at the end). (gdb) tar rem :1234 Remote debugging using :1234 Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done. Loaded symbols for /lib64/ld-linux-x86-64.so.2 0x00007ffff7de2a60 in ?? () from /lib64/ld-linux-x86-64.so.2 (gdb) set breakpoint always-inserted on (gdb) mo set debug-hw-points 1 H/W point debugging output enabled. (gdb) watch *(long *) 0x0 Hardware watchpoint 1: *(long *) 0x0 (gdb) watch *(long *) 0x0 Hardware watchpoint 2: *(long *) 0x0 (gdb) watch *(long *) 0x0 Hardware watchpoint 3: *(long *) 0x0 (gdb) dis 1 (gdb) dis 2 (gdb) dis 3 (gdb) enabl (gdb) watch * (char (*)[16]) 0x0 Hardware watchpoint 4: * (char (*)[16]) 0x0 (gdb) dis 1 (gdb) dis 2 (gdb) dis 3 (gdb) Listening on port 1234 Remote debugging from host 127.0.0.1 insert_watchpoint (addr=0, len=8, type=data-write): CONTROL (DR7): 00090101 STATUS (DR6): 00000000 DR0: addr=0x0, ref.count=1 DR1: addr=0x0, ref.count=0 DR2: addr=0x0, ref.count=0 DR3: addr=0x0, ref.count=0 remove_watchpoint (addr=0, len=8, type=data-write): CONTROL (DR7): 00090100 STATUS (DR6): 00000000 DR0: addr=0x0, ref.count=0 DR1: addr=0x0, ref.count=0 DR2: addr=0x0, ref.count=0 DR3: addr=0x0, ref.count=0 remove_watchpoint (addr=0, len=8, type=data-write): CONTROL (DR7): 00090100 STATUS (DR6): 00000000 DR0: addr=0x0, ref.count=0 DR1: addr=0x0, ref.count=0 DR2: addr=0x0, ref.count=0 DR3: addr=0x0, ref.count=0 remove_watchpoint (addr=0, len=8, type=data-write): CONTROL (DR7): 00090100 STATUS (DR6): 00000000 DR0: addr=0x0, ref.count=0 DR1: addr=0x0, ref.count=0 DR2: addr=0x0, ref.count=0 DR3: addr=0x0, ref.count=0 insert_watchpoint (addr=0, len=8, type=data-write): CONTROL (DR7): 00090101 STATUS (DR6): 00000000 DR0: addr=0x0, ref.count=1 DR1: addr=0x0, ref.count=0 DR2: addr=0x0, ref.count=0 DR3: addr=0x0, ref.count=0 insert_watchpoint (addr=0, len=16, type=data-write): CONTROL (DR7): 00990105 STATUS (DR6): 00000000 DR0: addr=0x0, ref.count=2 DR1: addr=0x8, ref.count=1 DR2: addr=0x0, ref.count=0 DR3: addr=0x0, ref.count=0 remove_watchpoint (addr=0, len=8, type=data-write): CONTROL (DR7): 00990105 STATUS (DR6): 00000000 DR0: addr=0x0, ref.count=1 DR1: addr=0x8, ref.count=1 DR2: addr=0x0, ref.count=0 DR3: addr=0x0, ref.count=0 remove_watchpoint (addr=0, len=8, type=data-write): CONTROL (DR7): 00990104 STATUS (DR6): 00000000 DR0: addr=0x0, ref.count=0 DR1: addr=0x8, ref.count=1 DR2: addr=0x0, ref.count=0 DR3: addr=0x0, ref.count=0 remove_watchpoint (addr=0, len=8, type=data-write): CONTROL (DR7): 00990104 STATUS (DR6): 00000000 DR0: addr=0x0, ref.count=0 DR1: addr=0x8, ref.count=1 DR2: addr=0x0, ref.count=0 DR3: addr=0x0, ref.count=0 (gdb) watch * (char (*)[16]) 0x0 Hardware watchpoint 6: * (char (*)[16]) 0x0 insert_watchpoint (addr=0, len=16, type=data-write): CONTROL (DR7): 0000000000990105 STATUS (DR6): 00000000ffff4ff0 DR0: addr=0x0000000000000000, ref.count=2 DR1: addr=0x0000000000000008, ref.count=1 DR2: addr=0x0000000000000000, ref.count=0 DR3: addr=0x0000000000000000, ref.count=0 (gdb) info wat Num Type Disp Enb Address What 1 hw watchpoint keep y *(long *) 0x0 3 hw watchpoint keep y *(long *) 0x0 4 hw watchpoint keep y *(long *) 0x0 6 hw watchpoint keep y * (char (*)[16]) 0x0 (gdb) dis 1 (gdb) dis 3 (gdb) dis 4 remove_watchpoint (addr=0, len=8, type=data-write): CONTROL (DR7): 0000000000990105 STATUS (DR6): 00000000ffff4ff0 DR0: addr=0x0000000000000000, ref.count=1 DR1: addr=0x0000000000000008, ref.count=1 DR2: addr=0x0000000000000000, ref.count=0 DR3: addr=0x0000000000000000, ref.count=0 remove_watchpoint (addr=0, len=8, type=data-write): CONTROL (DR7): 0000000000990104 STATUS (DR6): 00000000ffff4ff0 DR0: addr=0x0000000000000000, ref.count=0 DR1: addr=0x0000000000000008, ref.count=1 DR2: addr=0x0000000000000000, ref.count=0 DR3: addr=0x0000000000000000, ref.count=0 remove_watchpoint (addr=0, len=8, type=data-write): CONTROL (DR7): 0000000000990104 STATUS (DR6): 00000000ffff4ff0 DR0: addr=0x0000000000000000, ref.count=0 DR1: addr=0x0000000000000008, ref.count=1 DR2: addr=0x0000000000000000, ref.count=0 DR3: addr=0x0000000000000000, ref.count=0 (gdb) info watch Num Type Disp Enb Address What 1 hw watchpoint keep n *(long *) 0x0 3 hw watchpoint keep n *(long *) 0x0 4 hw watchpoint keep n *(long *) 0x0 6 hw watchpoint keep y * (char (*)[16]) 0x0 (gdb)