Re: [patch] Watchpoints: support for thread <thread_num> parameters

Luis Machado
2007-12-17T12:36:05Z

[-- Attachment #1: Type: text/plain, Size: 986 bytes --]

On Sun, 2007-12-16 at 16:48 -0500, Daniel Jacobowitz wrote:
> On Wed, Nov 14, 2007 at 10:20:01AM -0200, Luis Machado wrote:
> > 2007-11-14 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.
>
> Remember to put the doc/ and testsuite/ bits in their respective
> changelogs. This patch is OK.
>
> > +/* This testcase is part of GDB, the GNU debugger.
> > +
> > + Copyright 2002, 2003, 2004 Free Software Foundation, Inc.
>
> Add 2007 here.
>

Thanks for reviewing this. I've checked in the updated version.

Regards,
--
Luis Machado
Software Engineer
IBM Linux Technology Center

[-- Attachment #2: watch_thread_param.diff --]
[-- Type: text/x-patch, Size: 9368 bytes --]

2007-12-17 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-12-17 03:18:56.000000000 -0800
+++ gdb/breakpoint.c 2007-12-17 03:41:12.000000000 -0800
@@ -5606,7 +5606,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;
@@ -5614,10 +5614,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 && arg[0] != '\0')
+ {
+ 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);
@@ -5710,6 +5772,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-12-17 03:41:12.000000000 -0800
@@ -0,0 +1,63 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2002, 2003, 2004, 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 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-12-17 03:41:12.000000000 -0800
@@ -0,0 +1,64 @@
+# 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
+}
+
+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-12-17 03:18:27.000000000 -0800
+++ gdb/doc/gdb.texinfo 2007-12-17 03:41:12.000000000 -0800
@@ -3216,7 +3216,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.

@@ -3357,6 +3357,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 @var{threadnum}
+Set a watchpoint that will break when @var{expr} is either read from
+or written into by the thread identified by @var{threadnum}. If @var{expr}
+is modified by any other threads not matching @var{threadnum}, @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