[RFA] Fix hw watchpoints in process record.
Hui Zhu
teawater@gmail.com
Sat Nov 21 09:41:00 GMT 2009
Hi guys,
There are some patches about hw breakpoint are running on maillist.
If you don't mind, I suggest we wait until they OK.
Thanks,
Hui
On Sat, Nov 21, 2009 at 02:06, Michael Snyder <msnyder@vmware.com> wrote:
> OK, I withdraw my patch in favor of this one.
> If Hui likes it, let's check it in.
>
> Thanks for your help, Pedro!
>
> Pedro Alves wrote:
>>
>> On Thursday 05 November 2009 19:09:40, Pedro Alves wrote:
>>
>>> this assumes the target/arch has continuable watchpoints,
>>> like x86/x86_64. You'll see this misbehave on mips, when Hui
>>> contributes the support.
>>>
>>> You're evaluating the watchpoint expression on every event --- I
>>> have a feeling it would make more sense to decouple precord from
>>> that, like all other targets are decoupled. That is, have precord
>>> check for low level watchpoint triggers --- given [addr,length,type]
>>> low level watchpoints --- when memory changes (at record_mem_entry time),
>>> instead of evaluating the whole high-level watchpoint expression.
>>>
>>> [IMO, we should mostly think of precord as just another a
>>> simulator that mostly only interfaces gdb's core through
>>> the target_ methods.]
>>
>>
>> Sure enough, trying a simple test like this shows
>> breakage:
>>
>> 1 #include <stdio.h>
>> 2
>> 3 int glob;
>> 4
>> 5 int main ()
>> 6 {
>> 7 int loc = -1;
>> 8
>> 9 while (1)
>> 10 {
>> 11 printf ("glob = %d\n", glob);
>> 12 glob++;
>> 13 printf ("loc = %d\n", loc);
>> 14 loc++;
>> 15 }
>> 16 }
>>
>>
>> (gdb) start
>> Temporary breakpoint 1, main () at test.c:7
>> 7 int loc = -1;
>> (gdb) record
>> (gdb) watch loc
>> During symbol reading, incomplete CFI data; unspecified registers (e.g.,
>> rax) at 0x40049c.
>> Hardware watchpoint 2: loc
>> (gdb) watch glob
>> Hardware watchpoint 3: glob
>> (gdb) n
>> Hardware watchpoint 2: loc
>>
>> Old value = 0
>> New value = -1
>> main () at test.c:11
>> 11 printf ("glob = %d\n", glob);
>> (gdb)
>> glob = 0
>> 12 glob++;
>> (gdb)
>> Hardware watchpoint 3: glob
>>
>> <a few more next's, then>
>>
>> (gdb) reverse-continue
>> Continuing.
>> Hardware watchpoint 2: loc
>>
>> Old value = 1
>> New value = 0
>> main () at test.c:14
>> 14 loc++;
>> (gdb) reverse-continue
>> Continuing.
>>
>> Watchpoint 2 deleted because the program has left the block in
>> which its expression is valid.
>>
>> Watchpoint 2 deleted because the program has left the block in
>> which its expression is valid.
>> 0x00007ffff7aebd15 in _IO_file_write () from /lib/libc.so.6
>> (gdb)
>>
>> Nope, it hasn't left the block, I was continue'ing in the
>> loop... The trouble relates to the fact that precord forces
>> single-stepping --- which reverse single-steps into the
>> printf call --- and, watchpoint_check wants to check
>> if the watchpoints are in scope.
>>
>>
>> Here's the alternative implementation I was suggesting last
>> week. It ends up being simpler and more efficient, in fact.
>> We checking for watchpoint hits less often, and the check is
>> much cheaper since it doesn't do any extra reads, create
>> values, or evaluate expressions.
>>
>> I also didn't see any need for set_executing calls, which
>> were a sign of trouble.
>>
>> I've added a comment indicating what needs to be done
>> to support targets/archs with non-continuable watchpoints
>> (just check for inserted watchpoints that should trigger,
>> before actually doing any memory change)
>>
>> An alternative to that is to expose better the continuable vs
>> steppable watchpoints at the target layer, so that record can
>> always claim continuable watchpoints while replaying, dispite
>> what the target beneath or the arch supports.
>>
>>
>> This passed the new watch-reverse.exp and watch-precsave.exp.
>> Playing a bit with the small test shown above didn't show
>> any issue. Want to give it a try?
>>
>> --
>> Pedro Alves
>>
>>
>> ---
>> gdb/NEWS | 4 +
>> gdb/breakpoint.c | 33 ++++++++++
>> gdb/breakpoint.h | 5 +
>> gdb/record.c | 68
>> +++++++++++++++++++---
>> gdb/testsuite/gdb.reverse/watch-precsave.exp | 80
>> ++++++++++++++++++++++++++
>> gdb/testsuite/gdb.reverse/watch-reverse.exp | 82
>> ++++++++++++++++++++++++++-
>> 6 files changed, 260 insertions(+), 12 deletions(-)
>>
>> Index: src/gdb/breakpoint.c
>> ===================================================================
>> --- src.orig/gdb/breakpoint.c 2009-11-12 00:06:31.000000000 +0000
>> +++ src/gdb/breakpoint.c 2009-11-12 00:06:55.000000000 +0000
>> @@ -3063,6 +3063,39 @@ watchpoints_triggered (struct target_wai
>> return 1;
>> }
>>
>> +int
>> +hardware_watchpoint_inserted_in_range (CORE_ADDR addr, ULONGEST len)
>> +{
>> + struct breakpoint *bpt;
>> +
>> + ALL_BREAKPOINTS (bpt)
>> + {
>> + struct bp_location *loc;
>> +
>> + if (bpt->type != bp_hardware_watchpoint
>> + && bpt->type != bp_access_watchpoint)
>> + continue;
>> +
>> + if (!breakpoint_enabled (bpt))
>> + continue;
>> +
>> + for (loc = bpt->loc; loc; loc = loc->next)
>> + if (loc->inserted)
>> + {
>> + CORE_ADDR l, h;
>> +
>> + /* Check for interception. */
>> +
>> + l = max (loc->address, addr);
>> + h = min (loc->address + loc->length, addr + len);
>> +
>> + if (l < h)
>> + return 1;
>> + }
>> + }
>> + return 0;
>> +}
>> +
>> /* Possible return values for watchpoint_check (this can't be an enum
>> because of check_errors). */
>> /* The watchpoint has been deleted. */
>> Index: src/gdb/breakpoint.h
>> ===================================================================
>> --- src.orig/gdb/breakpoint.h 2009-11-12 00:06:31.000000000 +0000
>> +++ src/gdb/breakpoint.h 2009-11-12 00:06:55.000000000 +0000
>> @@ -978,4 +978,9 @@ extern struct breakpoint *get_tracepoint
>> is newly allocated; the caller should free when done with it. */
>> extern VEC(breakpoint_p) *all_tracepoints (void);
>>
>> +/* Returns true if there's a hardware watchpoint or access watchpoint
>> + inserted in the range defined by ADDR and LEN. */
>> +extern int hardware_watchpoint_inserted_in_range (CORE_ADDR addr,
>> + ULONGEST len);
>> +
>> #endif /* !defined (BREAKPOINT_H) */
>> Index: src/gdb/record.c
>> ===================================================================
>> --- src.orig/gdb/record.c 2009-11-12 00:06:31.000000000 +0000
>> +++ src/gdb/record.c 2009-11-12 00:06:55.000000000 +0000
>> @@ -224,6 +224,7 @@ static int (*record_beneath_to_insert_br
>> struct bp_target_info
>> *);
>> static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *,
>> struct bp_target_info
>> *);
>> +static int (*record_beneath_to_stopped_by_watchpoint) (void);
>>
>> /* Alloc and free functions for record_reg, record_mem, and record_end
>> entries. */
>> @@ -673,6 +674,9 @@ record_gdb_operation_disable_set (void)
>> return old_cleanups;
>> }
>>
>> +/* Flag set to TRUE for target_stopped_by_watchpoint. */
>> +static int record_hw_watchpoint = 0;
>> +
>> /* Execute one instruction from the record log. Each instruction in
>> the log will be represented by an arbitrary sequence of register
>> entries and memory entries, followed by an 'end' entry. */
>> @@ -739,7 +743,21 @@ record_exec_insn (struct regcache *regca
>> entry->u.mem.len);
>> }
>> else
>> - memcpy (record_get_loc (entry), mem, entry->u.mem.len);
>> + {
>> + memcpy (record_get_loc (entry), mem,
>> entry->u.mem.len);
>> +
>> + /* We've changed memory --- check if a hardware
>> + watchpoint should trap. Note that this
>> + presently assumes the target beneath supports
>> + continuable watchpoints. On non-continuable
>> + watchpoints target, we'll want to check this
>> + _before_ actually doing the memory change, and
>> + not doing the change at all if the watchpoint
>> + traps. */
>> + if (hardware_watchpoint_inserted_in_range
>> + (entry->u.mem.addr, entry->u.mem.len))
>> + record_hw_watchpoint = 1;
>> + }
>> }
>> }
>> }
>> @@ -770,6 +788,7 @@ static int (*tmp_to_insert_breakpoint) (
>> struct bp_target_info *);
>> static int (*tmp_to_remove_breakpoint) (struct gdbarch *,
>> struct bp_target_info *);
>> +static int (*tmp_to_stopped_by_watchpoint) (void);
>>
>> static void record_restore (void);
>>
>> @@ -894,6 +913,8 @@ record_open (char *name, int from_tty)
>> tmp_to_insert_breakpoint = t->to_insert_breakpoint;
>> if (!tmp_to_remove_breakpoint)
>> tmp_to_remove_breakpoint = t->to_remove_breakpoint;
>> + if (!tmp_to_stopped_by_watchpoint)
>> + tmp_to_stopped_by_watchpoint = t->to_stopped_by_watchpoint;
>> }
>> if (!tmp_to_xfer_partial)
>> error (_("Could not find 'to_xfer_partial' method on the target
>> stack."));
>> @@ -915,6 +936,7 @@ record_open (char *name, int from_tty)
>> record_beneath_to_xfer_partial = tmp_to_xfer_partial;
>> record_beneath_to_insert_breakpoint = tmp_to_insert_breakpoint;
>> record_beneath_to_remove_breakpoint = tmp_to_remove_breakpoint;
>> + record_beneath_to_stopped_by_watchpoint = tmp_to_stopped_by_watchpoint;
>>
>> if (current_target.to_stratum == core_stratum)
>> record_core_open_1 (name, from_tty);
>> @@ -1069,14 +1091,23 @@ record_wait (struct target_ops *ops,
>> {
>> struct regcache *regcache;
>>
>> - /* Yes -- check if there is a breakpoint. */
>> + /* Yes -- this is likely our single-step finishing,
>> + but check if there's any reason the core would be
>> + interested in the event. */
>> +
>> registers_changed ();
>> regcache = get_current_regcache ();
>> tmp_pc = regcache_read_pc (regcache);
>> - if (breakpoint_inserted_here_p (get_regcache_aspace
>> (regcache),
>> - tmp_pc))
>> +
>> + if (target_stopped_by_watchpoint ())
>> + {
>> + /* Always interested in watchpoints. */
>> + }
>> + else if (breakpoint_inserted_here_p (get_regcache_aspace
>> (regcache),
>> + tmp_pc))
>> {
>> - /* There is a breakpoint. GDB will want to stop.
>> */
>> + /* There is a breakpoint here. Let the core
>> + handle it. */
>> struct gdbarch *gdbarch = get_regcache_arch
>> (regcache);
>> CORE_ADDR decr_pc_after_break
>> = gdbarch_decr_pc_after_break (gdbarch);
>> @@ -1086,10 +1117,8 @@ record_wait (struct target_ops *ops,
>> }
>> else
>> {
>> - /* There is not a breakpoint, and gdb is not
>> - stepping, therefore gdb will not stop.
>> - Therefore we will not return to gdb.
>> - Record the insn and resume. */
>> + /* This must be a single-step trap. Record the
>> + insn and issue another step. */
>> if (!do_record_message (regcache, TARGET_SIGNAL_0))
>> break;
>>
>> @@ -1116,6 +1145,7 @@ record_wait (struct target_ops *ops,
>> struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups,
>> 0);
>> CORE_ADDR tmp_pc;
>>
>> + record_hw_watchpoint = 0;
>> status->kind = TARGET_WAITKIND_STOPPED;
>>
>> /* Check breakpoint when forward execute. */
>> @@ -1219,6 +1249,14 @@ record_wait (struct target_ops *ops,
>> gdbarch_decr_pc_after_break
>> (gdbarch));
>> continue_flag = 0;
>> }
>> +
>> + if (record_hw_watchpoint)
>> + {
>> + if (record_debug)
>> + fprintf_unfiltered (gdb_stdlog,
>> + "Process record: hit hw
>> watchpoint.\n");
>> + continue_flag = 0;
>> + }
>> /* Check target signal */
>> if (record_list->u.end.sigval != TARGET_SIGNAL_0)
>> /* FIXME: better way to check */
>> @@ -1260,6 +1298,16 @@ replay_out:
>> return inferior_ptid;
>> }
>>
>> +/* to_stopped_by_watchpoint method */
>> +static int
>> +record_stopped_by_watchpoint (void)
>> +{
>> + if (RECORD_IS_REPLAY)
>> + return record_hw_watchpoint;
>> + else
>> + return record_beneath_to_stopped_by_watchpoint ();
>> +}
>> +
>> /* "to_disconnect" method for process record target. */
>>
>> static void
>> @@ -1545,6 +1593,7 @@ init_record_ops (void)
>> record_ops.to_remove_breakpoint = record_remove_breakpoint;
>> record_ops.to_can_execute_reverse = record_can_execute_reverse;
>> record_ops.to_stratum = record_stratum;
>> + record_ops.to_stopped_by_watchpoint = record_stopped_by_watchpoint;
>> record_ops.to_magic = OPS_MAGIC;
>> }
>>
>> @@ -1750,6 +1799,7 @@ init_record_core_ops (void)
>> record_core_ops.to_can_execute_reverse = record_can_execute_reverse;
>> record_core_ops.to_has_execution = record_core_has_execution;
>> record_core_ops.to_stratum = record_stratum;
>> + record_core_ops.to_stopped_by_watchpoint =
>> record_stopped_by_watchpoint;
>> record_core_ops.to_magic = OPS_MAGIC;
>> }
>>
>> Index: src/gdb/NEWS
>> ===================================================================
>> --- src.orig/gdb/NEWS 2009-11-12 00:06:31.000000000 +0000
>> +++ src/gdb/NEWS 2009-11-12 00:06:55.000000000 +0000
>> @@ -71,6 +71,10 @@ show follow-exec-mode
>> creates a new one. This is useful to be able to restart the old
>> executable after the inferior having done an exec call.
>>
>> +* Bug fixes
>> +
>> +Process record now works correctly with hardware watchpoints.
>> +
>> *** Changes in GDB 7.0
>>
>> * GDB now has an interface for JIT compilation. Applications that
>> Index: src/gdb/testsuite/gdb.reverse/watch-reverse.exp
>> ===================================================================
>> --- src.orig/gdb/testsuite/gdb.reverse/watch-reverse.exp 2009-11-12
>> 00:06:31.000000000 +0000
>> +++ src/gdb/testsuite/gdb.reverse/watch-reverse.exp 2009-11-12
>> 00:10:23.000000000 +0000
>> @@ -38,8 +38,8 @@ if [target_info exists gdb,use_precord]
>> # FIXME: command ought to acknowledge, so we can test if it succeeded.
>> }
>>
>> -# Only software watchpoints can be used in reverse
>> -gdb_test "set can-use-hw-watchpoints 0" "" ""
>> +# Test software watchpoints
>> +gdb_test "set can-use-hw-watchpoints 0" "" "disable hw watchpoints"
>>
>> gdb_test "break marker1" \
>> "Breakpoint $decimal at $hex: file .*$srcfile, line $decimal.*" \
>> @@ -122,3 +122,81 @@ gdb_test "continue" \
>> gdb_test "continue" \
>> ".*\[Ww\]atchpoint.*ival3.*Old value = 0.*New value = -1.*ival3 =
>> count; ival4 = count;.*" \
>> "watchpoint hit in reverse, fifth time"
>> +
>> +gdb_test "set can-use-hw-watchpoints 1" "" "enable hw watchpoints"
>> +
>> +###
>> +###
>> +###
>> +
>> +# FIXME 'set exec-dir' command should give some output so we can test.
>> +gdb_test "set exec-direction forward" "" "set forward"
>> +
>> +# Continue until first change, from -1 to 0
>> +
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = -1.*New value = 0.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit, forward replay, first time"
>> +
>> +# Continue until the next change, from 0 to 1.
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 0.*New value = 1.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit, forward replay, second time"
>> +
>> +# Continue until the next change, from 1 to 2.
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 1.*New value = 2.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit, forward replay, third time"
>> +
>> +# Continue until the next change, from 2 to 3.
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 2.*New value = 3.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit, forward replay, fourth time"
>> +
>> +# Continue until the next change, from 3 to 4.
>> +# Note that this one is outside the loop.
>> +
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 3.*New value = 4.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit, forward replay, fifth time"
>> +
>> +# Continue until we hit the finishing marker function.
>> +# Make sure we hit no more watchpoints.
>> +
>> +gdb_test "continue" "marker2 .*" "replay forward to marker2"
>> +
>> +###
>> +###
>> +###
>> +
>> +# FIXME 'set exec-dir' command should give some output so we can test.
>> +gdb_test "set exec-direction reverse" "" "set reverse"
>> +
>> +# Reverse until the previous change, from 4 to 3
>> +# Note that this one is outside the loop
>> +
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 4.*New value = 3.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit in reverse, HW, first time"
>> +
>> +# Reverse until the previous change, from 3 to 2.
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 3.*New value = 2.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit in reverse, HW, second time"
>> +
>> +# Reverse until the previous change, from 2 to 1.
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 2.*New value = 1.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit in reverse, HW, third time"
>> +
>> +# Reverse until the previous change, from 1 to 0.
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 1.*New value = 0.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit in reverse, HW, fourth time"
>> +
>> +# Reverse until first change, from 0 to -1
>> +
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 0.*New value = -1.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit in reverse, HW, fifth time"
>> +
>> Index: src/gdb/testsuite/gdb.reverse/watch-precsave.exp
>> ===================================================================
>> --- src.orig/gdb/testsuite/gdb.reverse/watch-precsave.exp 2009-11-12
>> 00:06:31.000000000 +0000
>> +++ src/gdb/testsuite/gdb.reverse/watch-precsave.exp 2009-11-12
>> 00:10:07.000000000 +0000
>> @@ -1,4 +1,4 @@
>> -# Copyright 2008, 2009 Free Software Foundation, Inc.
>> +# Copyright 2009 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
>> @@ -140,3 +140,81 @@ gdb_test "continue" \
>> gdb_test "continue" \
>> ".*\[Ww\]atchpoint.*ival3.*Old value = 0.*New value = -1.*ival3 =
>> count; ival4 = count;.*" \
>> "watchpoint hit in reverse, fifth time"
>> +
>> +gdb_test "set can-use-hw-watchpoints 1" "" "enable hw watchpoints"
>> +
>> +###
>> +###
>> +###
>> +
>> +# FIXME 'set exec-dir' command should give some output so we can test.
>> +gdb_test "set exec-direction forward" "" "set forward"
>> +
>> +# Continue until first change, from -1 to 0
>> +
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = -1.*New value = 0.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit, forward replay, first time"
>> +
>> +# Continue until the next change, from 0 to 1.
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 0.*New value = 1.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit, forward replay, second time"
>> +
>> +# Continue until the next change, from 1 to 2.
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 1.*New value = 2.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit, forward replay, third time"
>> +
>> +# Continue until the next change, from 2 to 3.
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 2.*New value = 3.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit, forward replay, fourth time"
>> +
>> +# Continue until the next change, from 3 to 4.
>> +# Note that this one is outside the loop.
>> +
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 3.*New value = 4.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit, forward replay, fifth time"
>> +
>> +# Continue until we hit the finishing marker function.
>> +# Make sure we hit no more watchpoints.
>> +
>> +gdb_test "continue" "marker2 .*" "replay forward to marker2"
>> +
>> +###
>> +###
>> +###
>> +
>> +# FIXME 'set exec-dir' command should give some output so we can test.
>> +gdb_test "set exec-direction reverse" "" "set reverse"
>> +
>> +# Reverse until the previous change, from 4 to 3
>> +# Note that this one is outside the loop
>> +
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 4.*New value = 3.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit in reverse, HW, first time"
>> +
>> +# Reverse until the previous change, from 3 to 2.
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 3.*New value = 2.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit in reverse, HW, second time"
>> +
>> +# Reverse until the previous change, from 2 to 1.
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 2.*New value = 1.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit in reverse, HW, third time"
>> +
>> +# Reverse until the previous change, from 1 to 0.
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 1.*New value = 0.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit in reverse, HW, fourth time"
>> +
>> +# Reverse until first change, from 0 to -1
>> +
>> +gdb_test "continue" \
>> + ".*\[Ww\]atchpoint.*ival3.*Old value = 0.*New value = -1.*ival3 =
>> count; ival4 = count;.*" \
>> + "watchpoint hit in reverse, HW, fifth time"
>> +
>>
>
>
More information about the Gdb-patches
mailing list