From: Tom Tromey Date: Mon, 30 Aug 2021 19:58:48 +0000 (-0600) Subject: Add "task" keyword to the "watch" command X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=8a18382f94515b4be7e51dbe3865d202403d21d5;p=binutils-gdb.git 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. --- 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;