]>
Commit | Line | Data |
---|---|---|
e3b09ad2 PD |
1 | /* |
2 | * replay-debugging.c | |
3 | * | |
4 | * Copyright (c) 2010-2020 Institute for System Programming | |
5 | * of the Russian Academy of Sciences. | |
6 | * | |
7 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
8 | * See the COPYING file in the top-level directory. | |
9 | * | |
10 | */ | |
11 | ||
12 | #include "qemu/osdep.h" | |
13 | #include "qapi/error.h" | |
14 | #include "sysemu/replay.h" | |
e7510671 | 15 | #include "sysemu/runstate.h" |
e3b09ad2 PD |
16 | #include "replay-internal.h" |
17 | #include "monitor/hmp.h" | |
18 | #include "monitor/monitor.h" | |
19 | #include "qapi/qapi-commands-replay.h" | |
e7510671 PD |
20 | #include "qapi/qmp/qdict.h" |
21 | #include "qemu/timer.h" | |
f6baed3d PD |
22 | #include "block/snapshot.h" |
23 | #include "migration/snapshot.h" | |
e3b09ad2 | 24 | |
fda8458b | 25 | static bool replay_is_debugging; |
cda38259 PD |
26 | static int64_t replay_last_breakpoint; |
27 | static int64_t replay_last_snapshot; | |
fda8458b PD |
28 | |
29 | bool replay_running_debug(void) | |
30 | { | |
31 | return replay_is_debugging; | |
32 | } | |
33 | ||
e3b09ad2 PD |
34 | void hmp_info_replay(Monitor *mon, const QDict *qdict) |
35 | { | |
36 | if (replay_mode == REPLAY_MODE_NONE) { | |
37 | monitor_printf(mon, "Record/replay is not active\n"); | |
38 | } else { | |
39 | monitor_printf(mon, | |
40 | "%s execution '%s': instruction count = %"PRId64"\n", | |
41 | replay_mode == REPLAY_MODE_RECORD ? "Recording" : "Replaying", | |
42 | replay_get_filename(), replay_get_current_icount()); | |
43 | } | |
44 | } | |
45 | ||
46 | ReplayInfo *qmp_query_replay(Error **errp) | |
47 | { | |
48 | ReplayInfo *retval = g_new0(ReplayInfo, 1); | |
49 | ||
50 | retval->mode = replay_mode; | |
51 | if (replay_get_filename()) { | |
52 | retval->filename = g_strdup(replay_get_filename()); | |
e3b09ad2 PD |
53 | } |
54 | retval->icount = replay_get_current_icount(); | |
55 | return retval; | |
56 | } | |
e7510671 PD |
57 | |
58 | static void replay_break(uint64_t icount, QEMUTimerCB callback, void *opaque) | |
59 | { | |
60 | assert(replay_mode == REPLAY_MODE_PLAY); | |
61 | assert(replay_mutex_locked()); | |
62 | assert(replay_break_icount >= replay_get_current_icount()); | |
63 | assert(callback); | |
64 | ||
65 | replay_break_icount = icount; | |
66 | ||
67 | if (replay_break_timer) { | |
68 | timer_del(replay_break_timer); | |
69 | } | |
70 | replay_break_timer = timer_new_ns(QEMU_CLOCK_REALTIME, | |
71 | callback, opaque); | |
72 | } | |
73 | ||
74 | static void replay_delete_break(void) | |
75 | { | |
76 | assert(replay_mode == REPLAY_MODE_PLAY); | |
77 | assert(replay_mutex_locked()); | |
78 | ||
79 | if (replay_break_timer) { | |
e7510671 PD |
80 | timer_free(replay_break_timer); |
81 | replay_break_timer = NULL; | |
82 | } | |
83 | replay_break_icount = -1ULL; | |
84 | } | |
85 | ||
86 | static void replay_stop_vm(void *opaque) | |
87 | { | |
88 | vm_stop(RUN_STATE_PAUSED); | |
89 | replay_delete_break(); | |
90 | } | |
91 | ||
92 | void qmp_replay_break(int64_t icount, Error **errp) | |
93 | { | |
94 | if (replay_mode == REPLAY_MODE_PLAY) { | |
95 | if (icount >= replay_get_current_icount()) { | |
96 | replay_break(icount, replay_stop_vm, NULL); | |
97 | } else { | |
98 | error_setg(errp, | |
99 | "cannot set breakpoint at the instruction in the past"); | |
100 | } | |
101 | } else { | |
102 | error_setg(errp, "setting the breakpoint is allowed only in play mode"); | |
103 | } | |
104 | } | |
105 | ||
106 | void hmp_replay_break(Monitor *mon, const QDict *qdict) | |
107 | { | |
108 | int64_t icount = qdict_get_try_int(qdict, "icount", -1LL); | |
109 | Error *err = NULL; | |
110 | ||
111 | qmp_replay_break(icount, &err); | |
112 | if (err) { | |
113 | error_report_err(err); | |
114 | return; | |
115 | } | |
116 | } | |
117 | ||
118 | void qmp_replay_delete_break(Error **errp) | |
119 | { | |
120 | if (replay_mode == REPLAY_MODE_PLAY) { | |
121 | replay_delete_break(); | |
122 | } else { | |
123 | error_setg(errp, "replay breakpoints are allowed only in play mode"); | |
124 | } | |
125 | } | |
126 | ||
127 | void hmp_replay_delete_break(Monitor *mon, const QDict *qdict) | |
128 | { | |
129 | Error *err = NULL; | |
130 | ||
131 | qmp_replay_delete_break(&err); | |
132 | if (err) { | |
133 | error_report_err(err); | |
134 | return; | |
135 | } | |
136 | } | |
f6baed3d PD |
137 | |
138 | static char *replay_find_nearest_snapshot(int64_t icount, | |
139 | int64_t *snapshot_icount) | |
140 | { | |
141 | BlockDriverState *bs; | |
142 | QEMUSnapshotInfo *sn_tab; | |
143 | QEMUSnapshotInfo *nearest = NULL; | |
144 | char *ret = NULL; | |
3d3e9b1f | 145 | int rv; |
f6baed3d | 146 | int nb_sns, i; |
f6baed3d PD |
147 | |
148 | *snapshot_icount = -1; | |
149 | ||
c22d644c | 150 | bs = bdrv_all_find_vmstate_bs(NULL, false, NULL, NULL); |
f6baed3d PD |
151 | if (!bs) { |
152 | goto fail; | |
153 | } | |
f6baed3d | 154 | |
f6baed3d | 155 | nb_sns = bdrv_snapshot_list(bs, &sn_tab); |
f6baed3d PD |
156 | |
157 | for (i = 0; i < nb_sns; i++) { | |
3d3e9b1f DB |
158 | rv = bdrv_all_has_snapshot(sn_tab[i].name, false, NULL, NULL); |
159 | if (rv < 0) | |
160 | goto fail; | |
161 | if (rv == 1) { | |
f6baed3d PD |
162 | if (sn_tab[i].icount != -1ULL |
163 | && sn_tab[i].icount <= icount | |
164 | && (!nearest || nearest->icount < sn_tab[i].icount)) { | |
165 | nearest = &sn_tab[i]; | |
166 | } | |
167 | } | |
168 | } | |
169 | if (nearest) { | |
170 | ret = g_strdup(nearest->name); | |
171 | *snapshot_icount = nearest->icount; | |
172 | } | |
173 | g_free(sn_tab); | |
174 | ||
175 | fail: | |
176 | return ret; | |
177 | } | |
178 | ||
179 | static void replay_seek(int64_t icount, QEMUTimerCB callback, Error **errp) | |
180 | { | |
181 | char *snapshot = NULL; | |
182 | int64_t snapshot_icount; | |
183 | ||
184 | if (replay_mode != REPLAY_MODE_PLAY) { | |
185 | error_setg(errp, "replay must be enabled to seek"); | |
186 | return; | |
187 | } | |
188 | ||
189 | snapshot = replay_find_nearest_snapshot(icount, &snapshot_icount); | |
190 | if (snapshot) { | |
191 | if (icount < replay_get_current_icount() | |
192 | || replay_get_current_icount() < snapshot_icount) { | |
193 | vm_stop(RUN_STATE_RESTORE_VM); | |
f1a9fcdd | 194 | load_snapshot(snapshot, NULL, false, NULL, errp); |
f6baed3d PD |
195 | } |
196 | g_free(snapshot); | |
197 | } | |
198 | if (replay_get_current_icount() <= icount) { | |
199 | replay_break(icount, callback, NULL); | |
200 | vm_start(); | |
201 | } else { | |
202 | error_setg(errp, "cannot seek to the specified instruction count"); | |
203 | } | |
204 | } | |
205 | ||
206 | void qmp_replay_seek(int64_t icount, Error **errp) | |
207 | { | |
208 | replay_seek(icount, replay_stop_vm, errp); | |
209 | } | |
210 | ||
211 | void hmp_replay_seek(Monitor *mon, const QDict *qdict) | |
212 | { | |
213 | int64_t icount = qdict_get_try_int(qdict, "icount", -1LL); | |
214 | Error *err = NULL; | |
215 | ||
216 | qmp_replay_seek(icount, &err); | |
217 | if (err) { | |
218 | error_report_err(err); | |
219 | return; | |
220 | } | |
221 | } | |
fda8458b PD |
222 | |
223 | static void replay_stop_vm_debug(void *opaque) | |
224 | { | |
225 | replay_is_debugging = false; | |
226 | vm_stop(RUN_STATE_DEBUG); | |
227 | replay_delete_break(); | |
228 | } | |
229 | ||
230 | bool replay_reverse_step(void) | |
231 | { | |
232 | Error *err = NULL; | |
233 | ||
234 | assert(replay_mode == REPLAY_MODE_PLAY); | |
235 | ||
236 | if (replay_get_current_icount() != 0) { | |
237 | replay_seek(replay_get_current_icount() - 1, | |
238 | replay_stop_vm_debug, &err); | |
239 | if (err) { | |
240 | error_free(err); | |
241 | return false; | |
242 | } | |
243 | replay_is_debugging = true; | |
244 | return true; | |
245 | } | |
246 | ||
247 | return false; | |
248 | } | |
cda38259 PD |
249 | |
250 | static void replay_continue_end(void) | |
251 | { | |
252 | replay_is_debugging = false; | |
253 | vm_stop(RUN_STATE_DEBUG); | |
254 | replay_delete_break(); | |
255 | } | |
256 | ||
257 | static void replay_continue_stop(void *opaque) | |
258 | { | |
259 | Error *err = NULL; | |
260 | if (replay_last_breakpoint != -1LL) { | |
261 | replay_seek(replay_last_breakpoint, replay_stop_vm_debug, &err); | |
262 | if (err) { | |
263 | error_free(err); | |
264 | replay_continue_end(); | |
265 | } | |
266 | return; | |
267 | } | |
268 | /* | |
269 | * No breakpoints since the last snapshot. | |
270 | * Find previous snapshot and try again. | |
271 | */ | |
272 | if (replay_last_snapshot != 0) { | |
273 | replay_seek(replay_last_snapshot - 1, replay_continue_stop, &err); | |
274 | if (err) { | |
275 | error_free(err); | |
276 | replay_continue_end(); | |
277 | } | |
278 | replay_last_snapshot = replay_get_current_icount(); | |
cda38259 PD |
279 | } else { |
280 | /* Seek to the very first step */ | |
281 | replay_seek(0, replay_stop_vm_debug, &err); | |
282 | if (err) { | |
283 | error_free(err); | |
284 | replay_continue_end(); | |
285 | } | |
cda38259 | 286 | } |
cda38259 PD |
287 | } |
288 | ||
289 | bool replay_reverse_continue(void) | |
290 | { | |
291 | Error *err = NULL; | |
292 | ||
293 | assert(replay_mode == REPLAY_MODE_PLAY); | |
294 | ||
295 | if (replay_get_current_icount() != 0) { | |
296 | replay_seek(replay_get_current_icount() - 1, | |
297 | replay_continue_stop, &err); | |
298 | if (err) { | |
299 | error_free(err); | |
300 | return false; | |
301 | } | |
302 | replay_last_breakpoint = -1LL; | |
303 | replay_is_debugging = true; | |
304 | replay_last_snapshot = replay_get_current_icount(); | |
305 | return true; | |
306 | } | |
307 | ||
308 | return false; | |
309 | } | |
310 | ||
311 | void replay_breakpoint(void) | |
312 | { | |
313 | assert(replay_mode == REPLAY_MODE_PLAY); | |
314 | replay_last_breakpoint = replay_get_current_icount(); | |
315 | } | |
56357d80 PD |
316 | |
317 | void replay_gdb_attached(void) | |
318 | { | |
319 | /* | |
320 | * Create VM snapshot on temporary overlay to allow reverse | |
321 | * debugging even if snapshots were not enabled. | |
322 | */ | |
323 | if (replay_mode == REPLAY_MODE_PLAY | |
324 | && !replay_snapshot) { | |
f1a9fcdd | 325 | if (!save_snapshot("start_debugging", true, NULL, false, NULL, NULL)) { |
56357d80 PD |
326 | /* Can't create the snapshot. Continue conventional debugging. */ |
327 | } | |
328 | } | |
329 | } |