]>
Commit | Line | Data |
---|---|---|
69d05d38 HZ |
1 | /* Process record and replay target for GDB, the GNU debugger. |
2 | ||
28e7fd62 | 3 | Copyright (C) 2008-2013 Free Software Foundation, Inc. |
69d05d38 HZ |
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 | |
9 | the Free Software Foundation; either version 3 of the License, or | |
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 | |
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
19 | ||
20 | #include "defs.h" | |
21 | #include "gdbcmd.h" | |
0156b218 | 22 | #include "completer.h" |
69d05d38 | 23 | #include "record.h" |
82a90ccf | 24 | #include "observer.h" |
d02ed0bb MM |
25 | #include "inferior.h" |
26 | #include "common/common-utils.h" | |
69d05d38 | 27 | |
d02ed0bb MM |
28 | /* This is the debug switch for process record. */ |
29 | unsigned int record_debug = 0; | |
27699eea | 30 | |
d02ed0bb MM |
31 | struct cmd_list_element *record_cmdlist = NULL; |
32 | struct cmd_list_element *set_record_cmdlist = NULL; | |
33 | struct cmd_list_element *show_record_cmdlist = NULL; | |
34 | struct cmd_list_element *info_record_cmdlist = NULL; | |
27699eea | 35 | |
d02ed0bb | 36 | /* Find the record target in the target stack. */ |
27699eea | 37 | |
d02ed0bb MM |
38 | static struct target_ops * |
39 | find_record_target (void) | |
27699eea | 40 | { |
d02ed0bb | 41 | struct target_ops *t; |
123f5f96 | 42 | |
d02ed0bb MM |
43 | for (t = current_target.beneath; t != NULL; t = t->beneath) |
44 | if (t->to_stratum == record_stratum) | |
45 | return t; | |
27699eea | 46 | |
d02ed0bb | 47 | return NULL; |
27699eea MS |
48 | } |
49 | ||
d02ed0bb | 50 | /* Check that recording is active. Throw an error, if it isn't. */ |
27699eea | 51 | |
d02ed0bb MM |
52 | static struct target_ops * |
53 | require_record_target (void) | |
27699eea | 54 | { |
d02ed0bb | 55 | struct target_ops *t; |
27699eea | 56 | |
d02ed0bb MM |
57 | t = find_record_target (); |
58 | if (t == NULL) | |
59 | error (_("No record target is currently active.\n" | |
60 | "Use one of the \"target record-<tab><tab>\" commands first.")); | |
27699eea | 61 | |
d02ed0bb | 62 | return t; |
27699eea MS |
63 | } |
64 | ||
d02ed0bb | 65 | /* See record.h. */ |
27699eea | 66 | |
d02ed0bb MM |
67 | int |
68 | record_read_memory (struct gdbarch *gdbarch, | |
69 | CORE_ADDR memaddr, gdb_byte *myaddr, | |
70 | ssize_t len) | |
27699eea | 71 | { |
d02ed0bb | 72 | int ret = target_read_memory (memaddr, myaddr, len); |
27699eea | 73 | |
d02ed0bb MM |
74 | if (ret && record_debug) |
75 | printf_unfiltered (_("Process record: error reading memory " | |
76 | "at addr %s len = %ld.\n"), | |
77 | paddress (gdbarch, memaddr), (long) len); | |
78 | return ret; | |
27699eea MS |
79 | } |
80 | ||
6df67667 MS |
81 | /* Implement "show record debug" command. */ |
82 | ||
69d05d38 HZ |
83 | static void |
84 | show_record_debug (struct ui_file *file, int from_tty, | |
85 | struct cmd_list_element *c, const char *value) | |
86 | { | |
87 | fprintf_filtered (file, _("Debugging of process record target is %s.\n"), | |
88 | value); | |
89 | } | |
90 | ||
91 | /* Alias for "target record". */ | |
92 | ||
93 | static void | |
94 | cmd_record_start (char *args, int from_tty) | |
95 | { | |
d02ed0bb | 96 | execute_command ("target record-full", from_tty); |
69d05d38 HZ |
97 | } |
98 | ||
99 | /* Truncate the record log from the present point | |
100 | of replay until the end. */ | |
101 | ||
102 | static void | |
103 | cmd_record_delete (char *args, int from_tty) | |
104 | { | |
d02ed0bb MM |
105 | require_record_target (); |
106 | ||
107 | if (!target_record_is_replaying ()) | |
69d05d38 | 108 | { |
d02ed0bb MM |
109 | printf_unfiltered (_("Already at end of record list.\n")); |
110 | return; | |
111 | } | |
69d05d38 | 112 | |
d02ed0bb MM |
113 | if (!target_supports_delete_record ()) |
114 | { | |
115 | printf_unfiltered (_("The current record target does not support " | |
116 | "this operation.\n")); | |
117 | return; | |
69d05d38 | 118 | } |
d02ed0bb MM |
119 | |
120 | if (!from_tty || query (_("Delete the log from this point forward " | |
121 | "and begin to record the running message " | |
122 | "at current PC?"))) | |
123 | target_delete_record (); | |
69d05d38 HZ |
124 | } |
125 | ||
6df67667 | 126 | /* Implement the "stoprecord" or "record stop" command. */ |
69d05d38 HZ |
127 | |
128 | static void | |
129 | cmd_record_stop (char *args, int from_tty) | |
130 | { | |
d02ed0bb | 131 | struct target_ops *t; |
82a90ccf | 132 | |
d02ed0bb MM |
133 | t = require_record_target (); |
134 | unpush_target (t); | |
69d05d38 | 135 | |
d02ed0bb MM |
136 | printf_unfiltered (_("Process record is stopped and all execution " |
137 | "logs are deleted.\n")); | |
69d05d38 | 138 | |
d02ed0bb | 139 | observer_notify_record_changed (current_inferior (), 0); |
69d05d38 HZ |
140 | } |
141 | ||
d02ed0bb | 142 | /* The "set record" command. */ |
69d05d38 HZ |
143 | |
144 | static void | |
145 | set_record_command (char *args, int from_tty) | |
146 | { | |
3e43a32a MS |
147 | printf_unfiltered (_("\"set record\" must be followed " |
148 | "by an apporpriate subcommand.\n")); | |
69d05d38 HZ |
149 | help_list (set_record_cmdlist, "set record ", all_commands, gdb_stdout); |
150 | } | |
151 | ||
d02ed0bb MM |
152 | /* The "show record" command. */ |
153 | ||
69d05d38 HZ |
154 | static void |
155 | show_record_command (char *args, int from_tty) | |
156 | { | |
157 | cmd_show_list (show_record_cmdlist, from_tty, ""); | |
158 | } | |
159 | ||
d02ed0bb | 160 | /* The "info record" command. */ |
b54295a7 | 161 | |
69d05d38 HZ |
162 | static void |
163 | info_record_command (char *args, int from_tty) | |
164 | { | |
d02ed0bb | 165 | struct target_ops *t; |
0156b218 | 166 | |
d02ed0bb MM |
167 | t = find_record_target (); |
168 | if (t == NULL) | |
0156b218 | 169 | { |
d02ed0bb MM |
170 | printf_filtered (_("No record target is currently active.\n")); |
171 | return; | |
0156b218 MS |
172 | } |
173 | ||
d02ed0bb MM |
174 | printf_filtered (_("Active record target: %s\n"), t->to_shortname); |
175 | if (t->to_info_record != NULL) | |
176 | t->to_info_record (); | |
0156b218 MS |
177 | } |
178 | ||
d02ed0bb | 179 | /* The "record save" command. */ |
0156b218 MS |
180 | |
181 | static void | |
182 | cmd_record_save (char *args, int from_tty) | |
183 | { | |
184 | char *recfilename, recfilename_buffer[40]; | |
0156b218 | 185 | |
d02ed0bb | 186 | require_record_target (); |
0156b218 | 187 | |
d02ed0bb | 188 | if (args != NULL && *args != 0) |
0156b218 MS |
189 | recfilename = args; |
190 | else | |
191 | { | |
192 | /* Default recfile name is "gdb_record.PID". */ | |
d02ed0bb | 193 | xsnprintf (recfilename_buffer, sizeof (recfilename_buffer), |
0156b218 MS |
194 | "gdb_record.%d", PIDGET (inferior_ptid)); |
195 | recfilename = recfilename_buffer; | |
196 | } | |
197 | ||
d02ed0bb | 198 | target_save_record (recfilename); |
6b04bdb7 MS |
199 | } |
200 | ||
201 | /* "record goto" command. Argument is an instruction number, | |
202 | as given by "info record". | |
203 | ||
204 | Rewinds the recording (forward or backward) to the given instruction. */ | |
205 | ||
d02ed0bb | 206 | void |
6b04bdb7 MS |
207 | cmd_record_goto (char *arg, int from_tty) |
208 | { | |
d02ed0bb | 209 | require_record_target (); |
6b04bdb7 MS |
210 | |
211 | if (arg == NULL || *arg == '\0') | |
212 | error (_("Command requires an argument (insn number to go to).")); | |
213 | ||
214 | if (strncmp (arg, "start", strlen ("start")) == 0 | |
215 | || strncmp (arg, "begin", strlen ("begin")) == 0) | |
d02ed0bb | 216 | target_goto_record_begin (); |
6b04bdb7 | 217 | else if (strncmp (arg, "end", strlen ("end")) == 0) |
d02ed0bb | 218 | target_goto_record_end (); |
6b04bdb7 MS |
219 | else |
220 | { | |
d02ed0bb | 221 | ULONGEST insn; |
6b04bdb7 | 222 | |
d02ed0bb MM |
223 | insn = parse_and_eval_long (arg); |
224 | target_goto_record (insn); | |
6b04bdb7 | 225 | } |
6b04bdb7 MS |
226 | } |
227 | ||
70221824 PA |
228 | /* Provide a prototype to silence -Wmissing-prototypes. */ |
229 | extern initialize_file_ftype _initialize_record; | |
230 | ||
69d05d38 HZ |
231 | void |
232 | _initialize_record (void) | |
233 | { | |
0156b218 MS |
234 | struct cmd_list_element *c; |
235 | ||
ccce17b0 YQ |
236 | add_setshow_zuinteger_cmd ("record", no_class, &record_debug, |
237 | _("Set debugging of record/replay feature."), | |
238 | _("Show debugging of record/replay feature."), | |
239 | _("When enabled, debugging output for " | |
240 | "record/replay feature is displayed."), | |
241 | NULL, show_record_debug, &setdebuglist, | |
242 | &showdebuglist); | |
69d05d38 | 243 | |
0156b218 | 244 | c = add_prefix_cmd ("record", class_obscure, cmd_record_start, |
d02ed0bb | 245 | _("Start recording."), |
0156b218 MS |
246 | &record_cmdlist, "record ", 0, &cmdlist); |
247 | set_cmd_completer (c, filename_completer); | |
248 | ||
69d05d38 HZ |
249 | add_com_alias ("rec", "record", class_obscure, 1); |
250 | add_prefix_cmd ("record", class_support, set_record_command, | |
251 | _("Set record options"), &set_record_cmdlist, | |
252 | "set record ", 0, &setlist); | |
253 | add_alias_cmd ("rec", "record", class_obscure, 1, &setlist); | |
254 | add_prefix_cmd ("record", class_support, show_record_command, | |
255 | _("Show record options"), &show_record_cmdlist, | |
256 | "show record ", 0, &showlist); | |
257 | add_alias_cmd ("rec", "record", class_obscure, 1, &showlist); | |
258 | add_prefix_cmd ("record", class_support, info_record_command, | |
259 | _("Info record options"), &info_record_cmdlist, | |
260 | "info record ", 0, &infolist); | |
261 | add_alias_cmd ("rec", "record", class_obscure, 1, &infolist); | |
262 | ||
0156b218 MS |
263 | c = add_cmd ("save", class_obscure, cmd_record_save, |
264 | _("Save the execution log to a file.\n\ | |
265 | Argument is optional filename.\n\ | |
266 | Default filename is 'gdb_record.<process_id>'."), | |
267 | &record_cmdlist); | |
268 | set_cmd_completer (c, filename_completer); | |
269 | ||
69d05d38 HZ |
270 | add_cmd ("delete", class_obscure, cmd_record_delete, |
271 | _("Delete the rest of execution log and start recording it anew."), | |
272 | &record_cmdlist); | |
273 | add_alias_cmd ("d", "delete", class_obscure, 1, &record_cmdlist); | |
274 | add_alias_cmd ("del", "delete", class_obscure, 1, &record_cmdlist); | |
275 | ||
276 | add_cmd ("stop", class_obscure, cmd_record_stop, | |
277 | _("Stop the record/replay target."), | |
278 | &record_cmdlist); | |
279 | add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist); | |
280 | ||
6b04bdb7 MS |
281 | add_cmd ("goto", class_obscure, cmd_record_goto, _("\ |
282 | Restore the program to its state at instruction number N.\n\ | |
283 | Argument is instruction number, as shown by 'info record'."), | |
284 | &record_cmdlist); | |
69d05d38 | 285 | } |