]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/reverse.c
Automatic date update in version.in
[thirdparty/binutils-gdb.git] / gdb / reverse.c
CommitLineData
b2175913
MS
1/* Reverse execution and reverse debugging.
2
d01e8234 3 Copyright (C) 2006-2025 Free Software Foundation, Inc.
b2175913
MS
4
5 This file is part of GDB.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
5b1ba0e5 9 the Free Software Foundation; either version 3 of the License, or
b2175913
MS
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
5b1ba0e5 18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
b2175913 19
b2175913
MS
20#include "target.h"
21#include "top.h"
22#include "cli/cli-cmds.h"
23#include "cli/cli-decode.h"
e9cafbcc 24#include "cli/cli-utils.h"
b2175913 25#include "inferior.h"
45741a9c 26#include "infrun.h"
6b04bdb7 27#include "regcache.h"
b2175913
MS
28
29/* User interface:
30 reverse-step, reverse-next etc. */
31
b2175913
MS
32/* exec_reverse_once -- accepts an arbitrary gdb command (string),
33 and executes it with exec-direction set to 'reverse'.
34
35 Used to implement reverse-next etc. commands. */
36
37static void
0b39b52e 38exec_reverse_once (const char *cmd, const char *args, int from_tty)
b2175913 39{
b2175913 40 enum exec_direction_kind dir = execution_direction;
b2175913 41
b2175913
MS
42 if (dir == EXEC_REVERSE)
43 error (_("Already in reverse mode. Use '%s' or 'set exec-dir forward'."),
44 cmd);
45
05374cfd 46 if (!target_can_execute_reverse ())
d777bf0d 47 error (_("Target %s does not support this command."), target_shortname ());
b2175913 48
2ec845e7
TT
49 std::string reverse_command = string_printf ("%s %s", cmd, args ? args : "");
50 scoped_restore restore_exec_dir
51 = make_scoped_restore (&execution_direction, EXEC_REVERSE);
95a6b0a1 52 execute_command (reverse_command.c_str (), from_tty);
b2175913
MS
53}
54
55static void
0b39b52e 56reverse_step (const char *args, int from_tty)
b2175913
MS
57{
58 exec_reverse_once ("step", args, from_tty);
59}
60
61static void
0b39b52e 62reverse_stepi (const char *args, int from_tty)
b2175913
MS
63{
64 exec_reverse_once ("stepi", args, from_tty);
65}
66
67static void
0b39b52e 68reverse_next (const char *args, int from_tty)
b2175913
MS
69{
70 exec_reverse_once ("next", args, from_tty);
71}
72
73static void
0b39b52e 74reverse_nexti (const char *args, int from_tty)
b2175913
MS
75{
76 exec_reverse_once ("nexti", args, from_tty);
77}
78
79static void
0b39b52e 80reverse_continue (const char *args, int from_tty)
b2175913
MS
81{
82 exec_reverse_once ("continue", args, from_tty);
83}
84
85static void
0b39b52e 86reverse_finish (const char *args, int from_tty)
b2175913
MS
87{
88 exec_reverse_once ("finish", args, from_tty);
89}
90
6b04bdb7
MS
91/* Data structures for a bookmark list. */
92
93struct bookmark {
243cf0f6
TT
94 int number = 0;
95 CORE_ADDR pc = 0;
6b04bdb7 96 struct symtab_and_line sal;
243cf0f6 97 gdb::unique_xmalloc_ptr<gdb_byte> opaque_data;
6b04bdb7
MS
98};
99
243cf0f6 100static std::vector<struct bookmark> all_bookmarks;
6b04bdb7
MS
101static int bookmark_count;
102
6b04bdb7
MS
103/* save_bookmark_command -- implement "bookmark" command.
104 Call target method to get a bookmark identifier.
105 Insert bookmark identifier into list.
106
107 Identifier will be a malloc string (gdb_byte *).
108 Up to us to free it as required. */
109
110static void
0b39b52e 111save_bookmark_command (const char *args, int from_tty)
6b04bdb7
MS
112{
113 /* Get target's idea of a bookmark. */
114 gdb_byte *bookmark_id = target_get_bookmark (args, from_tty);
9c742269
SM
115 regcache *regcache = get_thread_regcache (inferior_thread ());
116 gdbarch *gdbarch = regcache->arch ();
6b04bdb7
MS
117
118 /* CR should not cause another identical bookmark. */
119 dont_repeat ();
120
121 if (bookmark_id == NULL)
122 error (_("target_get_bookmark failed."));
123
124 /* Set up a bookmark struct. */
b36a2634 125 bookmark &b = all_bookmarks.emplace_back ();
243cf0f6 126 b.number = ++bookmark_count;
9c742269 127 b.pc = regcache_read_pc (regcache);
243cf0f6
TT
128 b.sal = find_pc_line (b.pc, 0);
129 b.sal.pspace = get_frame_program_space (get_current_frame ());
130 b.opaque_data.reset (bookmark_id);
131
132 gdb_printf (_("Saved bookmark %d at %s\n"), b.number,
133 paddress (gdbarch, b.sal.pc));
6b04bdb7
MS
134}
135
136/* Implement "delete bookmark" command. */
137
243cf0f6 138static bool
7d357efd 139delete_one_bookmark (int num)
6b04bdb7 140{
7d357efd 141 /* Find bookmark with corresponding number. */
243cf0f6
TT
142 for (auto iter = all_bookmarks.begin ();
143 iter != all_bookmarks.end ();
144 ++iter)
6b04bdb7 145 {
243cf0f6
TT
146 if (iter->number == num)
147 {
148 all_bookmarks.erase (iter);
149 return true;
150 }
6b04bdb7 151 }
243cf0f6 152 return false;
6b04bdb7
MS
153}
154
155static void
243cf0f6 156delete_all_bookmarks ()
6b04bdb7 157{
243cf0f6 158 all_bookmarks.clear ();
6b04bdb7
MS
159}
160
161static void
8949cb87 162delete_bookmark_command (const char *args, int from_tty)
6b04bdb7 163{
243cf0f6 164 if (all_bookmarks.empty ())
6b04bdb7
MS
165 {
166 warning (_("No bookmarks."));
167 return;
168 }
169
170 if (args == NULL || args[0] == '\0')
171 {
172 if (from_tty && !query (_("Delete all bookmarks? ")))
173 return;
174 delete_all_bookmarks ();
175 return;
176 }
177
bfd28288
PA
178 number_or_range_parser parser (args);
179 while (!parser.finished ())
7d357efd 180 {
bfd28288 181 int num = parser.get_number ();
7d357efd
MS
182 if (!delete_one_bookmark (num))
183 /* Not found. */
184 warning (_("No bookmark #%d."), num);
185 }
6b04bdb7
MS
186}
187
188/* Implement "goto-bookmark" command. */
189
190static void
0b39b52e 191goto_bookmark_command (const char *args, int from_tty)
6b04bdb7 192{
6b04bdb7 193 unsigned long num;
0b39b52e 194 const char *p = args;
6b04bdb7
MS
195
196 if (args == NULL || args[0] == '\0')
197 error (_("Command requires an argument."));
198
61012eef
GB
199 if (startswith (args, "start")
200 || startswith (args, "begin")
201 || startswith (args, "end"))
6b04bdb7
MS
202 {
203 /* Special case. Give target opportunity to handle. */
0f928d68 204 target_goto_bookmark ((gdb_byte *) args, from_tty);
6b04bdb7
MS
205 return;
206 }
207
208 if (args[0] == '\'' || args[0] == '\"')
209 {
210 /* Special case -- quoted string. Pass on to target. */
211 if (args[strlen (args) - 1] != args[0])
212 error (_("Unbalanced quotes: %s"), args);
0f928d68 213 target_goto_bookmark ((gdb_byte *) args, from_tty);
6b04bdb7
MS
214 return;
215 }
216
217 /* General case. Bookmark identified by bookmark number. */
7d357efd 218 num = get_number (&args);
0c13193f
YQ
219
220 if (num == 0)
221 error (_("goto-bookmark: invalid bookmark number '%s'."), p);
222
243cf0f6 223 for (const bookmark &iter : all_bookmarks)
6b04bdb7 224 {
243cf0f6
TT
225 if (iter.number == num)
226 {
227 /* Found. Send to target method. */
228 target_goto_bookmark (iter.opaque_data.get (), from_tty);
229 return;
230 }
6b04bdb7
MS
231 }
232 /* Not found. */
0c13193f 233 error (_("goto-bookmark: no bookmark found for '%s'."), p);
6b04bdb7
MS
234}
235
7d357efd
MS
236static int
237bookmark_1 (int bnum)
238{
9c742269 239 gdbarch *gdbarch = get_thread_regcache (inferior_thread ())->arch ();
7d357efd
MS
240 int matched = 0;
241
243cf0f6
TT
242 for (const bookmark &iter : all_bookmarks)
243 {
244 if (bnum == -1 || bnum == iter.number)
245 {
246 gdb_printf (" %d %s '%s'\n",
247 iter.number,
248 paddress (gdbarch, iter.pc),
249 iter.opaque_data.get ());
250 matched++;
251 }
252 }
7d357efd
MS
253
254 if (bnum > 0 && matched == 0)
6cb06a8c 255 gdb_printf ("No bookmark #%d\n", bnum);
7d357efd
MS
256
257 return matched;
258}
259
6b04bdb7
MS
260/* Implement "info bookmarks" command. */
261
262static void
1d12d88f 263info_bookmarks_command (const char *args, int from_tty)
6b04bdb7 264{
243cf0f6 265 if (all_bookmarks.empty ())
6cb06a8c 266 gdb_printf (_("No bookmarks.\n"));
7d357efd
MS
267 else if (args == NULL || *args == '\0')
268 bookmark_1 (-1);
269 else
197f0a60 270 {
bfd28288
PA
271 number_or_range_parser parser (args);
272 while (!parser.finished ())
197f0a60 273 {
bfd28288 274 int bnum = parser.get_number ();
197f0a60
TT
275 bookmark_1 (bnum);
276 }
277 }
6b04bdb7
MS
278}
279
5fe70629 280INIT_GDB_FILE (reverse)
b2175913 281{
3947f654
SM
282 cmd_list_element *reverse_step_cmd
283 = add_com ("reverse-step", class_run, reverse_step, _("\
b2175913 284Step program backward until it reaches the beginning of another source line.\n\
3947f654
SM
285Argument N means do this N times (or till program stops for another reason)."));
286 add_com_alias ("rs", reverse_step_cmd, class_run, 1);
b2175913 287
3947f654
SM
288 cmd_list_element *reverse_next_cmd
289 = add_com ("reverse-next", class_run, reverse_next, _("\
b2175913
MS
290Step program backward, proceeding through subroutine calls.\n\
291Like the \"reverse-step\" command as long as subroutine calls do not happen;\n\
292when they do, the call is treated as one instruction.\n\
3947f654
SM
293Argument N means do this N times (or till program stops for another reason)."));
294 add_com_alias ("rn", reverse_next_cmd, class_run, 1);
b2175913 295
3947f654
SM
296 cmd_list_element *reverse_stepi_cmd
297 = add_com ("reverse-stepi", class_run, reverse_stepi, _("\
b2175913 298Step backward exactly one instruction.\n\
3947f654
SM
299Argument N means do this N times (or till program stops for another reason)."));
300 add_com_alias ("rsi", reverse_stepi_cmd, class_run, 0);
b2175913 301
3947f654
SM
302 cmd_list_element *reverse_nexti_cmd
303 = add_com ("reverse-nexti", class_run, reverse_nexti, _("\
b2175913 304Step backward one instruction, but proceed through called subroutines.\n\
3947f654
SM
305Argument N means do this N times (or till program stops for another reason)."));
306 add_com_alias ("rni", reverse_nexti_cmd, class_run, 0);
b2175913 307
3947f654
SM
308 cmd_list_element *reverse_continue_cmd
309 = add_com ("reverse-continue", class_run, reverse_continue, _("\
b2175913
MS
310Continue program being debugged but run it in reverse.\n\
311If proceeding from breakpoint, a number N may be used as an argument,\n\
312which means to set the ignore count of that breakpoint to N - 1 (so that\n\
313the breakpoint won't break until the Nth time it is reached)."));
3947f654 314 add_com_alias ("rc", reverse_continue_cmd, class_run, 0);
b2175913
MS
315
316 add_com ("reverse-finish", class_run, reverse_finish, _("\
317Execute backward until just before selected stack frame is called."));
6b04bdb7
MS
318
319 add_com ("bookmark", class_bookmark, save_bookmark_command, _("\
320Set a bookmark in the program's execution history.\n\
be2a6a58 321A bookmark represents a point in the execution history\n\
6b04bdb7 322that can be returned to at a later point in the debug session."));
11db9430 323 add_info ("bookmarks", info_bookmarks_command, _("\
6b04bdb7 324Status of user-settable bookmarks.\n\
be2a6a58
TT
325Bookmarks are user-settable markers representing a point in the\n\
326execution history that can be returned to later in the same debug\n\
6b04bdb7
MS
327session."));
328 add_cmd ("bookmark", class_bookmark, delete_bookmark_command, _("\
329Delete a bookmark from the bookmark list.\n\
7d357efd 330Argument is a bookmark number or numbers,\n\
89549d7f 331 or no argument to delete all bookmarks."),
6b04bdb7
MS
332 &deletelist);
333 add_com ("goto-bookmark", class_bookmark, goto_bookmark_command, _("\
334Go to an earlier-bookmarked point in the program's execution history.\n\
be2a6a58 335Argument is the bookmark number of a bookmark saved earlier by using\n\
6b04bdb7
MS
336the 'bookmark' command, or the special arguments:\n\
337 start (beginning of recording)\n\
89549d7f 338 end (end of recording)"));
b2175913 339}