Commit | Line | Data |
---|---|---|
69d05d38 HZ |
1 | /* Process record and replay target for GDB, the GNU debugger. |
2 | ||
3 | Copyright (C) 2008, 2009 Free Software Foundation, Inc. | |
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" | |
22 | #include "regcache.h" | |
23 | #include "gdbthread.h" | |
24 | #include "event-top.h" | |
25 | #include "exceptions.h" | |
27699eea MS |
26 | #include "gdbcore.h" |
27 | #include "exec.h" | |
69d05d38 HZ |
28 | #include "record.h" |
29 | ||
30 | #include <signal.h> | |
31 | ||
6df67667 MS |
32 | /* This module implements "target record", also known as "process |
33 | record and replay". This target sits on top of a "normal" target | |
34 | (a target that "has execution"), and provides a record and replay | |
35 | functionality, including reverse debugging. | |
36 | ||
37 | Target record has two modes: recording, and replaying. | |
38 | ||
39 | In record mode, we intercept the to_resume and to_wait methods. | |
40 | Whenever gdb resumes the target, we run the target in single step | |
41 | mode, and we build up an execution log in which, for each executed | |
42 | instruction, we record all changes in memory and register state. | |
43 | This is invisible to the user, to whom it just looks like an | |
44 | ordinary debugging session (except for performance degredation). | |
45 | ||
46 | In replay mode, instead of actually letting the inferior run as a | |
47 | process, we simulate its execution by playing back the recorded | |
48 | execution log. For each instruction in the log, we simulate the | |
49 | instruction's side effects by duplicating the changes that it would | |
50 | have made on memory and registers. */ | |
51 | ||
69d05d38 HZ |
52 | #define DEFAULT_RECORD_INSN_MAX_NUM 200000 |
53 | ||
54 | #define RECORD_IS_REPLAY \ | |
55 | (record_list->next || execution_direction == EXEC_REVERSE) | |
56 | ||
fda458ee | 57 | /* These are the core structs of the process record functionality. |
69d05d38 | 58 | |
fda458ee | 59 | A record_entry is a record of the value change of a register |
69d05d38 | 60 | ("record_reg") or a part of memory ("record_mem"). And each |
fda458ee MS |
61 | instruction must have a struct record_entry ("record_end") that |
62 | indicates that this is the last struct record_entry of this | |
63 | instruction. | |
69d05d38 | 64 | |
fda458ee MS |
65 | Each struct record_entry is linked to "record_list" by "prev" and |
66 | "next" pointers. */ | |
69d05d38 | 67 | |
69d05d38 HZ |
68 | struct record_mem_entry |
69 | { | |
70 | CORE_ADDR addr; | |
71 | int len; | |
afd0cd3f MS |
72 | /* Set this flag if target memory for this entry |
73 | can no longer be accessed. */ | |
74 | int mem_entry_not_accessible; | |
44389f9b MS |
75 | union |
76 | { | |
77 | gdb_byte *ptr; | |
78 | gdb_byte buf[sizeof (gdb_byte *)]; | |
79 | } u; | |
80 | }; | |
81 | ||
82 | struct record_reg_entry | |
83 | { | |
84 | unsigned short num; | |
85 | unsigned short len; | |
86 | union | |
87 | { | |
88 | gdb_byte *ptr; | |
89 | gdb_byte buf[2 * sizeof (gdb_byte *)]; | |
90 | } u; | |
69d05d38 HZ |
91 | }; |
92 | ||
8b739a96 HZ |
93 | struct record_end_entry |
94 | { | |
95 | enum target_signal sigval; | |
b54295a7 | 96 | ULONGEST insn_num; |
8b739a96 HZ |
97 | }; |
98 | ||
69d05d38 HZ |
99 | enum record_type |
100 | { | |
101 | record_end = 0, | |
102 | record_reg, | |
103 | record_mem | |
104 | }; | |
105 | ||
6df67667 MS |
106 | /* This is the data structure that makes up the execution log. |
107 | ||
108 | The execution log consists of a single linked list of entries | |
109 | of type "struct record_entry". It is doubly linked so that it | |
110 | can be traversed in either direction. | |
111 | ||
112 | The start of the list is anchored by a struct called | |
113 | "record_first". The pointer "record_list" either points to the | |
114 | last entry that was added to the list (in record mode), or to the | |
115 | next entry in the list that will be executed (in replay mode). | |
116 | ||
117 | Each list element (struct record_entry), in addition to next and | |
118 | prev pointers, consists of a union of three entry types: mem, reg, | |
119 | and end. A field called "type" determines which entry type is | |
120 | represented by a given list element. | |
121 | ||
122 | Each instruction that is added to the execution log is represented | |
123 | by a variable number of list elements ('entries'). The instruction | |
124 | will have one "reg" entry for each register that is changed by | |
125 | executing the instruction (including the PC in every case). It | |
126 | will also have one "mem" entry for each memory change. Finally, | |
127 | each instruction will have an "end" entry that separates it from | |
128 | the changes associated with the next instruction. */ | |
129 | ||
69d05d38 HZ |
130 | struct record_entry |
131 | { | |
132 | struct record_entry *prev; | |
133 | struct record_entry *next; | |
134 | enum record_type type; | |
135 | union | |
136 | { | |
137 | /* reg */ | |
138 | struct record_reg_entry reg; | |
139 | /* mem */ | |
140 | struct record_mem_entry mem; | |
8b739a96 HZ |
141 | /* end */ |
142 | struct record_end_entry end; | |
69d05d38 HZ |
143 | } u; |
144 | }; | |
145 | ||
146 | /* This is the debug switch for process record. */ | |
147 | int record_debug = 0; | |
148 | ||
27699eea MS |
149 | struct record_core_buf_entry |
150 | { | |
151 | struct record_core_buf_entry *prev; | |
152 | struct target_section *p; | |
153 | bfd_byte *buf; | |
154 | }; | |
155 | ||
156 | /* Record buf with core target. */ | |
157 | static gdb_byte *record_core_regbuf = NULL; | |
158 | static struct target_section *record_core_start; | |
159 | static struct target_section *record_core_end; | |
160 | static struct record_core_buf_entry *record_core_buf_list = NULL; | |
161 | ||
6df67667 MS |
162 | /* The following variables are used for managing the linked list that |
163 | represents the execution log. | |
164 | ||
165 | record_first is the anchor that holds down the beginning of the list. | |
166 | ||
167 | record_list serves two functions: | |
168 | 1) In record mode, it anchors the end of the list. | |
169 | 2) In replay mode, it traverses the list and points to | |
170 | the next instruction that must be emulated. | |
171 | ||
172 | record_arch_list_head and record_arch_list_tail are used to manage | |
173 | a separate list, which is used to build up the change elements of | |
174 | the currently executing instruction during record mode. When this | |
175 | instruction has been completely annotated in the "arch list", it | |
176 | will be appended to the main execution log. */ | |
177 | ||
69d05d38 HZ |
178 | static struct record_entry record_first; |
179 | static struct record_entry *record_list = &record_first; | |
180 | static struct record_entry *record_arch_list_head = NULL; | |
181 | static struct record_entry *record_arch_list_tail = NULL; | |
182 | ||
183 | /* 1 ask user. 0 auto delete the last struct record_entry. */ | |
184 | static int record_stop_at_limit = 1; | |
b54295a7 | 185 | /* Maximum allowed number of insns in execution log. */ |
191e1813 | 186 | static unsigned int record_insn_max_num = DEFAULT_RECORD_INSN_MAX_NUM; |
b54295a7 | 187 | /* Actual count of insns presently in execution log. */ |
69d05d38 | 188 | static int record_insn_num = 0; |
b54295a7 MS |
189 | /* Count of insns logged so far (may be larger |
190 | than count of insns presently in execution log). */ | |
191 | static ULONGEST record_insn_count; | |
69d05d38 HZ |
192 | |
193 | /* The target_ops of process record. */ | |
194 | static struct target_ops record_ops; | |
27699eea | 195 | static struct target_ops record_core_ops; |
69d05d38 HZ |
196 | |
197 | /* The beneath function pointers. */ | |
198 | static struct target_ops *record_beneath_to_resume_ops; | |
199 | static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, int, | |
200 | enum target_signal); | |
201 | static struct target_ops *record_beneath_to_wait_ops; | |
202 | static ptid_t (*record_beneath_to_wait) (struct target_ops *, ptid_t, | |
47608cb1 PA |
203 | struct target_waitstatus *, |
204 | int); | |
69d05d38 HZ |
205 | static struct target_ops *record_beneath_to_store_registers_ops; |
206 | static void (*record_beneath_to_store_registers) (struct target_ops *, | |
207 | struct regcache *, | |
208 | int regno); | |
209 | static struct target_ops *record_beneath_to_xfer_partial_ops; | |
210 | static LONGEST (*record_beneath_to_xfer_partial) (struct target_ops *ops, | |
211 | enum target_object object, | |
212 | const char *annex, | |
213 | gdb_byte *readbuf, | |
214 | const gdb_byte *writebuf, | |
215 | ULONGEST offset, | |
216 | LONGEST len); | |
a6d9a66e UW |
217 | static int (*record_beneath_to_insert_breakpoint) (struct gdbarch *, |
218 | struct bp_target_info *); | |
219 | static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *, | |
220 | struct bp_target_info *); | |
69d05d38 | 221 | |
61f75dd8 MS |
222 | /* Alloc and free functions for record_reg, record_mem, and record_end |
223 | entries. */ | |
224 | ||
225 | /* Alloc a record_reg record entry. */ | |
226 | ||
227 | static inline struct record_entry * | |
228 | record_reg_alloc (struct regcache *regcache, int regnum) | |
229 | { | |
230 | struct record_entry *rec; | |
231 | struct gdbarch *gdbarch = get_regcache_arch (regcache); | |
232 | ||
233 | rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry)); | |
234 | rec->type = record_reg; | |
235 | rec->u.reg.num = regnum; | |
44389f9b MS |
236 | rec->u.reg.len = register_size (gdbarch, regnum); |
237 | if (rec->u.reg.len > sizeof (rec->u.reg.u.buf)) | |
238 | rec->u.reg.u.ptr = (gdb_byte *) xmalloc (rec->u.reg.len); | |
61f75dd8 MS |
239 | |
240 | return rec; | |
241 | } | |
242 | ||
243 | /* Free a record_reg record entry. */ | |
244 | ||
245 | static inline void | |
246 | record_reg_release (struct record_entry *rec) | |
247 | { | |
248 | gdb_assert (rec->type == record_reg); | |
44389f9b MS |
249 | if (rec->u.reg.len > sizeof (rec->u.reg.u.buf)) |
250 | xfree (rec->u.reg.u.ptr); | |
61f75dd8 MS |
251 | xfree (rec); |
252 | } | |
253 | ||
254 | /* Alloc a record_mem record entry. */ | |
255 | ||
256 | static inline struct record_entry * | |
257 | record_mem_alloc (CORE_ADDR addr, int len) | |
258 | { | |
259 | struct record_entry *rec; | |
260 | ||
261 | rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry)); | |
262 | rec->type = record_mem; | |
263 | rec->u.mem.addr = addr; | |
264 | rec->u.mem.len = len; | |
44389f9b MS |
265 | if (rec->u.mem.len > sizeof (rec->u.mem.u.buf)) |
266 | rec->u.mem.u.ptr = (gdb_byte *) xmalloc (len); | |
61f75dd8 MS |
267 | |
268 | return rec; | |
269 | } | |
270 | ||
271 | /* Free a record_mem record entry. */ | |
272 | ||
273 | static inline void | |
274 | record_mem_release (struct record_entry *rec) | |
275 | { | |
276 | gdb_assert (rec->type == record_mem); | |
44389f9b MS |
277 | if (rec->u.mem.len > sizeof (rec->u.mem.u.buf)) |
278 | xfree (rec->u.mem.u.ptr); | |
61f75dd8 MS |
279 | xfree (rec); |
280 | } | |
281 | ||
282 | /* Alloc a record_end record entry. */ | |
283 | ||
284 | static inline struct record_entry * | |
285 | record_end_alloc (void) | |
286 | { | |
287 | struct record_entry *rec; | |
288 | ||
289 | rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry)); | |
290 | rec->type = record_end; | |
291 | ||
292 | return rec; | |
293 | } | |
294 | ||
295 | /* Free a record_end record entry. */ | |
296 | ||
297 | static inline void | |
298 | record_end_release (struct record_entry *rec) | |
299 | { | |
300 | xfree (rec); | |
301 | } | |
302 | ||
303 | /* Free one record entry, any type. | |
304 | Return entry->type, in case caller wants to know. */ | |
305 | ||
306 | static inline enum record_type | |
307 | record_entry_release (struct record_entry *rec) | |
308 | { | |
309 | enum record_type type = rec->type; | |
310 | ||
311 | switch (type) { | |
312 | case record_reg: | |
313 | record_reg_release (rec); | |
314 | break; | |
315 | case record_mem: | |
316 | record_mem_release (rec); | |
317 | break; | |
318 | case record_end: | |
319 | record_end_release (rec); | |
320 | break; | |
321 | } | |
322 | return type; | |
323 | } | |
324 | ||
325 | /* Free all record entries in list pointed to by REC. */ | |
326 | ||
69d05d38 HZ |
327 | static void |
328 | record_list_release (struct record_entry *rec) | |
329 | { | |
69d05d38 HZ |
330 | if (!rec) |
331 | return; | |
332 | ||
333 | while (rec->next) | |
61f75dd8 | 334 | rec = rec->next; |
69d05d38 HZ |
335 | |
336 | while (rec->prev) | |
337 | { | |
69d05d38 | 338 | rec = rec->prev; |
61f75dd8 | 339 | record_entry_release (rec->next); |
69d05d38 HZ |
340 | } |
341 | ||
61f75dd8 MS |
342 | if (rec == &record_first) |
343 | { | |
344 | record_insn_num = 0; | |
345 | record_first.next = NULL; | |
346 | } | |
347 | else | |
348 | record_entry_release (rec); | |
69d05d38 HZ |
349 | } |
350 | ||
61f75dd8 MS |
351 | /* Free all record entries forward of the given list position. */ |
352 | ||
69d05d38 | 353 | static void |
61f75dd8 | 354 | record_list_release_following (struct record_entry *rec) |
69d05d38 | 355 | { |
69d05d38 | 356 | struct record_entry *tmp = rec->next; |
61f75dd8 | 357 | |
69d05d38 HZ |
358 | rec->next = NULL; |
359 | while (tmp) | |
360 | { | |
361 | rec = tmp->next; | |
61f75dd8 | 362 | if (record_entry_release (tmp) == record_end) |
b54295a7 MS |
363 | { |
364 | record_insn_num--; | |
365 | record_insn_count--; | |
366 | } | |
69d05d38 HZ |
367 | tmp = rec; |
368 | } | |
369 | } | |
370 | ||
265aad34 MS |
371 | /* Delete the first instruction from the beginning of the log, to make |
372 | room for adding a new instruction at the end of the log. | |
373 | ||
374 | Note -- this function does not modify record_insn_num. */ | |
375 | ||
69d05d38 HZ |
376 | static void |
377 | record_list_release_first (void) | |
378 | { | |
61f75dd8 | 379 | struct record_entry *tmp; |
69d05d38 HZ |
380 | |
381 | if (!record_first.next) | |
382 | return; | |
383 | ||
61f75dd8 | 384 | /* Loop until a record_end. */ |
69d05d38 HZ |
385 | while (1) |
386 | { | |
61f75dd8 | 387 | /* Cut record_first.next out of the linked list. */ |
69d05d38 HZ |
388 | tmp = record_first.next; |
389 | record_first.next = tmp->next; | |
61f75dd8 MS |
390 | tmp->next->prev = &record_first; |
391 | ||
392 | /* tmp is now isolated, and can be deleted. */ | |
393 | if (record_entry_release (tmp) == record_end) | |
b54295a7 | 394 | break; /* End loop at first record_end. */ |
69d05d38 HZ |
395 | |
396 | if (!record_first.next) | |
397 | { | |
398 | gdb_assert (record_insn_num == 1); | |
61f75dd8 | 399 | break; /* End loop when list is empty. */ |
69d05d38 | 400 | } |
69d05d38 | 401 | } |
69d05d38 HZ |
402 | } |
403 | ||
404 | /* Add a struct record_entry to record_arch_list. */ | |
405 | ||
406 | static void | |
407 | record_arch_list_add (struct record_entry *rec) | |
408 | { | |
409 | if (record_debug > 1) | |
410 | fprintf_unfiltered (gdb_stdlog, | |
411 | "Process record: record_arch_list_add %s.\n", | |
412 | host_address_to_string (rec)); | |
413 | ||
414 | if (record_arch_list_tail) | |
415 | { | |
416 | record_arch_list_tail->next = rec; | |
417 | rec->prev = record_arch_list_tail; | |
418 | record_arch_list_tail = rec; | |
419 | } | |
420 | else | |
421 | { | |
422 | record_arch_list_head = rec; | |
423 | record_arch_list_tail = rec; | |
424 | } | |
425 | } | |
426 | ||
44389f9b MS |
427 | /* Return the value storage location of a record entry. */ |
428 | static inline gdb_byte * | |
429 | record_get_loc (struct record_entry *rec) | |
430 | { | |
431 | switch (rec->type) { | |
432 | case record_mem: | |
433 | if (rec->u.mem.len > sizeof (rec->u.mem.u.buf)) | |
434 | return rec->u.mem.u.ptr; | |
435 | else | |
436 | return rec->u.mem.u.buf; | |
437 | case record_reg: | |
438 | if (rec->u.reg.len > sizeof (rec->u.reg.u.buf)) | |
439 | return rec->u.reg.u.ptr; | |
440 | else | |
441 | return rec->u.reg.u.buf; | |
442 | case record_end: | |
443 | default: | |
444 | gdb_assert (0); | |
445 | return NULL; | |
446 | } | |
447 | } | |
448 | ||
449 | /* Record the value of a register NUM to record_arch_list. */ | |
69d05d38 HZ |
450 | |
451 | int | |
61f75dd8 | 452 | record_arch_list_add_reg (struct regcache *regcache, int regnum) |
69d05d38 HZ |
453 | { |
454 | struct record_entry *rec; | |
455 | ||
456 | if (record_debug > 1) | |
457 | fprintf_unfiltered (gdb_stdlog, | |
458 | "Process record: add register num = %d to " | |
459 | "record list.\n", | |
61f75dd8 | 460 | regnum); |
69d05d38 | 461 | |
61f75dd8 | 462 | rec = record_reg_alloc (regcache, regnum); |
69d05d38 | 463 | |
44389f9b | 464 | regcache_raw_read (regcache, regnum, record_get_loc (rec)); |
69d05d38 HZ |
465 | |
466 | record_arch_list_add (rec); | |
467 | ||
468 | return 0; | |
469 | } | |
470 | ||
471 | /* Record the value of a region of memory whose address is ADDR and | |
472 | length is LEN to record_arch_list. */ | |
473 | ||
474 | int | |
475 | record_arch_list_add_mem (CORE_ADDR addr, int len) | |
476 | { | |
477 | struct record_entry *rec; | |
478 | ||
479 | if (record_debug > 1) | |
480 | fprintf_unfiltered (gdb_stdlog, | |
5af949e3 | 481 | "Process record: add mem addr = %s len = %d to " |
69d05d38 | 482 | "record list.\n", |
5af949e3 | 483 | paddress (target_gdbarch, addr), len); |
69d05d38 | 484 | |
61f75dd8 | 485 | if (!addr) /* FIXME: Why? Some arch must permit it... */ |
69d05d38 HZ |
486 | return 0; |
487 | ||
61f75dd8 | 488 | rec = record_mem_alloc (addr, len); |
69d05d38 | 489 | |
44389f9b | 490 | if (target_read_memory (addr, record_get_loc (rec), len)) |
69d05d38 HZ |
491 | { |
492 | if (record_debug) | |
493 | fprintf_unfiltered (gdb_stdlog, | |
494 | "Process record: error reading memory at " | |
5af949e3 UW |
495 | "addr = %s len = %d.\n", |
496 | paddress (target_gdbarch, addr), len); | |
61f75dd8 | 497 | record_mem_release (rec); |
69d05d38 HZ |
498 | return -1; |
499 | } | |
500 | ||
501 | record_arch_list_add (rec); | |
502 | ||
503 | return 0; | |
504 | } | |
505 | ||
506 | /* Add a record_end type struct record_entry to record_arch_list. */ | |
507 | ||
508 | int | |
509 | record_arch_list_add_end (void) | |
510 | { | |
511 | struct record_entry *rec; | |
512 | ||
513 | if (record_debug > 1) | |
514 | fprintf_unfiltered (gdb_stdlog, | |
515 | "Process record: add end to arch list.\n"); | |
516 | ||
61f75dd8 | 517 | rec = record_end_alloc (); |
8b739a96 | 518 | rec->u.end.sigval = TARGET_SIGNAL_0; |
b54295a7 | 519 | rec->u.end.insn_num = ++record_insn_count; |
69d05d38 HZ |
520 | |
521 | record_arch_list_add (rec); | |
522 | ||
523 | return 0; | |
524 | } | |
525 | ||
526 | static void | |
527 | record_check_insn_num (int set_terminal) | |
528 | { | |
529 | if (record_insn_max_num) | |
530 | { | |
531 | gdb_assert (record_insn_num <= record_insn_max_num); | |
532 | if (record_insn_num == record_insn_max_num) | |
533 | { | |
534 | /* Ask user what to do. */ | |
535 | if (record_stop_at_limit) | |
536 | { | |
537 | int q; | |
538 | if (set_terminal) | |
539 | target_terminal_ours (); | |
540 | q = yquery (_("Do you want to auto delete previous execution " | |
541 | "log entries when record/replay buffer becomes " | |
542 | "full (record stop-at-limit)?")); | |
543 | if (set_terminal) | |
544 | target_terminal_inferior (); | |
545 | if (q) | |
546 | record_stop_at_limit = 0; | |
547 | else | |
548 | error (_("Process record: inferior program stopped.")); | |
549 | } | |
550 | } | |
551 | } | |
552 | } | |
553 | ||
554 | /* Before inferior step (when GDB record the running message, inferior | |
555 | only can step), GDB will call this function to record the values to | |
556 | record_list. This function will call gdbarch_process_record to | |
557 | record the running message of inferior and set them to | |
558 | record_arch_list, and add it to record_list. */ | |
559 | ||
560 | static void | |
561 | record_message_cleanups (void *ignore) | |
562 | { | |
563 | record_list_release (record_arch_list_tail); | |
564 | } | |
565 | ||
8b739a96 HZ |
566 | struct record_message_args { |
567 | struct regcache *regcache; | |
568 | enum target_signal signal; | |
569 | }; | |
570 | ||
69d05d38 HZ |
571 | static int |
572 | record_message (void *args) | |
573 | { | |
574 | int ret; | |
8b739a96 HZ |
575 | struct record_message_args *myargs = args; |
576 | struct gdbarch *gdbarch = get_regcache_arch (myargs->regcache); | |
69d05d38 HZ |
577 | struct cleanup *old_cleanups = make_cleanup (record_message_cleanups, 0); |
578 | ||
579 | record_arch_list_head = NULL; | |
580 | record_arch_list_tail = NULL; | |
581 | ||
582 | /* Check record_insn_num. */ | |
583 | record_check_insn_num (1); | |
584 | ||
8b739a96 HZ |
585 | /* If gdb sends a signal value to target_resume, |
586 | save it in the 'end' field of the previous instruction. | |
587 | ||
588 | Maybe process record should record what really happened, | |
589 | rather than what gdb pretends has happened. | |
590 | ||
591 | So if Linux delivered the signal to the child process during | |
592 | the record mode, we will record it and deliver it again in | |
593 | the replay mode. | |
594 | ||
595 | If user says "ignore this signal" during the record mode, then | |
596 | it will be ignored again during the replay mode (no matter if | |
597 | the user says something different, like "deliver this signal" | |
598 | during the replay mode). | |
599 | ||
600 | User should understand that nothing he does during the replay | |
601 | mode will change the behavior of the child. If he tries, | |
602 | then that is a user error. | |
603 | ||
604 | But we should still deliver the signal to gdb during the replay, | |
605 | if we delivered it during the recording. Therefore we should | |
606 | record the signal during record_wait, not record_resume. */ | |
607 | if (record_list != &record_first) /* FIXME better way to check */ | |
608 | { | |
609 | gdb_assert (record_list->type == record_end); | |
610 | record_list->u.end.sigval = myargs->signal; | |
611 | } | |
612 | ||
613 | if (myargs->signal == TARGET_SIGNAL_0 | |
614 | || !gdbarch_process_record_signal_p (gdbarch)) | |
615 | ret = gdbarch_process_record (gdbarch, | |
616 | myargs->regcache, | |
617 | regcache_read_pc (myargs->regcache)); | |
618 | else | |
619 | ret = gdbarch_process_record_signal (gdbarch, | |
620 | myargs->regcache, | |
621 | myargs->signal); | |
622 | ||
69d05d38 HZ |
623 | if (ret > 0) |
624 | error (_("Process record: inferior program stopped.")); | |
625 | if (ret < 0) | |
626 | error (_("Process record: failed to record execution log.")); | |
627 | ||
628 | discard_cleanups (old_cleanups); | |
629 | ||
630 | record_list->next = record_arch_list_head; | |
631 | record_arch_list_head->prev = record_list; | |
632 | record_list = record_arch_list_tail; | |
633 | ||
634 | if (record_insn_num == record_insn_max_num && record_insn_max_num) | |
635 | record_list_release_first (); | |
636 | else | |
637 | record_insn_num++; | |
638 | ||
639 | return 1; | |
640 | } | |
641 | ||
642 | static int | |
8b739a96 HZ |
643 | do_record_message (struct regcache *regcache, |
644 | enum target_signal signal) | |
69d05d38 | 645 | { |
8b739a96 HZ |
646 | struct record_message_args args; |
647 | ||
648 | args.regcache = regcache; | |
649 | args.signal = signal; | |
650 | return catch_errors (record_message, &args, NULL, RETURN_MASK_ALL); | |
69d05d38 HZ |
651 | } |
652 | ||
653 | /* Set to 1 if record_store_registers and record_xfer_partial | |
654 | doesn't need record. */ | |
655 | ||
656 | static int record_gdb_operation_disable = 0; | |
657 | ||
658 | struct cleanup * | |
659 | record_gdb_operation_disable_set (void) | |
660 | { | |
661 | struct cleanup *old_cleanups = NULL; | |
662 | ||
663 | old_cleanups = | |
664 | make_cleanup_restore_integer (&record_gdb_operation_disable); | |
665 | record_gdb_operation_disable = 1; | |
666 | ||
667 | return old_cleanups; | |
668 | } | |
669 | ||
90ca0479 MS |
670 | /* Execute one instruction from the record log. Each instruction in |
671 | the log will be represented by an arbitrary sequence of register | |
672 | entries and memory entries, followed by an 'end' entry. */ | |
673 | ||
674 | static inline void | |
675 | record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch, | |
676 | struct record_entry *entry) | |
677 | { | |
678 | switch (entry->type) | |
679 | { | |
680 | case record_reg: /* reg */ | |
681 | { | |
682 | gdb_byte reg[MAX_REGISTER_SIZE]; | |
683 | ||
684 | if (record_debug > 1) | |
685 | fprintf_unfiltered (gdb_stdlog, | |
686 | "Process record: record_reg %s to " | |
687 | "inferior num = %d.\n", | |
688 | host_address_to_string (entry), | |
689 | entry->u.reg.num); | |
690 | ||
691 | regcache_cooked_read (regcache, entry->u.reg.num, reg); | |
692 | regcache_cooked_write (regcache, entry->u.reg.num, | |
693 | record_get_loc (entry)); | |
694 | memcpy (record_get_loc (entry), reg, entry->u.reg.len); | |
695 | } | |
696 | break; | |
697 | ||
698 | case record_mem: /* mem */ | |
699 | { | |
700 | /* Nothing to do if the entry is flagged not_accessible. */ | |
701 | if (!entry->u.mem.mem_entry_not_accessible) | |
702 | { | |
703 | gdb_byte *mem = alloca (entry->u.mem.len); | |
704 | ||
705 | if (record_debug > 1) | |
706 | fprintf_unfiltered (gdb_stdlog, | |
707 | "Process record: record_mem %s to " | |
708 | "inferior addr = %s len = %d.\n", | |
709 | host_address_to_string (entry), | |
710 | paddress (gdbarch, entry->u.mem.addr), | |
711 | entry->u.mem.len); | |
712 | ||
713 | if (target_read_memory (entry->u.mem.addr, mem, entry->u.mem.len)) | |
714 | { | |
715 | entry->u.mem.mem_entry_not_accessible = 1; | |
716 | if (record_debug) | |
717 | warning (_("Process record: error reading memory at " | |
718 | "addr = %s len = %d."), | |
719 | paddress (gdbarch, entry->u.mem.addr), | |
720 | entry->u.mem.len); | |
721 | } | |
722 | else | |
723 | { | |
724 | if (target_write_memory (entry->u.mem.addr, | |
725 | record_get_loc (entry), | |
726 | entry->u.mem.len)) | |
727 | { | |
728 | entry->u.mem.mem_entry_not_accessible = 1; | |
729 | if (record_debug) | |
730 | warning (_("Process record: error writing memory at " | |
731 | "addr = %s len = %d."), | |
732 | paddress (gdbarch, entry->u.mem.addr), | |
733 | entry->u.mem.len); | |
734 | } | |
735 | else | |
736 | memcpy (record_get_loc (entry), mem, entry->u.mem.len); | |
737 | } | |
738 | } | |
739 | } | |
740 | break; | |
741 | } | |
742 | } | |
743 | ||
27699eea MS |
744 | static struct target_ops *tmp_to_resume_ops; |
745 | static void (*tmp_to_resume) (struct target_ops *, ptid_t, int, | |
746 | enum target_signal); | |
747 | static struct target_ops *tmp_to_wait_ops; | |
748 | static ptid_t (*tmp_to_wait) (struct target_ops *, ptid_t, | |
749 | struct target_waitstatus *, | |
750 | int); | |
751 | static struct target_ops *tmp_to_store_registers_ops; | |
752 | static void (*tmp_to_store_registers) (struct target_ops *, | |
753 | struct regcache *, | |
754 | int regno); | |
755 | static struct target_ops *tmp_to_xfer_partial_ops; | |
756 | static LONGEST (*tmp_to_xfer_partial) (struct target_ops *ops, | |
757 | enum target_object object, | |
758 | const char *annex, | |
759 | gdb_byte *readbuf, | |
760 | const gdb_byte *writebuf, | |
761 | ULONGEST offset, | |
762 | LONGEST len); | |
763 | static int (*tmp_to_insert_breakpoint) (struct gdbarch *, | |
764 | struct bp_target_info *); | |
765 | static int (*tmp_to_remove_breakpoint) (struct gdbarch *, | |
766 | struct bp_target_info *); | |
767 | ||
768 | /* Open the process record target. */ | |
6df67667 | 769 | |
69d05d38 | 770 | static void |
27699eea MS |
771 | record_core_open_1 (char *name, int from_tty) |
772 | { | |
773 | struct regcache *regcache = get_current_regcache (); | |
774 | int regnum = gdbarch_num_regs (get_regcache_arch (regcache)); | |
775 | int i; | |
776 | ||
777 | /* Get record_core_regbuf. */ | |
778 | target_fetch_registers (regcache, -1); | |
779 | record_core_regbuf = xmalloc (MAX_REGISTER_SIZE * regnum); | |
780 | for (i = 0; i < regnum; i ++) | |
781 | regcache_raw_collect (regcache, i, | |
782 | record_core_regbuf + MAX_REGISTER_SIZE * i); | |
783 | ||
784 | /* Get record_core_start and record_core_end. */ | |
785 | if (build_section_table (core_bfd, &record_core_start, &record_core_end)) | |
786 | { | |
787 | xfree (record_core_regbuf); | |
788 | record_core_regbuf = NULL; | |
789 | error (_("\"%s\": Can't find sections: %s"), | |
790 | bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ())); | |
791 | } | |
792 | ||
793 | push_target (&record_core_ops); | |
794 | } | |
795 | ||
796 | /* "to_open" target method for 'live' processes. */ | |
797 | ||
798 | static void | |
799 | record_open_1 (char *name, int from_tty) | |
69d05d38 HZ |
800 | { |
801 | struct target_ops *t; | |
802 | ||
803 | if (record_debug) | |
804 | fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n"); | |
805 | ||
806 | /* check exec */ | |
807 | if (!target_has_execution) | |
808 | error (_("Process record: the program is not being run.")); | |
809 | if (non_stop) | |
810 | error (_("Process record target can't debug inferior in non-stop mode " | |
811 | "(non-stop).")); | |
812 | if (target_async_permitted) | |
813 | error (_("Process record target can't debug inferior in asynchronous " | |
814 | "mode (target-async).")); | |
815 | ||
a97b0ac8 | 816 | if (!gdbarch_process_record_p (target_gdbarch)) |
69d05d38 HZ |
817 | error (_("Process record: the current architecture doesn't support " |
818 | "record function.")); | |
819 | ||
27699eea MS |
820 | if (!tmp_to_resume) |
821 | error (_("Could not find 'to_resume' method on the target stack.")); | |
822 | if (!tmp_to_wait) | |
823 | error (_("Could not find 'to_wait' method on the target stack.")); | |
824 | if (!tmp_to_store_registers) | |
825 | error (_("Could not find 'to_store_registers' method on the target stack.")); | |
826 | if (!tmp_to_insert_breakpoint) | |
827 | error (_("Could not find 'to_insert_breakpoint' method on the target stack.")); | |
828 | if (!tmp_to_remove_breakpoint) | |
829 | error (_("Could not find 'to_remove_breakpoint' method on the target stack.")); | |
830 | ||
831 | push_target (&record_ops); | |
832 | } | |
833 | ||
834 | /* "to_open" target method. Open the process record target. */ | |
835 | ||
836 | static void | |
837 | record_open (char *name, int from_tty) | |
838 | { | |
839 | struct target_ops *t; | |
840 | ||
841 | if (record_debug) | |
842 | fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n"); | |
843 | ||
69d05d38 HZ |
844 | /* Check if record target is already running. */ |
845 | if (current_target.to_stratum == record_stratum) | |
5d40bb85 HZ |
846 | error (_("Process record target already running. Use \"record stop\" to " |
847 | "stop record target first.")); | |
69d05d38 | 848 | |
27699eea MS |
849 | /* Reset the tmp beneath pointers. */ |
850 | tmp_to_resume_ops = NULL; | |
851 | tmp_to_resume = NULL; | |
852 | tmp_to_wait_ops = NULL; | |
853 | tmp_to_wait = NULL; | |
854 | tmp_to_store_registers_ops = NULL; | |
855 | tmp_to_store_registers = NULL; | |
856 | tmp_to_xfer_partial_ops = NULL; | |
857 | tmp_to_xfer_partial = NULL; | |
858 | tmp_to_insert_breakpoint = NULL; | |
859 | tmp_to_remove_breakpoint = NULL; | |
69d05d38 HZ |
860 | |
861 | /* Set the beneath function pointers. */ | |
862 | for (t = current_target.beneath; t != NULL; t = t->beneath) | |
863 | { | |
27699eea | 864 | if (!tmp_to_resume) |
69d05d38 | 865 | { |
27699eea MS |
866 | tmp_to_resume = t->to_resume; |
867 | tmp_to_resume_ops = t; | |
69d05d38 | 868 | } |
27699eea | 869 | if (!tmp_to_wait) |
69d05d38 | 870 | { |
27699eea MS |
871 | tmp_to_wait = t->to_wait; |
872 | tmp_to_wait_ops = t; | |
69d05d38 | 873 | } |
27699eea | 874 | if (!tmp_to_store_registers) |
69d05d38 | 875 | { |
27699eea MS |
876 | tmp_to_store_registers = t->to_store_registers; |
877 | tmp_to_store_registers_ops = t; | |
69d05d38 | 878 | } |
27699eea | 879 | if (!tmp_to_xfer_partial) |
69d05d38 | 880 | { |
27699eea MS |
881 | tmp_to_xfer_partial = t->to_xfer_partial; |
882 | tmp_to_xfer_partial_ops = t; | |
69d05d38 | 883 | } |
27699eea MS |
884 | if (!tmp_to_insert_breakpoint) |
885 | tmp_to_insert_breakpoint = t->to_insert_breakpoint; | |
886 | if (!tmp_to_remove_breakpoint) | |
887 | tmp_to_remove_breakpoint = t->to_remove_breakpoint; | |
69d05d38 | 888 | } |
27699eea MS |
889 | if (!tmp_to_xfer_partial) |
890 | error (_("Could not find 'to_xfer_partial' method on the target stack.")); | |
69d05d38 HZ |
891 | |
892 | /* Reset */ | |
893 | record_insn_num = 0; | |
b54295a7 | 894 | record_insn_count = 0; |
69d05d38 HZ |
895 | record_list = &record_first; |
896 | record_list->next = NULL; | |
27699eea MS |
897 | |
898 | /* Set the tmp beneath pointers to beneath pointers. */ | |
899 | record_beneath_to_resume_ops = tmp_to_resume_ops; | |
900 | record_beneath_to_resume = tmp_to_resume; | |
901 | record_beneath_to_wait_ops = tmp_to_wait_ops; | |
902 | record_beneath_to_wait = tmp_to_wait; | |
903 | record_beneath_to_store_registers_ops = tmp_to_store_registers_ops; | |
904 | record_beneath_to_store_registers = tmp_to_store_registers; | |
905 | record_beneath_to_xfer_partial_ops = tmp_to_xfer_partial_ops; | |
906 | record_beneath_to_xfer_partial = tmp_to_xfer_partial; | |
907 | record_beneath_to_insert_breakpoint = tmp_to_insert_breakpoint; | |
908 | record_beneath_to_remove_breakpoint = tmp_to_remove_breakpoint; | |
909 | ||
910 | if (current_target.to_stratum == core_stratum) | |
911 | record_core_open_1 (name, from_tty); | |
912 | else | |
913 | record_open_1 (name, from_tty); | |
69d05d38 HZ |
914 | } |
915 | ||
6df67667 MS |
916 | /* "to_close" target method. Close the process record target. */ |
917 | ||
69d05d38 HZ |
918 | static void |
919 | record_close (int quitting) | |
920 | { | |
27699eea MS |
921 | struct record_core_buf_entry *entry; |
922 | ||
69d05d38 HZ |
923 | if (record_debug) |
924 | fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n"); | |
925 | ||
926 | record_list_release (record_list); | |
27699eea MS |
927 | |
928 | /* Release record_core_regbuf. */ | |
929 | if (record_core_regbuf) | |
930 | { | |
931 | xfree (record_core_regbuf); | |
932 | record_core_regbuf = NULL; | |
933 | } | |
934 | ||
935 | /* Release record_core_buf_list. */ | |
936 | if (record_core_buf_list) | |
937 | { | |
938 | for (entry = record_core_buf_list->prev; entry; entry = entry->prev) | |
939 | { | |
940 | xfree (record_core_buf_list); | |
941 | record_core_buf_list = entry; | |
942 | } | |
943 | record_core_buf_list = NULL; | |
944 | } | |
69d05d38 HZ |
945 | } |
946 | ||
947 | static int record_resume_step = 0; | |
69d05d38 HZ |
948 | static int record_resume_error; |
949 | ||
6df67667 MS |
950 | /* "to_resume" target method. Resume the process record target. */ |
951 | ||
69d05d38 HZ |
952 | static void |
953 | record_resume (struct target_ops *ops, ptid_t ptid, int step, | |
8b739a96 | 954 | enum target_signal signal) |
69d05d38 HZ |
955 | { |
956 | record_resume_step = step; | |
69d05d38 HZ |
957 | |
958 | if (!RECORD_IS_REPLAY) | |
959 | { | |
8b739a96 | 960 | if (do_record_message (get_current_regcache (), signal)) |
69d05d38 HZ |
961 | { |
962 | record_resume_error = 0; | |
963 | } | |
964 | else | |
965 | { | |
966 | record_resume_error = 1; | |
967 | return; | |
968 | } | |
969 | record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1, | |
8b739a96 | 970 | signal); |
69d05d38 HZ |
971 | } |
972 | } | |
973 | ||
974 | static int record_get_sig = 0; | |
975 | ||
6df67667 MS |
976 | /* SIGINT signal handler, registered by "to_wait" method. */ |
977 | ||
69d05d38 HZ |
978 | static void |
979 | record_sig_handler (int signo) | |
980 | { | |
981 | if (record_debug) | |
982 | fprintf_unfiltered (gdb_stdlog, "Process record: get a signal\n"); | |
983 | ||
984 | /* It will break the running inferior in replay mode. */ | |
985 | record_resume_step = 1; | |
986 | ||
987 | /* It will let record_wait set inferior status to get the signal | |
988 | SIGINT. */ | |
989 | record_get_sig = 1; | |
990 | } | |
991 | ||
992 | static void | |
993 | record_wait_cleanups (void *ignore) | |
994 | { | |
995 | if (execution_direction == EXEC_REVERSE) | |
996 | { | |
997 | if (record_list->next) | |
998 | record_list = record_list->next; | |
999 | } | |
1000 | else | |
1001 | record_list = record_list->prev; | |
1002 | } | |
1003 | ||
6df67667 MS |
1004 | /* "to_wait" target method for process record target. |
1005 | ||
1006 | In record mode, the target is always run in singlestep mode | |
1007 | (even when gdb says to continue). The to_wait method intercepts | |
1008 | the stop events and determines which ones are to be passed on to | |
1009 | gdb. Most stop events are just singlestep events that gdb is not | |
1010 | to know about, so the to_wait method just records them and keeps | |
1011 | singlestepping. | |
1012 | ||
1013 | In replay mode, this function emulates the recorded execution log, | |
1014 | one instruction at a time (forward or backward), and determines | |
1015 | where to stop. */ | |
69d05d38 HZ |
1016 | |
1017 | static ptid_t | |
1018 | record_wait (struct target_ops *ops, | |
47608cb1 PA |
1019 | ptid_t ptid, struct target_waitstatus *status, |
1020 | int options) | |
69d05d38 HZ |
1021 | { |
1022 | struct cleanup *set_cleanups = record_gdb_operation_disable_set (); | |
1023 | ||
1024 | if (record_debug) | |
1025 | fprintf_unfiltered (gdb_stdlog, | |
1026 | "Process record: record_wait " | |
1027 | "record_resume_step = %d\n", | |
1028 | record_resume_step); | |
1029 | ||
27699eea | 1030 | if (!RECORD_IS_REPLAY && ops != &record_core_ops) |
69d05d38 HZ |
1031 | { |
1032 | if (record_resume_error) | |
1033 | { | |
1034 | /* If record_resume get error, return directly. */ | |
1035 | status->kind = TARGET_WAITKIND_STOPPED; | |
1036 | status->value.sig = TARGET_SIGNAL_ABRT; | |
1037 | return inferior_ptid; | |
1038 | } | |
1039 | ||
1040 | if (record_resume_step) | |
1041 | { | |
1042 | /* This is a single step. */ | |
1043 | return record_beneath_to_wait (record_beneath_to_wait_ops, | |
90092760 | 1044 | ptid, status, options); |
69d05d38 HZ |
1045 | } |
1046 | else | |
1047 | { | |
1048 | /* This is not a single step. */ | |
1049 | ptid_t ret; | |
1050 | CORE_ADDR tmp_pc; | |
1051 | ||
1052 | while (1) | |
1053 | { | |
1054 | ret = record_beneath_to_wait (record_beneath_to_wait_ops, | |
90092760 | 1055 | ptid, status, options); |
69d05d38 | 1056 | |
8b739a96 | 1057 | /* Is this a SIGTRAP? */ |
69d05d38 HZ |
1058 | if (status->kind == TARGET_WAITKIND_STOPPED |
1059 | && status->value.sig == TARGET_SIGNAL_TRAP) | |
1060 | { | |
6c95b8df PA |
1061 | struct regcache *regcache; |
1062 | ||
8b739a96 | 1063 | /* Yes -- check if there is a breakpoint. */ |
69d05d38 | 1064 | registers_changed (); |
6c95b8df PA |
1065 | regcache = get_current_regcache (); |
1066 | tmp_pc = regcache_read_pc (regcache); | |
1067 | if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), | |
1068 | tmp_pc)) | |
69d05d38 | 1069 | { |
8b739a96 | 1070 | /* There is a breakpoint. GDB will want to stop. */ |
6c95b8df PA |
1071 | struct gdbarch *gdbarch = get_regcache_arch (regcache); |
1072 | CORE_ADDR decr_pc_after_break | |
1073 | = gdbarch_decr_pc_after_break (gdbarch); | |
69d05d38 | 1074 | if (decr_pc_after_break) |
6c95b8df PA |
1075 | regcache_write_pc (regcache, |
1076 | tmp_pc + decr_pc_after_break); | |
69d05d38 HZ |
1077 | } |
1078 | else | |
1079 | { | |
8b739a96 HZ |
1080 | /* There is not a breakpoint, and gdb is not |
1081 | stepping, therefore gdb will not stop. | |
1082 | Therefore we will not return to gdb. | |
1083 | Record the insn and resume. */ | |
6c95b8df PA |
1084 | if (!do_record_message (regcache, TARGET_SIGNAL_0)) |
1085 | break; | |
1086 | ||
69d05d38 HZ |
1087 | record_beneath_to_resume (record_beneath_to_resume_ops, |
1088 | ptid, 1, | |
88fef440 | 1089 | TARGET_SIGNAL_0); |
69d05d38 HZ |
1090 | continue; |
1091 | } | |
1092 | } | |
1093 | ||
1094 | /* The inferior is broken by a breakpoint or a signal. */ | |
1095 | break; | |
1096 | } | |
1097 | ||
1098 | return ret; | |
1099 | } | |
1100 | } | |
1101 | else | |
1102 | { | |
1103 | struct regcache *regcache = get_current_regcache (); | |
5af949e3 | 1104 | struct gdbarch *gdbarch = get_regcache_arch (regcache); |
69d05d38 HZ |
1105 | int continue_flag = 1; |
1106 | int first_record_end = 1; | |
1107 | struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0); | |
1108 | CORE_ADDR tmp_pc; | |
1109 | ||
1110 | status->kind = TARGET_WAITKIND_STOPPED; | |
1111 | ||
1112 | /* Check breakpoint when forward execute. */ | |
1113 | if (execution_direction == EXEC_FORWARD) | |
1114 | { | |
1115 | tmp_pc = regcache_read_pc (regcache); | |
6c95b8df PA |
1116 | if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), |
1117 | tmp_pc)) | |
69d05d38 HZ |
1118 | { |
1119 | if (record_debug) | |
1120 | fprintf_unfiltered (gdb_stdlog, | |
5af949e3 UW |
1121 | "Process record: break at %s.\n", |
1122 | paddress (gdbarch, tmp_pc)); | |
1123 | if (gdbarch_decr_pc_after_break (gdbarch) | |
69d05d38 HZ |
1124 | && !record_resume_step) |
1125 | regcache_write_pc (regcache, | |
1126 | tmp_pc + | |
5af949e3 | 1127 | gdbarch_decr_pc_after_break (gdbarch)); |
69d05d38 HZ |
1128 | goto replay_out; |
1129 | } | |
1130 | } | |
1131 | ||
1132 | record_get_sig = 0; | |
1133 | signal (SIGINT, record_sig_handler); | |
1134 | /* If GDB is in terminal_inferior mode, it will not get the signal. | |
1135 | And in GDB replay mode, GDB doesn't need to be in terminal_inferior | |
1136 | mode, because inferior will not executed. | |
1137 | Then set it to terminal_ours to make GDB get the signal. */ | |
1138 | target_terminal_ours (); | |
1139 | ||
1140 | /* In EXEC_FORWARD mode, record_list points to the tail of prev | |
1141 | instruction. */ | |
1142 | if (execution_direction == EXEC_FORWARD && record_list->next) | |
1143 | record_list = record_list->next; | |
1144 | ||
1145 | /* Loop over the record_list, looking for the next place to | |
1146 | stop. */ | |
1147 | do | |
1148 | { | |
1149 | /* Check for beginning and end of log. */ | |
1150 | if (execution_direction == EXEC_REVERSE | |
1151 | && record_list == &record_first) | |
1152 | { | |
1153 | /* Hit beginning of record log in reverse. */ | |
1154 | status->kind = TARGET_WAITKIND_NO_HISTORY; | |
1155 | break; | |
1156 | } | |
1157 | if (execution_direction != EXEC_REVERSE && !record_list->next) | |
1158 | { | |
1159 | /* Hit end of record log going forward. */ | |
1160 | status->kind = TARGET_WAITKIND_NO_HISTORY; | |
1161 | break; | |
1162 | } | |
1163 | ||
90ca0479 MS |
1164 | record_exec_insn (regcache, gdbarch, record_list); |
1165 | ||
1166 | if (record_list->type == record_end) | |
69d05d38 HZ |
1167 | { |
1168 | if (record_debug > 1) | |
1169 | fprintf_unfiltered (gdb_stdlog, | |
1170 | "Process record: record_end %s to " | |
1171 | "inferior.\n", | |
1172 | host_address_to_string (record_list)); | |
1173 | ||
1174 | if (first_record_end && execution_direction == EXEC_REVERSE) | |
1175 | { | |
1176 | /* When reverse excute, the first record_end is the part of | |
1177 | current instruction. */ | |
1178 | first_record_end = 0; | |
1179 | } | |
1180 | else | |
1181 | { | |
1182 | /* In EXEC_REVERSE mode, this is the record_end of prev | |
1183 | instruction. | |
1184 | In EXEC_FORWARD mode, this is the record_end of current | |
1185 | instruction. */ | |
1186 | /* step */ | |
1187 | if (record_resume_step) | |
1188 | { | |
1189 | if (record_debug > 1) | |
1190 | fprintf_unfiltered (gdb_stdlog, | |
1191 | "Process record: step.\n"); | |
1192 | continue_flag = 0; | |
1193 | } | |
1194 | ||
1195 | /* check breakpoint */ | |
1196 | tmp_pc = regcache_read_pc (regcache); | |
6c95b8df PA |
1197 | if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), |
1198 | tmp_pc)) | |
69d05d38 HZ |
1199 | { |
1200 | if (record_debug) | |
1201 | fprintf_unfiltered (gdb_stdlog, | |
1202 | "Process record: break " | |
5af949e3 UW |
1203 | "at %s.\n", |
1204 | paddress (gdbarch, tmp_pc)); | |
1205 | if (gdbarch_decr_pc_after_break (gdbarch) | |
69d05d38 HZ |
1206 | && execution_direction == EXEC_FORWARD |
1207 | && !record_resume_step) | |
1208 | regcache_write_pc (regcache, | |
1209 | tmp_pc + | |
5af949e3 | 1210 | gdbarch_decr_pc_after_break (gdbarch)); |
69d05d38 HZ |
1211 | continue_flag = 0; |
1212 | } | |
8b739a96 HZ |
1213 | /* Check target signal */ |
1214 | if (record_list->u.end.sigval != TARGET_SIGNAL_0) | |
1215 | /* FIXME: better way to check */ | |
1216 | continue_flag = 0; | |
69d05d38 HZ |
1217 | } |
1218 | } | |
1219 | ||
1220 | if (continue_flag) | |
1221 | { | |
1222 | if (execution_direction == EXEC_REVERSE) | |
1223 | { | |
1224 | if (record_list->prev) | |
1225 | record_list = record_list->prev; | |
1226 | } | |
1227 | else | |
1228 | { | |
1229 | if (record_list->next) | |
1230 | record_list = record_list->next; | |
1231 | } | |
1232 | } | |
1233 | } | |
1234 | while (continue_flag); | |
1235 | ||
1236 | signal (SIGINT, handle_sigint); | |
1237 | ||
1238 | replay_out: | |
1239 | if (record_get_sig) | |
1240 | status->value.sig = TARGET_SIGNAL_INT; | |
8b739a96 HZ |
1241 | else if (record_list->u.end.sigval != TARGET_SIGNAL_0) |
1242 | /* FIXME: better way to check */ | |
1243 | status->value.sig = record_list->u.end.sigval; | |
69d05d38 HZ |
1244 | else |
1245 | status->value.sig = TARGET_SIGNAL_TRAP; | |
1246 | ||
1247 | discard_cleanups (old_cleanups); | |
1248 | } | |
1249 | ||
1250 | do_cleanups (set_cleanups); | |
1251 | return inferior_ptid; | |
1252 | } | |
1253 | ||
6df67667 MS |
1254 | /* "to_disconnect" method for process record target. */ |
1255 | ||
69d05d38 HZ |
1256 | static void |
1257 | record_disconnect (struct target_ops *target, char *args, int from_tty) | |
1258 | { | |
1259 | if (record_debug) | |
1260 | fprintf_unfiltered (gdb_stdlog, "Process record: record_disconnect\n"); | |
1261 | ||
1262 | unpush_target (&record_ops); | |
1263 | target_disconnect (args, from_tty); | |
1264 | } | |
1265 | ||
6df67667 MS |
1266 | /* "to_detach" method for process record target. */ |
1267 | ||
69d05d38 HZ |
1268 | static void |
1269 | record_detach (struct target_ops *ops, char *args, int from_tty) | |
1270 | { | |
1271 | if (record_debug) | |
1272 | fprintf_unfiltered (gdb_stdlog, "Process record: record_detach\n"); | |
1273 | ||
1274 | unpush_target (&record_ops); | |
1275 | target_detach (args, from_tty); | |
1276 | } | |
1277 | ||
6df67667 MS |
1278 | /* "to_mourn_inferior" method for process record target. */ |
1279 | ||
69d05d38 HZ |
1280 | static void |
1281 | record_mourn_inferior (struct target_ops *ops) | |
1282 | { | |
1283 | if (record_debug) | |
1284 | fprintf_unfiltered (gdb_stdlog, "Process record: " | |
1285 | "record_mourn_inferior\n"); | |
1286 | ||
1287 | unpush_target (&record_ops); | |
1288 | target_mourn_inferior (); | |
1289 | } | |
1290 | ||
1291 | /* Close process record target before killing the inferior process. */ | |
1292 | ||
1293 | static void | |
1294 | record_kill (struct target_ops *ops) | |
1295 | { | |
1296 | if (record_debug) | |
1297 | fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n"); | |
1298 | ||
1299 | unpush_target (&record_ops); | |
1300 | target_kill (); | |
1301 | } | |
1302 | ||
1303 | /* Record registers change (by user or by GDB) to list as an instruction. */ | |
1304 | ||
1305 | static void | |
1306 | record_registers_change (struct regcache *regcache, int regnum) | |
1307 | { | |
1308 | /* Check record_insn_num. */ | |
1309 | record_check_insn_num (0); | |
1310 | ||
1311 | record_arch_list_head = NULL; | |
1312 | record_arch_list_tail = NULL; | |
1313 | ||
1314 | if (regnum < 0) | |
1315 | { | |
1316 | int i; | |
1317 | for (i = 0; i < gdbarch_num_regs (get_regcache_arch (regcache)); i++) | |
1318 | { | |
1319 | if (record_arch_list_add_reg (regcache, i)) | |
1320 | { | |
1321 | record_list_release (record_arch_list_tail); | |
1322 | error (_("Process record: failed to record execution log.")); | |
1323 | } | |
1324 | } | |
1325 | } | |
1326 | else | |
1327 | { | |
1328 | if (record_arch_list_add_reg (regcache, regnum)) | |
1329 | { | |
1330 | record_list_release (record_arch_list_tail); | |
1331 | error (_("Process record: failed to record execution log.")); | |
1332 | } | |
1333 | } | |
1334 | if (record_arch_list_add_end ()) | |
1335 | { | |
1336 | record_list_release (record_arch_list_tail); | |
1337 | error (_("Process record: failed to record execution log.")); | |
1338 | } | |
1339 | record_list->next = record_arch_list_head; | |
1340 | record_arch_list_head->prev = record_list; | |
1341 | record_list = record_arch_list_tail; | |
1342 | ||
1343 | if (record_insn_num == record_insn_max_num && record_insn_max_num) | |
1344 | record_list_release_first (); | |
1345 | else | |
1346 | record_insn_num++; | |
1347 | } | |
1348 | ||
6df67667 MS |
1349 | /* "to_store_registers" method for process record target. */ |
1350 | ||
69d05d38 HZ |
1351 | static void |
1352 | record_store_registers (struct target_ops *ops, struct regcache *regcache, | |
1353 | int regno) | |
1354 | { | |
1355 | if (!record_gdb_operation_disable) | |
1356 | { | |
1357 | if (RECORD_IS_REPLAY) | |
1358 | { | |
1359 | int n; | |
69d05d38 HZ |
1360 | |
1361 | /* Let user choose if he wants to write register or not. */ | |
1362 | if (regno < 0) | |
1363 | n = | |
604ad007 JB |
1364 | query (_("Because GDB is in replay mode, changing the " |
1365 | "value of a register will make the execution " | |
1366 | "log unusable from this point onward. " | |
1367 | "Change all registers?")); | |
69d05d38 HZ |
1368 | else |
1369 | n = | |
604ad007 JB |
1370 | query (_("Because GDB is in replay mode, changing the value " |
1371 | "of a register will make the execution log unusable " | |
1372 | "from this point onward. Change register %s?"), | |
69d05d38 HZ |
1373 | gdbarch_register_name (get_regcache_arch (regcache), |
1374 | regno)); | |
1375 | ||
1376 | if (!n) | |
1377 | { | |
1378 | /* Invalidate the value of regcache that was set in function | |
1379 | "regcache_raw_write". */ | |
1380 | if (regno < 0) | |
1381 | { | |
1382 | int i; | |
1383 | for (i = 0; | |
1384 | i < gdbarch_num_regs (get_regcache_arch (regcache)); | |
1385 | i++) | |
1386 | regcache_invalidate (regcache, i); | |
1387 | } | |
1388 | else | |
1389 | regcache_invalidate (regcache, regno); | |
1390 | ||
1391 | error (_("Process record canceled the operation.")); | |
1392 | } | |
1393 | ||
1394 | /* Destroy the record from here forward. */ | |
61f75dd8 | 1395 | record_list_release_following (record_list); |
69d05d38 HZ |
1396 | } |
1397 | ||
1398 | record_registers_change (regcache, regno); | |
1399 | } | |
1400 | record_beneath_to_store_registers (record_beneath_to_store_registers_ops, | |
1401 | regcache, regno); | |
1402 | } | |
1403 | ||
27699eea | 1404 | /* "to_xfer_partial" method. Behavior is conditional on RECORD_IS_REPLAY. |
69d05d38 HZ |
1405 | In replay mode, we cannot write memory unles we are willing to |
1406 | invalidate the record/replay log from this point forward. */ | |
1407 | ||
1408 | static LONGEST | |
1409 | record_xfer_partial (struct target_ops *ops, enum target_object object, | |
1410 | const char *annex, gdb_byte *readbuf, | |
1411 | const gdb_byte *writebuf, ULONGEST offset, LONGEST len) | |
1412 | { | |
1413 | if (!record_gdb_operation_disable | |
1414 | && (object == TARGET_OBJECT_MEMORY | |
1415 | || object == TARGET_OBJECT_RAW_MEMORY) && writebuf) | |
1416 | { | |
1417 | if (RECORD_IS_REPLAY) | |
1418 | { | |
1419 | /* Let user choose if he wants to write memory or not. */ | |
604ad007 JB |
1420 | if (!query (_("Because GDB is in replay mode, writing to memory " |
1421 | "will make the execution log unusable from this " | |
1422 | "point onward. Write memory at address %s?"), | |
5af949e3 | 1423 | paddress (target_gdbarch, offset))) |
9a9dc473 | 1424 | error (_("Process record canceled the operation.")); |
69d05d38 HZ |
1425 | |
1426 | /* Destroy the record from here forward. */ | |
61f75dd8 | 1427 | record_list_release_following (record_list); |
69d05d38 HZ |
1428 | } |
1429 | ||
1430 | /* Check record_insn_num */ | |
1431 | record_check_insn_num (0); | |
1432 | ||
1433 | /* Record registers change to list as an instruction. */ | |
1434 | record_arch_list_head = NULL; | |
1435 | record_arch_list_tail = NULL; | |
1436 | if (record_arch_list_add_mem (offset, len)) | |
1437 | { | |
1438 | record_list_release (record_arch_list_tail); | |
1439 | if (record_debug) | |
1440 | fprintf_unfiltered (gdb_stdlog, | |
1441 | _("Process record: failed to record " | |
1442 | "execution log.")); | |
1443 | return -1; | |
1444 | } | |
1445 | if (record_arch_list_add_end ()) | |
1446 | { | |
1447 | record_list_release (record_arch_list_tail); | |
1448 | if (record_debug) | |
1449 | fprintf_unfiltered (gdb_stdlog, | |
1450 | _("Process record: failed to record " | |
1451 | "execution log.")); | |
1452 | return -1; | |
1453 | } | |
1454 | record_list->next = record_arch_list_head; | |
1455 | record_arch_list_head->prev = record_list; | |
1456 | record_list = record_arch_list_tail; | |
1457 | ||
1458 | if (record_insn_num == record_insn_max_num && record_insn_max_num) | |
1459 | record_list_release_first (); | |
1460 | else | |
1461 | record_insn_num++; | |
1462 | } | |
1463 | ||
1464 | return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops, | |
1465 | object, annex, readbuf, writebuf, | |
1466 | offset, len); | |
1467 | } | |
1468 | ||
1469 | /* Behavior is conditional on RECORD_IS_REPLAY. | |
1470 | We will not actually insert or remove breakpoints when replaying, | |
1471 | nor when recording. */ | |
1472 | ||
1473 | static int | |
a6d9a66e UW |
1474 | record_insert_breakpoint (struct gdbarch *gdbarch, |
1475 | struct bp_target_info *bp_tgt) | |
69d05d38 HZ |
1476 | { |
1477 | if (!RECORD_IS_REPLAY) | |
1478 | { | |
1479 | struct cleanup *old_cleanups = record_gdb_operation_disable_set (); | |
a6d9a66e | 1480 | int ret = record_beneath_to_insert_breakpoint (gdbarch, bp_tgt); |
69d05d38 HZ |
1481 | |
1482 | do_cleanups (old_cleanups); | |
1483 | ||
1484 | return ret; | |
1485 | } | |
1486 | ||
1487 | return 0; | |
1488 | } | |
1489 | ||
6df67667 MS |
1490 | /* "to_remove_breakpoint" method for process record target. */ |
1491 | ||
69d05d38 | 1492 | static int |
a6d9a66e UW |
1493 | record_remove_breakpoint (struct gdbarch *gdbarch, |
1494 | struct bp_target_info *bp_tgt) | |
69d05d38 HZ |
1495 | { |
1496 | if (!RECORD_IS_REPLAY) | |
1497 | { | |
1498 | struct cleanup *old_cleanups = record_gdb_operation_disable_set (); | |
a6d9a66e | 1499 | int ret = record_beneath_to_remove_breakpoint (gdbarch, bp_tgt); |
69d05d38 HZ |
1500 | |
1501 | do_cleanups (old_cleanups); | |
1502 | ||
1503 | return ret; | |
1504 | } | |
1505 | ||
1506 | return 0; | |
1507 | } | |
1508 | ||
6df67667 | 1509 | /* "to_can_execute_reverse" method for process record target. */ |
27699eea | 1510 | |
69d05d38 HZ |
1511 | static int |
1512 | record_can_execute_reverse (void) | |
1513 | { | |
1514 | return 1; | |
1515 | } | |
1516 | ||
1517 | static void | |
1518 | init_record_ops (void) | |
1519 | { | |
1520 | record_ops.to_shortname = "record"; | |
1521 | record_ops.to_longname = "Process record and replay target"; | |
1522 | record_ops.to_doc = | |
1523 | "Log program while executing and replay execution from log."; | |
1524 | record_ops.to_open = record_open; | |
1525 | record_ops.to_close = record_close; | |
1526 | record_ops.to_resume = record_resume; | |
1527 | record_ops.to_wait = record_wait; | |
1528 | record_ops.to_disconnect = record_disconnect; | |
1529 | record_ops.to_detach = record_detach; | |
1530 | record_ops.to_mourn_inferior = record_mourn_inferior; | |
1531 | record_ops.to_kill = record_kill; | |
1532 | record_ops.to_create_inferior = find_default_create_inferior; | |
1533 | record_ops.to_store_registers = record_store_registers; | |
1534 | record_ops.to_xfer_partial = record_xfer_partial; | |
1535 | record_ops.to_insert_breakpoint = record_insert_breakpoint; | |
1536 | record_ops.to_remove_breakpoint = record_remove_breakpoint; | |
1537 | record_ops.to_can_execute_reverse = record_can_execute_reverse; | |
1538 | record_ops.to_stratum = record_stratum; | |
1539 | record_ops.to_magic = OPS_MAGIC; | |
1540 | } | |
1541 | ||
27699eea MS |
1542 | /* "to_resume" method for prec over corefile. */ |
1543 | ||
1544 | static void | |
1545 | record_core_resume (struct target_ops *ops, ptid_t ptid, int step, | |
1546 | enum target_signal signal) | |
1547 | { | |
1548 | record_resume_step = step; | |
1549 | } | |
1550 | ||
1551 | /* "to_kill" method for prec over corefile. */ | |
1552 | ||
1553 | static void | |
1554 | record_core_kill (struct target_ops *ops) | |
1555 | { | |
1556 | if (record_debug) | |
1557 | fprintf_unfiltered (gdb_stdlog, "Process record: record_core_kill\n"); | |
1558 | ||
1559 | unpush_target (&record_core_ops); | |
1560 | } | |
1561 | ||
1562 | /* "to_fetch_registers" method for prec over corefile. */ | |
1563 | ||
1564 | static void | |
1565 | record_core_fetch_registers (struct target_ops *ops, | |
1566 | struct regcache *regcache, | |
1567 | int regno) | |
1568 | { | |
1569 | if (regno < 0) | |
1570 | { | |
1571 | int num = gdbarch_num_regs (get_regcache_arch (regcache)); | |
1572 | int i; | |
1573 | ||
1574 | for (i = 0; i < num; i ++) | |
1575 | regcache_raw_supply (regcache, i, | |
1576 | record_core_regbuf + MAX_REGISTER_SIZE * i); | |
1577 | } | |
1578 | else | |
1579 | regcache_raw_supply (regcache, regno, | |
1580 | record_core_regbuf + MAX_REGISTER_SIZE * regno); | |
1581 | } | |
1582 | ||
1583 | /* "to_prepare_to_store" method for prec over corefile. */ | |
1584 | ||
1585 | static void | |
1586 | record_core_prepare_to_store (struct regcache *regcache) | |
1587 | { | |
1588 | } | |
1589 | ||
1590 | /* "to_store_registers" method for prec over corefile. */ | |
1591 | ||
1592 | static void | |
1593 | record_core_store_registers (struct target_ops *ops, | |
1594 | struct regcache *regcache, | |
1595 | int regno) | |
1596 | { | |
1597 | if (record_gdb_operation_disable) | |
1598 | regcache_raw_collect (regcache, regno, | |
1599 | record_core_regbuf + MAX_REGISTER_SIZE * regno); | |
1600 | else | |
1601 | error (_("You can't do that without a process to debug.")); | |
1602 | } | |
1603 | ||
1604 | /* "to_xfer_partial" method for prec over corefile. */ | |
1605 | ||
1606 | static LONGEST | |
1607 | record_core_xfer_partial (struct target_ops *ops, enum target_object object, | |
1608 | const char *annex, gdb_byte *readbuf, | |
1609 | const gdb_byte *writebuf, ULONGEST offset, | |
1610 | LONGEST len) | |
1611 | { | |
1612 | if (object == TARGET_OBJECT_MEMORY) | |
1613 | { | |
1614 | if (record_gdb_operation_disable || !writebuf) | |
1615 | { | |
1616 | struct target_section *p; | |
1617 | for (p = record_core_start; p < record_core_end; p++) | |
1618 | { | |
1619 | if (offset >= p->addr) | |
1620 | { | |
1621 | struct record_core_buf_entry *entry; | |
1622 | ||
1623 | if (offset >= p->endaddr) | |
1624 | continue; | |
1625 | ||
1626 | if (offset + len > p->endaddr) | |
1627 | len = p->endaddr - offset; | |
1628 | ||
1629 | offset -= p->addr; | |
1630 | ||
1631 | /* Read readbuf or write writebuf p, offset, len. */ | |
1632 | /* Check flags. */ | |
1633 | if (p->the_bfd_section->flags & SEC_CONSTRUCTOR | |
1634 | || (p->the_bfd_section->flags & SEC_HAS_CONTENTS) == 0) | |
1635 | { | |
1636 | if (readbuf) | |
1637 | memset (readbuf, 0, len); | |
1638 | return len; | |
1639 | } | |
1640 | /* Get record_core_buf_entry. */ | |
1641 | for (entry = record_core_buf_list; entry; | |
1642 | entry = entry->prev) | |
1643 | if (entry->p == p) | |
1644 | break; | |
1645 | if (writebuf) | |
1646 | { | |
1647 | if (!entry) | |
1648 | { | |
1649 | /* Add a new entry. */ | |
1650 | entry | |
1651 | = (struct record_core_buf_entry *) | |
1652 | xmalloc | |
1653 | (sizeof (struct record_core_buf_entry)); | |
1654 | entry->p = p; | |
1655 | if (!bfd_malloc_and_get_section (p->bfd, | |
1656 | p->the_bfd_section, | |
1657 | &entry->buf)) | |
1658 | { | |
1659 | xfree (entry); | |
1660 | return 0; | |
1661 | } | |
1662 | entry->prev = record_core_buf_list; | |
1663 | record_core_buf_list = entry; | |
1664 | } | |
1665 | ||
1666 | memcpy (entry->buf + offset, writebuf, (size_t) len); | |
1667 | } | |
1668 | else | |
1669 | { | |
1670 | if (!entry) | |
1671 | return record_beneath_to_xfer_partial | |
1672 | (record_beneath_to_xfer_partial_ops, | |
1673 | object, annex, readbuf, writebuf, | |
1674 | offset, len); | |
1675 | ||
1676 | memcpy (readbuf, entry->buf + offset, (size_t) len); | |
1677 | } | |
1678 | ||
1679 | return len; | |
1680 | } | |
1681 | } | |
1682 | ||
1683 | return -1; | |
1684 | } | |
1685 | else | |
1686 | error (_("You can't do that without a process to debug.")); | |
1687 | } | |
1688 | ||
1689 | return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops, | |
1690 | object, annex, readbuf, writebuf, | |
1691 | offset, len); | |
1692 | } | |
1693 | ||
1694 | /* "to_insert_breakpoint" method for prec over corefile. */ | |
1695 | ||
1696 | static int | |
1697 | record_core_insert_breakpoint (struct gdbarch *gdbarch, | |
1698 | struct bp_target_info *bp_tgt) | |
1699 | { | |
1700 | return 0; | |
1701 | } | |
1702 | ||
1703 | /* "to_remove_breakpoint" method for prec over corefile. */ | |
1704 | ||
1705 | static int | |
1706 | record_core_remove_breakpoint (struct gdbarch *gdbarch, | |
1707 | struct bp_target_info *bp_tgt) | |
1708 | { | |
1709 | return 0; | |
1710 | } | |
1711 | ||
1712 | /* "to_has_execution" method for prec over corefile. */ | |
1713 | ||
1714 | int | |
1715 | record_core_has_execution (struct target_ops *ops) | |
1716 | { | |
1717 | return 1; | |
1718 | } | |
1719 | ||
1720 | static void | |
1721 | init_record_core_ops (void) | |
1722 | { | |
1723 | record_core_ops.to_shortname = "record_core"; | |
1724 | record_core_ops.to_longname = "Process record and replay target"; | |
1725 | record_core_ops.to_doc = | |
1726 | "Log program while executing and replay execution from log."; | |
1727 | record_core_ops.to_open = record_open; | |
1728 | record_core_ops.to_close = record_close; | |
1729 | record_core_ops.to_resume = record_core_resume; | |
1730 | record_core_ops.to_wait = record_wait; | |
1731 | record_core_ops.to_kill = record_core_kill; | |
1732 | record_core_ops.to_fetch_registers = record_core_fetch_registers; | |
1733 | record_core_ops.to_prepare_to_store = record_core_prepare_to_store; | |
1734 | record_core_ops.to_store_registers = record_core_store_registers; | |
1735 | record_core_ops.to_xfer_partial = record_core_xfer_partial; | |
1736 | record_core_ops.to_insert_breakpoint = record_core_insert_breakpoint; | |
1737 | record_core_ops.to_remove_breakpoint = record_core_remove_breakpoint; | |
1738 | record_core_ops.to_can_execute_reverse = record_can_execute_reverse; | |
1739 | record_core_ops.to_has_execution = record_core_has_execution; | |
1740 | record_core_ops.to_stratum = record_stratum; | |
1741 | record_core_ops.to_magic = OPS_MAGIC; | |
1742 | } | |
1743 | ||
6df67667 MS |
1744 | /* Implement "show record debug" command. */ |
1745 | ||
69d05d38 HZ |
1746 | static void |
1747 | show_record_debug (struct ui_file *file, int from_tty, | |
1748 | struct cmd_list_element *c, const char *value) | |
1749 | { | |
1750 | fprintf_filtered (file, _("Debugging of process record target is %s.\n"), | |
1751 | value); | |
1752 | } | |
1753 | ||
1754 | /* Alias for "target record". */ | |
1755 | ||
1756 | static void | |
1757 | cmd_record_start (char *args, int from_tty) | |
1758 | { | |
1759 | execute_command ("target record", from_tty); | |
1760 | } | |
1761 | ||
1762 | /* Truncate the record log from the present point | |
1763 | of replay until the end. */ | |
1764 | ||
1765 | static void | |
1766 | cmd_record_delete (char *args, int from_tty) | |
1767 | { | |
1768 | if (current_target.to_stratum == record_stratum) | |
1769 | { | |
1770 | if (RECORD_IS_REPLAY) | |
1771 | { | |
1772 | if (!from_tty || query (_("Delete the log from this point forward " | |
1773 | "and begin to record the running message " | |
1774 | "at current PC?"))) | |
61f75dd8 | 1775 | record_list_release_following (record_list); |
69d05d38 HZ |
1776 | } |
1777 | else | |
1778 | printf_unfiltered (_("Already at end of record list.\n")); | |
1779 | ||
1780 | } | |
1781 | else | |
1782 | printf_unfiltered (_("Process record is not started.\n")); | |
1783 | } | |
1784 | ||
6df67667 | 1785 | /* Implement the "stoprecord" or "record stop" command. */ |
69d05d38 HZ |
1786 | |
1787 | static void | |
1788 | cmd_record_stop (char *args, int from_tty) | |
1789 | { | |
1790 | if (current_target.to_stratum == record_stratum) | |
1791 | { | |
5d40bb85 | 1792 | unpush_target (&record_ops); |
b54295a7 MS |
1793 | printf_unfiltered (_("Process record is stopped and all execution " |
1794 | "logs are deleted.\n")); | |
69d05d38 HZ |
1795 | } |
1796 | else | |
1797 | printf_unfiltered (_("Process record is not started.\n")); | |
1798 | } | |
1799 | ||
1800 | /* Set upper limit of record log size. */ | |
1801 | ||
1802 | static void | |
1803 | set_record_insn_max_num (char *args, int from_tty, struct cmd_list_element *c) | |
1804 | { | |
1805 | if (record_insn_num > record_insn_max_num && record_insn_max_num) | |
1806 | { | |
265aad34 | 1807 | /* Count down record_insn_num while releasing records from list. */ |
69d05d38 | 1808 | while (record_insn_num > record_insn_max_num) |
265aad34 MS |
1809 | { |
1810 | record_list_release_first (); | |
1811 | record_insn_num--; | |
1812 | } | |
69d05d38 HZ |
1813 | } |
1814 | } | |
1815 | ||
69d05d38 HZ |
1816 | static struct cmd_list_element *record_cmdlist, *set_record_cmdlist, |
1817 | *show_record_cmdlist, *info_record_cmdlist; | |
1818 | ||
1819 | static void | |
1820 | set_record_command (char *args, int from_tty) | |
1821 | { | |
1822 | printf_unfiltered (_("\ | |
1823 | \"set record\" must be followed by an apporpriate subcommand.\n")); | |
1824 | help_list (set_record_cmdlist, "set record ", all_commands, gdb_stdout); | |
1825 | } | |
1826 | ||
1827 | static void | |
1828 | show_record_command (char *args, int from_tty) | |
1829 | { | |
1830 | cmd_show_list (show_record_cmdlist, from_tty, ""); | |
1831 | } | |
1832 | ||
b54295a7 MS |
1833 | /* Display some statistics about the execution log. */ |
1834 | ||
69d05d38 HZ |
1835 | static void |
1836 | info_record_command (char *args, int from_tty) | |
1837 | { | |
b54295a7 MS |
1838 | struct record_entry *p; |
1839 | ||
1840 | if (current_target.to_stratum == record_stratum) | |
1841 | { | |
1842 | if (RECORD_IS_REPLAY) | |
1843 | printf_filtered (_("Replay mode:\n")); | |
1844 | else | |
1845 | printf_filtered (_("Record mode:\n")); | |
1846 | ||
1847 | /* Find entry for first actual instruction in the log. */ | |
1848 | for (p = record_first.next; | |
1849 | p != NULL && p->type != record_end; | |
1850 | p = p->next) | |
1851 | ; | |
1852 | ||
1853 | /* Do we have a log at all? */ | |
1854 | if (p != NULL && p->type == record_end) | |
1855 | { | |
1856 | /* Display instruction number for first instruction in the log. */ | |
1857 | printf_filtered (_("Lowest recorded instruction number is %s.\n"), | |
1858 | pulongest (p->u.end.insn_num)); | |
1859 | ||
1860 | /* If in replay mode, display where we are in the log. */ | |
1861 | if (RECORD_IS_REPLAY) | |
1862 | printf_filtered (_("Current instruction number is %s.\n"), | |
1863 | pulongest (record_list->u.end.insn_num)); | |
1864 | ||
1865 | /* Display instruction number for last instruction in the log. */ | |
1866 | printf_filtered (_("Highest recorded instruction number is %s.\n"), | |
1867 | pulongest (record_insn_count)); | |
1868 | ||
1869 | /* Display log count. */ | |
1870 | printf_filtered (_("Log contains %d instructions.\n"), | |
1871 | record_insn_num); | |
1872 | } | |
1873 | else | |
1874 | { | |
1875 | printf_filtered (_("No instructions have been logged.\n")); | |
1876 | } | |
1877 | } | |
1878 | else | |
1879 | { | |
1880 | printf_filtered (_("target record is not active.\n")); | |
1881 | } | |
1882 | ||
1883 | /* Display max log size. */ | |
1884 | printf_filtered (_("Max logged instructions is %d.\n"), | |
1885 | record_insn_max_num); | |
69d05d38 HZ |
1886 | } |
1887 | ||
1888 | void | |
1889 | _initialize_record (void) | |
1890 | { | |
1891 | /* Init record_first. */ | |
1892 | record_first.prev = NULL; | |
1893 | record_first.next = NULL; | |
1894 | record_first.type = record_end; | |
1895 | ||
1896 | init_record_ops (); | |
1897 | add_target (&record_ops); | |
27699eea MS |
1898 | init_record_core_ops (); |
1899 | add_target (&record_core_ops); | |
69d05d38 HZ |
1900 | |
1901 | add_setshow_zinteger_cmd ("record", no_class, &record_debug, | |
1902 | _("Set debugging of record/replay feature."), | |
1903 | _("Show debugging of record/replay feature."), | |
1904 | _("When enabled, debugging output for " | |
1905 | "record/replay feature is displayed."), | |
1906 | NULL, show_record_debug, &setdebuglist, | |
1907 | &showdebuglist); | |
1908 | ||
1909 | add_prefix_cmd ("record", class_obscure, cmd_record_start, | |
1910 | _("Abbreviated form of \"target record\" command."), | |
1911 | &record_cmdlist, "record ", 0, &cmdlist); | |
1912 | add_com_alias ("rec", "record", class_obscure, 1); | |
1913 | add_prefix_cmd ("record", class_support, set_record_command, | |
1914 | _("Set record options"), &set_record_cmdlist, | |
1915 | "set record ", 0, &setlist); | |
1916 | add_alias_cmd ("rec", "record", class_obscure, 1, &setlist); | |
1917 | add_prefix_cmd ("record", class_support, show_record_command, | |
1918 | _("Show record options"), &show_record_cmdlist, | |
1919 | "show record ", 0, &showlist); | |
1920 | add_alias_cmd ("rec", "record", class_obscure, 1, &showlist); | |
1921 | add_prefix_cmd ("record", class_support, info_record_command, | |
1922 | _("Info record options"), &info_record_cmdlist, | |
1923 | "info record ", 0, &infolist); | |
1924 | add_alias_cmd ("rec", "record", class_obscure, 1, &infolist); | |
1925 | ||
1926 | ||
1927 | add_cmd ("delete", class_obscure, cmd_record_delete, | |
1928 | _("Delete the rest of execution log and start recording it anew."), | |
1929 | &record_cmdlist); | |
1930 | add_alias_cmd ("d", "delete", class_obscure, 1, &record_cmdlist); | |
1931 | add_alias_cmd ("del", "delete", class_obscure, 1, &record_cmdlist); | |
1932 | ||
1933 | add_cmd ("stop", class_obscure, cmd_record_stop, | |
1934 | _("Stop the record/replay target."), | |
1935 | &record_cmdlist); | |
1936 | add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist); | |
1937 | ||
1938 | /* Record instructions number limit command. */ | |
1939 | add_setshow_boolean_cmd ("stop-at-limit", no_class, | |
fda458ee | 1940 | &record_stop_at_limit, _("\ |
299a410e EZ |
1941 | Set whether record/replay stops when record/replay buffer becomes full."), _("\ |
1942 | Show whether record/replay stops when record/replay buffer becomes full."), _("\ | |
1943 | Default is ON.\n\ | |
1944 | When ON, if the record/replay buffer becomes full, ask user what to do.\n\ | |
1945 | When OFF, if the record/replay buffer becomes full,\n\ | |
1946 | delete the oldest recorded instruction to make room for each new one."), | |
fda458ee MS |
1947 | NULL, NULL, |
1948 | &set_record_cmdlist, &show_record_cmdlist); | |
191e1813 | 1949 | add_setshow_uinteger_cmd ("insn-number-max", no_class, |
69d05d38 HZ |
1950 | &record_insn_max_num, |
1951 | _("Set record/replay buffer limit."), | |
299a410e EZ |
1952 | _("Show record/replay buffer limit."), _("\ |
1953 | Set the maximum number of instructions to be stored in the\n\ | |
1954 | record/replay buffer. Zero means unlimited. Default is 200000."), | |
69d05d38 HZ |
1955 | set_record_insn_max_num, |
1956 | NULL, &set_record_cmdlist, &show_record_cmdlist); | |
69d05d38 | 1957 | } |