]>
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 PD |
146 | int nb_sns, i; |
147 | AioContext *aio_context; | |
148 | ||
149 | *snapshot_icount = -1; | |
150 | ||
c22d644c | 151 | bs = bdrv_all_find_vmstate_bs(NULL, false, NULL, NULL); |
f6baed3d PD |
152 | if (!bs) { |
153 | goto fail; | |
154 | } | |
155 | aio_context = bdrv_get_aio_context(bs); | |
156 | ||
157 | aio_context_acquire(aio_context); | |
158 | nb_sns = bdrv_snapshot_list(bs, &sn_tab); | |
159 | aio_context_release(aio_context); | |
160 | ||
161 | for (i = 0; i < nb_sns; i++) { | |
3d3e9b1f DB |
162 | rv = bdrv_all_has_snapshot(sn_tab[i].name, false, NULL, NULL); |
163 | if (rv < 0) | |
164 | goto fail; | |
165 | if (rv == 1) { | |
f6baed3d PD |
166 | if (sn_tab[i].icount != -1ULL |
167 | && sn_tab[i].icount <= icount | |
168 | && (!nearest || nearest->icount < sn_tab[i].icount)) { | |
169 | nearest = &sn_tab[i]; | |
170 | } | |
171 | } | |
172 | } | |
173 | if (nearest) { | |
174 | ret = g_strdup(nearest->name); | |
175 | *snapshot_icount = nearest->icount; | |
176 | } | |
177 | g_free(sn_tab); | |
178 | ||
179 | fail: | |
180 | return ret; | |
181 | } | |
182 | ||
183 | static void replay_seek(int64_t icount, QEMUTimerCB callback, Error **errp) | |
184 | { | |
185 | char *snapshot = NULL; | |
186 | int64_t snapshot_icount; | |
187 | ||
188 | if (replay_mode != REPLAY_MODE_PLAY) { | |
189 | error_setg(errp, "replay must be enabled to seek"); | |
190 | return; | |
191 | } | |
192 | ||
193 | snapshot = replay_find_nearest_snapshot(icount, &snapshot_icount); | |
194 | if (snapshot) { | |
195 | if (icount < replay_get_current_icount() | |
196 | || replay_get_current_icount() < snapshot_icount) { | |
197 | vm_stop(RUN_STATE_RESTORE_VM); | |
f1a9fcdd | 198 | load_snapshot(snapshot, NULL, false, NULL, errp); |
f6baed3d PD |
199 | } |
200 | g_free(snapshot); | |
201 | } | |
202 | if (replay_get_current_icount() <= icount) { | |
203 | replay_break(icount, callback, NULL); | |
204 | vm_start(); | |
205 | } else { | |
206 | error_setg(errp, "cannot seek to the specified instruction count"); | |
207 | } | |
208 | } | |
209 | ||
210 | void qmp_replay_seek(int64_t icount, Error **errp) | |
211 | { | |
212 | replay_seek(icount, replay_stop_vm, errp); | |
213 | } | |
214 | ||
215 | void hmp_replay_seek(Monitor *mon, const QDict *qdict) | |
216 | { | |
217 | int64_t icount = qdict_get_try_int(qdict, "icount", -1LL); | |
218 | Error *err = NULL; | |
219 | ||
220 | qmp_replay_seek(icount, &err); | |
221 | if (err) { | |
222 | error_report_err(err); | |
223 | return; | |
224 | } | |
225 | } | |
fda8458b PD |
226 | |
227 | static void replay_stop_vm_debug(void *opaque) | |
228 | { | |
229 | replay_is_debugging = false; | |
230 | vm_stop(RUN_STATE_DEBUG); | |
231 | replay_delete_break(); | |
232 | } | |
233 | ||
234 | bool replay_reverse_step(void) | |
235 | { | |
236 | Error *err = NULL; | |
237 | ||
238 | assert(replay_mode == REPLAY_MODE_PLAY); | |
239 | ||
240 | if (replay_get_current_icount() != 0) { | |
241 | replay_seek(replay_get_current_icount() - 1, | |
242 | replay_stop_vm_debug, &err); | |
243 | if (err) { | |
244 | error_free(err); | |
245 | return false; | |
246 | } | |
247 | replay_is_debugging = true; | |
248 | return true; | |
249 | } | |
250 | ||
251 | return false; | |
252 | } | |
cda38259 PD |
253 | |
254 | static void replay_continue_end(void) | |
255 | { | |
256 | replay_is_debugging = false; | |
257 | vm_stop(RUN_STATE_DEBUG); | |
258 | replay_delete_break(); | |
259 | } | |
260 | ||
261 | static void replay_continue_stop(void *opaque) | |
262 | { | |
263 | Error *err = NULL; | |
264 | if (replay_last_breakpoint != -1LL) { | |
265 | replay_seek(replay_last_breakpoint, replay_stop_vm_debug, &err); | |
266 | if (err) { | |
267 | error_free(err); | |
268 | replay_continue_end(); | |
269 | } | |
270 | return; | |
271 | } | |
272 | /* | |
273 | * No breakpoints since the last snapshot. | |
274 | * Find previous snapshot and try again. | |
275 | */ | |
276 | if (replay_last_snapshot != 0) { | |
277 | replay_seek(replay_last_snapshot - 1, replay_continue_stop, &err); | |
278 | if (err) { | |
279 | error_free(err); | |
280 | replay_continue_end(); | |
281 | } | |
282 | replay_last_snapshot = replay_get_current_icount(); | |
cda38259 PD |
283 | } else { |
284 | /* Seek to the very first step */ | |
285 | replay_seek(0, replay_stop_vm_debug, &err); | |
286 | if (err) { | |
287 | error_free(err); | |
288 | replay_continue_end(); | |
289 | } | |
cda38259 | 290 | } |
cda38259 PD |
291 | } |
292 | ||
293 | bool replay_reverse_continue(void) | |
294 | { | |
295 | Error *err = NULL; | |
296 | ||
297 | assert(replay_mode == REPLAY_MODE_PLAY); | |
298 | ||
299 | if (replay_get_current_icount() != 0) { | |
300 | replay_seek(replay_get_current_icount() - 1, | |
301 | replay_continue_stop, &err); | |
302 | if (err) { | |
303 | error_free(err); | |
304 | return false; | |
305 | } | |
306 | replay_last_breakpoint = -1LL; | |
307 | replay_is_debugging = true; | |
308 | replay_last_snapshot = replay_get_current_icount(); | |
309 | return true; | |
310 | } | |
311 | ||
312 | return false; | |
313 | } | |
314 | ||
315 | void replay_breakpoint(void) | |
316 | { | |
317 | assert(replay_mode == REPLAY_MODE_PLAY); | |
318 | replay_last_breakpoint = replay_get_current_icount(); | |
319 | } | |
56357d80 PD |
320 | |
321 | void replay_gdb_attached(void) | |
322 | { | |
323 | /* | |
324 | * Create VM snapshot on temporary overlay to allow reverse | |
325 | * debugging even if snapshots were not enabled. | |
326 | */ | |
327 | if (replay_mode == REPLAY_MODE_PLAY | |
328 | && !replay_snapshot) { | |
f1a9fcdd | 329 | if (!save_snapshot("start_debugging", true, NULL, false, NULL, NULL)) { |
56357d80 PD |
330 | /* Can't create the snapshot. Continue conventional debugging. */ |
331 | } | |
332 | } | |
333 | } |