]>
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()); | |
53 | retval->has_filename = true; | |
54 | } | |
55 | retval->icount = replay_get_current_icount(); | |
56 | return retval; | |
57 | } | |
e7510671 PD |
58 | |
59 | static void replay_break(uint64_t icount, QEMUTimerCB callback, void *opaque) | |
60 | { | |
61 | assert(replay_mode == REPLAY_MODE_PLAY); | |
62 | assert(replay_mutex_locked()); | |
63 | assert(replay_break_icount >= replay_get_current_icount()); | |
64 | assert(callback); | |
65 | ||
66 | replay_break_icount = icount; | |
67 | ||
68 | if (replay_break_timer) { | |
69 | timer_del(replay_break_timer); | |
70 | } | |
71 | replay_break_timer = timer_new_ns(QEMU_CLOCK_REALTIME, | |
72 | callback, opaque); | |
73 | } | |
74 | ||
75 | static void replay_delete_break(void) | |
76 | { | |
77 | assert(replay_mode == REPLAY_MODE_PLAY); | |
78 | assert(replay_mutex_locked()); | |
79 | ||
80 | if (replay_break_timer) { | |
81 | timer_del(replay_break_timer); | |
82 | timer_free(replay_break_timer); | |
83 | replay_break_timer = NULL; | |
84 | } | |
85 | replay_break_icount = -1ULL; | |
86 | } | |
87 | ||
88 | static void replay_stop_vm(void *opaque) | |
89 | { | |
90 | vm_stop(RUN_STATE_PAUSED); | |
91 | replay_delete_break(); | |
92 | } | |
93 | ||
94 | void qmp_replay_break(int64_t icount, Error **errp) | |
95 | { | |
96 | if (replay_mode == REPLAY_MODE_PLAY) { | |
97 | if (icount >= replay_get_current_icount()) { | |
98 | replay_break(icount, replay_stop_vm, NULL); | |
99 | } else { | |
100 | error_setg(errp, | |
101 | "cannot set breakpoint at the instruction in the past"); | |
102 | } | |
103 | } else { | |
104 | error_setg(errp, "setting the breakpoint is allowed only in play mode"); | |
105 | } | |
106 | } | |
107 | ||
108 | void hmp_replay_break(Monitor *mon, const QDict *qdict) | |
109 | { | |
110 | int64_t icount = qdict_get_try_int(qdict, "icount", -1LL); | |
111 | Error *err = NULL; | |
112 | ||
113 | qmp_replay_break(icount, &err); | |
114 | if (err) { | |
115 | error_report_err(err); | |
116 | return; | |
117 | } | |
118 | } | |
119 | ||
120 | void qmp_replay_delete_break(Error **errp) | |
121 | { | |
122 | if (replay_mode == REPLAY_MODE_PLAY) { | |
123 | replay_delete_break(); | |
124 | } else { | |
125 | error_setg(errp, "replay breakpoints are allowed only in play mode"); | |
126 | } | |
127 | } | |
128 | ||
129 | void hmp_replay_delete_break(Monitor *mon, const QDict *qdict) | |
130 | { | |
131 | Error *err = NULL; | |
132 | ||
133 | qmp_replay_delete_break(&err); | |
134 | if (err) { | |
135 | error_report_err(err); | |
136 | return; | |
137 | } | |
138 | } | |
f6baed3d PD |
139 | |
140 | static char *replay_find_nearest_snapshot(int64_t icount, | |
141 | int64_t *snapshot_icount) | |
142 | { | |
143 | BlockDriverState *bs; | |
144 | QEMUSnapshotInfo *sn_tab; | |
145 | QEMUSnapshotInfo *nearest = NULL; | |
146 | char *ret = NULL; | |
147 | int nb_sns, i; | |
148 | AioContext *aio_context; | |
149 | ||
150 | *snapshot_icount = -1; | |
151 | ||
152 | bs = bdrv_all_find_vmstate_bs(); | |
153 | if (!bs) { | |
154 | goto fail; | |
155 | } | |
156 | aio_context = bdrv_get_aio_context(bs); | |
157 | ||
158 | aio_context_acquire(aio_context); | |
159 | nb_sns = bdrv_snapshot_list(bs, &sn_tab); | |
160 | aio_context_release(aio_context); | |
161 | ||
162 | for (i = 0; i < nb_sns; i++) { | |
163 | if (bdrv_all_find_snapshot(sn_tab[i].name, &bs) == 0) { | |
164 | if (sn_tab[i].icount != -1ULL | |
165 | && sn_tab[i].icount <= icount | |
166 | && (!nearest || nearest->icount < sn_tab[i].icount)) { | |
167 | nearest = &sn_tab[i]; | |
168 | } | |
169 | } | |
170 | } | |
171 | if (nearest) { | |
172 | ret = g_strdup(nearest->name); | |
173 | *snapshot_icount = nearest->icount; | |
174 | } | |
175 | g_free(sn_tab); | |
176 | ||
177 | fail: | |
178 | return ret; | |
179 | } | |
180 | ||
181 | static void replay_seek(int64_t icount, QEMUTimerCB callback, Error **errp) | |
182 | { | |
183 | char *snapshot = NULL; | |
184 | int64_t snapshot_icount; | |
185 | ||
186 | if (replay_mode != REPLAY_MODE_PLAY) { | |
187 | error_setg(errp, "replay must be enabled to seek"); | |
188 | return; | |
189 | } | |
190 | ||
191 | snapshot = replay_find_nearest_snapshot(icount, &snapshot_icount); | |
192 | if (snapshot) { | |
193 | if (icount < replay_get_current_icount() | |
194 | || replay_get_current_icount() < snapshot_icount) { | |
195 | vm_stop(RUN_STATE_RESTORE_VM); | |
196 | load_snapshot(snapshot, errp); | |
197 | } | |
198 | g_free(snapshot); | |
199 | } | |
200 | if (replay_get_current_icount() <= icount) { | |
201 | replay_break(icount, callback, NULL); | |
202 | vm_start(); | |
203 | } else { | |
204 | error_setg(errp, "cannot seek to the specified instruction count"); | |
205 | } | |
206 | } | |
207 | ||
208 | void qmp_replay_seek(int64_t icount, Error **errp) | |
209 | { | |
210 | replay_seek(icount, replay_stop_vm, errp); | |
211 | } | |
212 | ||
213 | void hmp_replay_seek(Monitor *mon, const QDict *qdict) | |
214 | { | |
215 | int64_t icount = qdict_get_try_int(qdict, "icount", -1LL); | |
216 | Error *err = NULL; | |
217 | ||
218 | qmp_replay_seek(icount, &err); | |
219 | if (err) { | |
220 | error_report_err(err); | |
221 | return; | |
222 | } | |
223 | } | |
fda8458b PD |
224 | |
225 | static void replay_stop_vm_debug(void *opaque) | |
226 | { | |
227 | replay_is_debugging = false; | |
228 | vm_stop(RUN_STATE_DEBUG); | |
229 | replay_delete_break(); | |
230 | } | |
231 | ||
232 | bool replay_reverse_step(void) | |
233 | { | |
234 | Error *err = NULL; | |
235 | ||
236 | assert(replay_mode == REPLAY_MODE_PLAY); | |
237 | ||
238 | if (replay_get_current_icount() != 0) { | |
239 | replay_seek(replay_get_current_icount() - 1, | |
240 | replay_stop_vm_debug, &err); | |
241 | if (err) { | |
242 | error_free(err); | |
243 | return false; | |
244 | } | |
245 | replay_is_debugging = true; | |
246 | return true; | |
247 | } | |
248 | ||
249 | return false; | |
250 | } | |
cda38259 PD |
251 | |
252 | static void replay_continue_end(void) | |
253 | { | |
254 | replay_is_debugging = false; | |
255 | vm_stop(RUN_STATE_DEBUG); | |
256 | replay_delete_break(); | |
257 | } | |
258 | ||
259 | static void replay_continue_stop(void *opaque) | |
260 | { | |
261 | Error *err = NULL; | |
262 | if (replay_last_breakpoint != -1LL) { | |
263 | replay_seek(replay_last_breakpoint, replay_stop_vm_debug, &err); | |
264 | if (err) { | |
265 | error_free(err); | |
266 | replay_continue_end(); | |
267 | } | |
268 | return; | |
269 | } | |
270 | /* | |
271 | * No breakpoints since the last snapshot. | |
272 | * Find previous snapshot and try again. | |
273 | */ | |
274 | if (replay_last_snapshot != 0) { | |
275 | replay_seek(replay_last_snapshot - 1, replay_continue_stop, &err); | |
276 | if (err) { | |
277 | error_free(err); | |
278 | replay_continue_end(); | |
279 | } | |
280 | replay_last_snapshot = replay_get_current_icount(); | |
281 | return; | |
282 | } else { | |
283 | /* Seek to the very first step */ | |
284 | replay_seek(0, replay_stop_vm_debug, &err); | |
285 | if (err) { | |
286 | error_free(err); | |
287 | replay_continue_end(); | |
288 | } | |
289 | return; | |
290 | } | |
291 | replay_continue_end(); | |
292 | } | |
293 | ||
294 | bool replay_reverse_continue(void) | |
295 | { | |
296 | Error *err = NULL; | |
297 | ||
298 | assert(replay_mode == REPLAY_MODE_PLAY); | |
299 | ||
300 | if (replay_get_current_icount() != 0) { | |
301 | replay_seek(replay_get_current_icount() - 1, | |
302 | replay_continue_stop, &err); | |
303 | if (err) { | |
304 | error_free(err); | |
305 | return false; | |
306 | } | |
307 | replay_last_breakpoint = -1LL; | |
308 | replay_is_debugging = true; | |
309 | replay_last_snapshot = replay_get_current_icount(); | |
310 | return true; | |
311 | } | |
312 | ||
313 | return false; | |
314 | } | |
315 | ||
316 | void replay_breakpoint(void) | |
317 | { | |
318 | assert(replay_mode == REPLAY_MODE_PLAY); | |
319 | replay_last_breakpoint = replay_get_current_icount(); | |
320 | } |