From 8a18382f94515b4be7e51dbe3865d202403d21d5 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 30 Aug 2021 13:58:48 -0600 Subject: [PATCH] Add "task" keyword to the "watch" command Breakpoints in gdb can be made specific to an Ada task using the "task" qualifier. This patch applies this same idea to watchpoints. --- gdb/NEWS | 3 + gdb/breakpoint.c | 12 ++++ gdb/doc/gdb.texinfo | 5 +- gdb/testsuite/gdb.ada/task_watch.exp | 83 ++++++++++++++++++++++++ gdb/testsuite/gdb.ada/task_watch/foo.adb | 73 +++++++++++++++++++++ 5 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 gdb/testsuite/gdb.ada/task_watch.exp create mode 100644 gdb/testsuite/gdb.ada/task_watch/foo.adb diff --git a/gdb/NEWS b/gdb/NEWS index eeca1d39b10..b0a3a0867ea 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -24,6 +24,9 @@ show varsize-limit These are now deprecated aliases for "set max-value-size" and "show max-value-size". +watch [...] task ID + Watchpoints can now be restricted to a specific Ada task. + maint set internal-error backtrace on|off maint show internal-error backtrace maint set internal-warning backtrace on|off diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index b1eae3af634..acf38818c3f 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -10557,6 +10557,7 @@ watch_command_1 (const char *arg, int accessflag, int from_tty, the hardware watchpoint. */ bool use_mask = false; CORE_ADDR mask = 0; + int task = 0; /* Make sure that we actually have parameters to parse. */ if (arg != NULL && arg[0] != '\0') @@ -10612,6 +10613,16 @@ watch_command_1 (const char *arg, int accessflag, int from_tty, thread = thr->global_num; } + else if (toklen == 4 && startswith (tok, "task")) + { + char *tmp; + + task = strtol (value_start, &tmp, 0); + if (tmp == value_start) + error (_("Junk after task keyword.")); + if (!valid_task_id (task)) + error (_("Unknown task %d."), task); + } else if (toklen == 4 && startswith (tok, "mask")) { /* We've found a "mask" token, which means the user wants to @@ -10785,6 +10796,7 @@ watch_command_1 (const char *arg, int accessflag, int from_tty, init_raw_breakpoint_without_location (w.get (), NULL, bp_type, &watchpoint_breakpoint_ops); w->thread = thread; + w->task = task; w->disposition = disp_donttouch; w->pspace = current_program_space; w->exp = std::move (exp); diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 9d507795993..40f3e245a11 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -4860,7 +4860,7 @@ slow down the running of your program. @table @code @kindex watch -@item watch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{thread-id}@r{]} @r{[}mask @var{maskvalue}@r{]} +@item watch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{thread-id}@r{]} @r{[}mask @var{maskvalue}@r{]} @r{[}task @var{task-id}@r{]} Set a watchpoint for an expression. @value{GDBN} will break when the expression @var{expr} is written into by the program and its value changes. The simplest (and the most popular) use of this command is @@ -4877,6 +4877,9 @@ change the value of @var{expr}, @value{GDBN} will not break. Note that watchpoints restricted to a single thread in this way only work with Hardware Watchpoints. +Similarly, if the @code{task} argument is given, then the watchpoint +will be specific to the indicated Ada task (@pxref{Ada Tasks}). + Ordinarily a watchpoint respects the scope of variables in @var{expr} (see below). The @code{-location} argument tells @value{GDBN} to instead watch the memory referred to by @var{expr}. In this case, diff --git a/gdb/testsuite/gdb.ada/task_watch.exp b/gdb/testsuite/gdb.ada/task_watch.exp new file mode 100644 index 00000000000..fc276ef5f0e --- /dev/null +++ b/gdb/testsuite/gdb.ada/task_watch.exp @@ -0,0 +1,83 @@ +# Copyright 2009-2021 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 . + +# Test task-specific watchpoints. + +load_lib "ada.exp" + +if { [skip_ada_tests] } { return -1 } + +standard_ada_testfile foo + +if {[gdb_compile_ada "${srcfile}" "${binfile}" executable {debug}] != ""} { + return -1 +} + +clean_restart ${testfile} + +set bp_location [gdb_get_line_number "STOP_HERE" ${testdir}/foo.adb] +runto "foo.adb:$bp_location" + +# Make sure that all tasks appear in the "info tasks" listing, and +# that the active task is the environment task. +gdb_test "info tasks" \ + [join {" +ID +TID P-ID Pri State +Name" \ + "\\* +1 .* main_task" \ + " +2 .* task_list\\(1\\)" \ + " +3 .* task_list\\(2\\)" \ + " +4 .* task_list\\(3\\)"} \ + "\r\n"] \ + "info tasks before inserting breakpoint" + +# Insert a watchpoint that should stop only if task 3 stops, and +# extract its number. +set bp_number -1 +set test "watch -location value task 3" +gdb_test_multiple $test $test { + -re "atchpoint ($decimal): -location value\r\n$gdb_prompt $" { + set bp_number $expect_out(1,string) + pass $test + } +} + +if {$bp_number < 0} { + return +} + +# Continue to that watchpoint. Task 2 should hit it first, and GDB +# is expected to ignore that hit and resume the execution. Only then +# task 3 will hit our watchpoint, and GDB is expected to stop at that +# point. Also make sure that GDB reports the correct watchpoint number. +gdb_test "continue" \ + ".* hit .*atchpoint $bp_number: -location value.*Old value = 1.*New value = 2.*" \ + "continue to watchpoint" + +# Check that it is indeed task 3 that hit the watchpoint by checking +# which is the active task. +gdb_test "info tasks" \ + [join {" +ID +TID P-ID Pri State +Name" \ + " +1 .* main_task" \ + " +2 .* task_list\\(1\\)" \ + "\\* +3 .* task_list\\(2\\)" \ + " +4 .* task_list\\(3\\)"} \ + "\r\n"] \ + "info tasks after hitting watchpoint" + +# Now, resume the execution and make sure that GDB does not stop when +# task 4 hits the watchpoint. Continuing thus results in our program +# running to completion. +set bp_location [gdb_get_line_number "STOP_HERE_2" ${testdir}/foo.adb] +gdb_breakpoint foo.adb:$bp_location +gdb_continue_to_breakpoint second ".*foo.adb:$bp_location.*null; -- STOP_HERE_2" diff --git a/gdb/testsuite/gdb.ada/task_watch/foo.adb b/gdb/testsuite/gdb.ada/task_watch/foo.adb new file mode 100644 index 00000000000..f3540ec05c7 --- /dev/null +++ b/gdb/testsuite/gdb.ada/task_watch/foo.adb @@ -0,0 +1,73 @@ +-- Copyright 2009-2021 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 . + +procedure Foo is + + Value : Integer := 0; + + task type Caller is + entry Initialize; + entry Call_Break_Me; + entry Finalize; + end Caller; + type Caller_Ptr is access Caller; + + procedure Break_Me is + begin + Value := Value + 1; + end Break_Me; + + task body Caller is + begin + accept Initialize do + null; + end Initialize; + accept Call_Break_Me do + Break_Me; + end Call_Break_Me; + accept Finalize do + null; + end Finalize; + end Caller; + + Task_List : array (1 .. 3) of Caller_Ptr; + +begin + + -- Start all our tasks, and call the "Initialize" entry to make + -- sure all of them have now been started. We call that entry + -- immediately after having created the task in order to make sure + -- that we wait for that task to be created before we try to create + -- another one. That way, we know that the order in our Task_List + -- corresponds to the order in the GNAT runtime. + for J in Task_List'Range loop + Task_List (J) := new Caller; + Task_List (J).Initialize; + end loop; + + -- Next, call their Call_Break_Me entry of each task, using the same + -- order as the order used to create them. + for J in Task_List'Range loop -- STOP_HERE + Task_List (J).Call_Break_Me; + end loop; + + -- And finally, let all the tasks die... + for J in Task_List'Range loop + Task_List (J).Finalize; + end loop; + + null; -- STOP_HERE_2 + +end Foo; -- 2.30.2