This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [patch] Watchpoints: support for thread <thread_num> parameters
- From: Luis Machado <luisgpm at linux dot vnet dot ibm dot com>
- To: Daniel Jacobowitz <drow at false dot org>
- Cc: Eli Zaretskii <eliz at gnu dot org>, gdb-patches at sourceware dot org
- Date: Tue, 13 Nov 2007 11:50:11 -0200
- Subject: Re: [patch] Watchpoints: support for thread <thread_num> parameters
- References: <1187298178.5853.11.camel@localhost> <uwsvuv4re.fsf@gnu.org> <1187365616.4520.14.camel@localhost> <20071011193558.GE30810@caradoc.them.org> <1192134591.18528.1.camel@localhost>
- Reply-to: luisgpm at linux dot vnet dot ibm dot com
Hi folks,
Here is the patch together with the testcase and the documentation bits.
Please let me know if there are any improvements/corrections suitable
for this one.
Best regards,
Luis
On Thu, 2007-10-11 at 17:29 -0300, Luis Machado wrote:
> Daniel, Eli,
>
> Thanks for reviewing.
>
> I'll provide the necessary bits of documentation and a testcase for this
> one, then i'll attach those to the patch.
>
> Regards,
> On Thu, 2007-10-11 at 15:35 -0400, Daniel Jacobowitz wrote:
> > On Fri, Aug 17, 2007 at 12:46:56PM -0300, Luis Machado wrote:
> > > 2007-08-16 Luis Machado <luisgpm@linux.vnet.ibm.com>
> > >
> > > * breakpoint.c: (watch_command_1): Parse additional optional
> > > "thread" parameter to the watchpoint command and set the
> > > "thread" member of the breakpoint struct.
> >
> > Like Eli, I'm happy enough with this version - but it needs
> > documentation and a test case.
> >
2007-11-13 Luis Machado <luisgpm@br.ibm.com>
* breakpoint.c: (watch_command_1): Parse additional optional
"thread" parameter to the watchpoint command and set the
"thread" member of the breakpoint struct.
* doc/gdb.texinfo: Add new parameter's description.
* testsuite/gdb.base/watch_thread_num.c: New testcase source file.
* testsuite/gdb.base/watch_thread_num.exp: New testcase expect file.
Index: gdb/breakpoint.c
===================================================================
--- gdb.orig/breakpoint.c 2007-11-12 11:22:15.000000000 -0800
+++ gdb/breakpoint.c 2007-11-12 11:22:18.000000000 -0800
@@ -5882,7 +5882,7 @@
struct frame_info *prev_frame = NULL;
char *exp_start = NULL;
char *exp_end = NULL;
- char *tok, *end_tok;
+ char *tok, *id_tok_start, *end_tok;
int toklen;
char *cond_start = NULL;
char *cond_end = NULL;
@@ -5890,10 +5890,72 @@
int i, other_type_used, target_resources_ok = 0;
enum bptype bp_type;
int mem_cnt = 0;
+ int thread = -1;
init_sal (&sal); /* initialize to zeroes */
- /* Parse arguments. */
+ /* Make sure that we actually have parameters to parse. */
+ if (arg != NULL && strlen (arg) >= 1)
+ {
+ toklen = strlen (arg); /* Size of argument list. */
+
+ /* Points tok to the end of the argument list. */
+ tok = arg + toklen - 1;
+
+ /* Go backwards in the parameters list. Skip the last parameter.
+ If we're expecting a 'thread <thread_num>' parameter, this should
+ be the thread identifier. */
+ while (tok > arg && (*tok == ' ' || *tok == '\t'))
+ tok--;
+ while (tok > arg && (*tok != ' ' && *tok != '\t'))
+ tok--;
+
+ /* Points end_tok to the beginning of the last token. */
+ id_tok_start = tok + 1;
+
+ /* Go backwards in the parameters list. Skip one more parameter.
+ If we're expecting a 'thread <thread_num>' parameter, we should
+ reach a "thread" token. */
+ while (tok > arg && (*tok == ' ' || *tok == '\t'))
+ tok--;
+
+ end_tok = tok;
+
+ while (tok > arg && (*tok != ' ' && *tok != '\t'))
+ tok--;
+
+ /* Move the pointer forward to skip the whitespace and
+ calculate the length of the token. */
+ tok++;
+ toklen = end_tok - tok;
+
+ if (toklen >= 1 && strncmp (tok, "thread", toklen) == 0)
+ {
+ /* At this point we've found a "thread" token, which means
+ the user is trying to set a watchpoint that triggers
+ only in a specific thread. */
+ char *endp;
+
+ /* Extract the thread ID from the next token. */
+ thread = strtol (id_tok_start, &endp, 0);
+
+ /* Check if the user provided a valid numeric value for the
+ thread ID. */
+ if (*endp != ' ' && *endp != '\t' && *endp != '\0')
+ error (_("Invalid thread ID specification %s."), id_tok_start);
+
+ /* Check if the thread actually exists. */
+ if (!valid_thread_id (thread))
+ error (_("Unknown thread %d."), thread);
+
+ /* Truncate the string and get rid of the thread <thread_num>
+ parameter before the parameter list is parsed by the
+ evaluate_expression() function. */
+ *tok = '\0';
+ }
+ }
+
+ /* Parse the rest of the arguments. */
innermost_block = NULL;
exp_start = arg;
exp = parse_exp_1 (&arg, 0, 0);
@@ -5986,6 +6048,7 @@
b = set_raw_breakpoint (sal, bp_type);
set_breakpoint_count (breakpoint_count + 1);
b->number = breakpoint_count;
+ b->thread = thread;
b->disposition = disp_donttouch;
b->exp = exp;
b->exp_valid_block = exp_valid_block;
Index: gdb/testsuite/gdb.base/watch_thread_num.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb/testsuite/gdb.base/watch_thread_num.c 2007-11-13 06:35:30.000000000 -0800
@@ -0,0 +1,63 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2002, 2003, 2004 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ This file is copied from schedlock.c. */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+void *thread_function (void *arg); /* Pointer to function executed by each thread */
+
+#define NUM 5
+
+static unsigned int shared_var = 1;
+
+int main () {
+ int res;
+ pthread_t threads[NUM];
+ void *thread_result;
+ long i;
+
+ for (i = 0; i < NUM; i++)
+ {
+ res = pthread_create (&threads[i],
+ NULL,
+ thread_function,
+ (void *) i);
+ }
+
+ thread_result = thread_function ((void *) i);
+
+ exit (EXIT_SUCCESS);
+}
+
+void *thread_function (void *arg) {
+ int my_number = (long) arg;
+ /* Don't run forever. Run just short of it :) */
+ while (shared_var > 0)
+ {
+ shared_var++;
+ usleep (1); /* Loop increment. */
+ }
+
+ pthread_exit (NULL);
+}
+
Index: gdb/testsuite/gdb.base/watch_thread_num.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb/testsuite/gdb.base/watch_thread_num.exp 2007-11-12 11:26:37.000000000 -0800
@@ -0,0 +1,67 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# 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/>. */
+
+# watch-thread_num.exp Test thread <thread_num> parameter for
+# watch commands.
+#
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+set testfile watch_thread_num
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+# What compiler are we using?
+#
+if [get_compiler_info ${binfile}] {
+ return -1
+}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug libs=-lpthread}] != "" } {
+ untested watch_thread_num.exp
+ return -1
+}
+
+# use this to debug:
+#log_user 1
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+if { ![runto main] } then {
+ fail "run to main"
+ return
+}
+
+gdb_test "watch shared_var thread 0" "Unknown thread 0\." "Watchpoint on invalid thread"
+gdb_test "watch shared_var thread" "A syntax error in expression, near `thread'\." "Invalid watch syntax"
+
+gdb_test "Next 5" ""
+
+gdb_test "watch shared_var thread 2" "Hardware watchpoint 2: shared_var" "Watchpoint on shared variable"
+gdb_test "info breakpoint 2" "stop only in thread 2"
+
+for {set i 0} {$i < 10} {incr i 1} {
+gdb_test "continue" "Hardware watchpoint 2: shared_var.*" "Watchpoint triggered"
+gdb_test "thread" "\\\[Current thread is 2 \\\(Thread $hex \\\(LWP $decimal\\\)\\\)\\\]" "Check thread that triggered"
+}
+
Index: gdb/doc/gdb.texinfo
===================================================================
--- gdb.orig/doc/gdb.texinfo 2007-11-12 11:22:15.000000000 -0800
+++ gdb/doc/gdb.texinfo 2007-11-12 11:22:18.000000000 -0800
@@ -3210,7 +3210,7 @@
catch errors where you have no clue what part of your program is the
culprit.)
-On some systems, such as HP-UX, @sc{gnu}/Linux and most other
+On some systems, such as HP-UX, PowerPC, @sc{gnu}/Linux and most other
x86-based targets, @value{GDBN} includes support for hardware
watchpoints, which do not slow down the running of your program.
@@ -3351,6 +3351,13 @@
In multi-threaded programs, watchpoints will detect changes to the
watched expression from every thread.
+@kindex watch thread thread_num
+@item watch @var{expr} thread thread_num
+Set a watchpoint that will break when @var{expr} is either read from
+or written into by the thread identified by @var{thread_num}. If @var{expr}
+is modified by any other threads not matching @var{thread_num}, @value{GDBN}
+will not break. Note that this will only work with Hardware Watchpoints.
+
@quotation
@emph{Warning:} In multi-threaded programs, software watchpoints
have only limited usefulness. If @value{GDBN} creates a software