xref: /openbmc/qemu/replay/replay-events.c (revision ee312992)
1c0c071d0SPavel Dovgalyuk /*
2c0c071d0SPavel Dovgalyuk  * replay-events.c
3c0c071d0SPavel Dovgalyuk  *
4c0c071d0SPavel Dovgalyuk  * Copyright (c) 2010-2015 Institute for System Programming
5c0c071d0SPavel Dovgalyuk  *                         of the Russian Academy of Sciences.
6c0c071d0SPavel Dovgalyuk  *
7c0c071d0SPavel Dovgalyuk  * This work is licensed under the terms of the GNU GPL, version 2 or later.
8c0c071d0SPavel Dovgalyuk  * See the COPYING file in the top-level directory.
9c0c071d0SPavel Dovgalyuk  *
10c0c071d0SPavel Dovgalyuk  */
11c0c071d0SPavel Dovgalyuk 
12c0c071d0SPavel Dovgalyuk #include "qemu-common.h"
13c0c071d0SPavel Dovgalyuk #include "qemu/error-report.h"
14c0c071d0SPavel Dovgalyuk #include "sysemu/replay.h"
15c0c071d0SPavel Dovgalyuk #include "replay-internal.h"
168a354bd9SPavel Dovgalyuk #include "block/aio.h"
17*ee312992SPavel Dovgalyuk #include "ui/input.h"
18c0c071d0SPavel Dovgalyuk 
19c0c071d0SPavel Dovgalyuk typedef struct Event {
20c0c071d0SPavel Dovgalyuk     ReplayAsyncEventKind event_kind;
21c0c071d0SPavel Dovgalyuk     void *opaque;
22c0c071d0SPavel Dovgalyuk     void *opaque2;
23c0c071d0SPavel Dovgalyuk     uint64_t id;
24c0c071d0SPavel Dovgalyuk 
25c0c071d0SPavel Dovgalyuk     QTAILQ_ENTRY(Event) events;
26c0c071d0SPavel Dovgalyuk } Event;
27c0c071d0SPavel Dovgalyuk 
28c0c071d0SPavel Dovgalyuk static QTAILQ_HEAD(, Event) events_list = QTAILQ_HEAD_INITIALIZER(events_list);
29c0c071d0SPavel Dovgalyuk static unsigned int read_event_kind = -1;
30c0c071d0SPavel Dovgalyuk static uint64_t read_id = -1;
31c0c071d0SPavel Dovgalyuk static int read_checkpoint = -1;
32c0c071d0SPavel Dovgalyuk 
33c0c071d0SPavel Dovgalyuk static bool events_enabled;
34c0c071d0SPavel Dovgalyuk 
35c0c071d0SPavel Dovgalyuk /* Functions */
36c0c071d0SPavel Dovgalyuk 
37c0c071d0SPavel Dovgalyuk static void replay_run_event(Event *event)
38c0c071d0SPavel Dovgalyuk {
39c0c071d0SPavel Dovgalyuk     switch (event->event_kind) {
408a354bd9SPavel Dovgalyuk     case REPLAY_ASYNC_EVENT_BH:
418a354bd9SPavel Dovgalyuk         aio_bh_call(event->opaque);
428a354bd9SPavel Dovgalyuk         break;
43*ee312992SPavel Dovgalyuk     case REPLAY_ASYNC_EVENT_INPUT:
44*ee312992SPavel Dovgalyuk         qemu_input_event_send_impl(NULL, (InputEvent *)event->opaque);
45*ee312992SPavel Dovgalyuk         qapi_free_InputEvent((InputEvent *)event->opaque);
46*ee312992SPavel Dovgalyuk         break;
47*ee312992SPavel Dovgalyuk     case REPLAY_ASYNC_EVENT_INPUT_SYNC:
48*ee312992SPavel Dovgalyuk         qemu_input_event_sync_impl();
49*ee312992SPavel Dovgalyuk         break;
50c0c071d0SPavel Dovgalyuk     default:
51c0c071d0SPavel Dovgalyuk         error_report("Replay: invalid async event ID (%d) in the queue",
52c0c071d0SPavel Dovgalyuk                     event->event_kind);
53c0c071d0SPavel Dovgalyuk         exit(1);
54c0c071d0SPavel Dovgalyuk         break;
55c0c071d0SPavel Dovgalyuk     }
56c0c071d0SPavel Dovgalyuk }
57c0c071d0SPavel Dovgalyuk 
58c0c071d0SPavel Dovgalyuk void replay_enable_events(void)
59c0c071d0SPavel Dovgalyuk {
60c0c071d0SPavel Dovgalyuk     events_enabled = true;
61c0c071d0SPavel Dovgalyuk }
62c0c071d0SPavel Dovgalyuk 
63c0c071d0SPavel Dovgalyuk bool replay_has_events(void)
64c0c071d0SPavel Dovgalyuk {
65c0c071d0SPavel Dovgalyuk     return !QTAILQ_EMPTY(&events_list);
66c0c071d0SPavel Dovgalyuk }
67c0c071d0SPavel Dovgalyuk 
68c0c071d0SPavel Dovgalyuk void replay_flush_events(void)
69c0c071d0SPavel Dovgalyuk {
70c0c071d0SPavel Dovgalyuk     replay_mutex_lock();
71c0c071d0SPavel Dovgalyuk     while (!QTAILQ_EMPTY(&events_list)) {
72c0c071d0SPavel Dovgalyuk         Event *event = QTAILQ_FIRST(&events_list);
73c0c071d0SPavel Dovgalyuk         replay_mutex_unlock();
74c0c071d0SPavel Dovgalyuk         replay_run_event(event);
75c0c071d0SPavel Dovgalyuk         replay_mutex_lock();
76c0c071d0SPavel Dovgalyuk         QTAILQ_REMOVE(&events_list, event, events);
77c0c071d0SPavel Dovgalyuk         g_free(event);
78c0c071d0SPavel Dovgalyuk     }
79c0c071d0SPavel Dovgalyuk     replay_mutex_unlock();
80c0c071d0SPavel Dovgalyuk }
81c0c071d0SPavel Dovgalyuk 
82c0c071d0SPavel Dovgalyuk void replay_disable_events(void)
83c0c071d0SPavel Dovgalyuk {
84c0c071d0SPavel Dovgalyuk     if (replay_mode != REPLAY_MODE_NONE) {
85c0c071d0SPavel Dovgalyuk         events_enabled = false;
86c0c071d0SPavel Dovgalyuk         /* Flush events queue before waiting of completion */
87c0c071d0SPavel Dovgalyuk         replay_flush_events();
88c0c071d0SPavel Dovgalyuk     }
89c0c071d0SPavel Dovgalyuk }
90c0c071d0SPavel Dovgalyuk 
91c0c071d0SPavel Dovgalyuk void replay_clear_events(void)
92c0c071d0SPavel Dovgalyuk {
93c0c071d0SPavel Dovgalyuk     replay_mutex_lock();
94c0c071d0SPavel Dovgalyuk     while (!QTAILQ_EMPTY(&events_list)) {
95c0c071d0SPavel Dovgalyuk         Event *event = QTAILQ_FIRST(&events_list);
96c0c071d0SPavel Dovgalyuk         QTAILQ_REMOVE(&events_list, event, events);
97c0c071d0SPavel Dovgalyuk 
98c0c071d0SPavel Dovgalyuk         g_free(event);
99c0c071d0SPavel Dovgalyuk     }
100c0c071d0SPavel Dovgalyuk     replay_mutex_unlock();
101c0c071d0SPavel Dovgalyuk }
102c0c071d0SPavel Dovgalyuk 
103c0c071d0SPavel Dovgalyuk /*! Adds specified async event to the queue */
104c0c071d0SPavel Dovgalyuk static void replay_add_event(ReplayAsyncEventKind event_kind,
105c0c071d0SPavel Dovgalyuk                              void *opaque,
106c0c071d0SPavel Dovgalyuk                              void *opaque2, uint64_t id)
107c0c071d0SPavel Dovgalyuk {
108c0c071d0SPavel Dovgalyuk     assert(event_kind < REPLAY_ASYNC_COUNT);
109c0c071d0SPavel Dovgalyuk 
110c0c071d0SPavel Dovgalyuk     if (!replay_file || replay_mode == REPLAY_MODE_NONE
111c0c071d0SPavel Dovgalyuk         || !events_enabled) {
112c0c071d0SPavel Dovgalyuk         Event e;
113c0c071d0SPavel Dovgalyuk         e.event_kind = event_kind;
114c0c071d0SPavel Dovgalyuk         e.opaque = opaque;
115c0c071d0SPavel Dovgalyuk         e.opaque2 = opaque2;
116c0c071d0SPavel Dovgalyuk         e.id = id;
117c0c071d0SPavel Dovgalyuk         replay_run_event(&e);
118c0c071d0SPavel Dovgalyuk         return;
119c0c071d0SPavel Dovgalyuk     }
120c0c071d0SPavel Dovgalyuk 
121c0c071d0SPavel Dovgalyuk     Event *event = g_malloc0(sizeof(Event));
122c0c071d0SPavel Dovgalyuk     event->event_kind = event_kind;
123c0c071d0SPavel Dovgalyuk     event->opaque = opaque;
124c0c071d0SPavel Dovgalyuk     event->opaque2 = opaque2;
125c0c071d0SPavel Dovgalyuk     event->id = id;
126c0c071d0SPavel Dovgalyuk 
127c0c071d0SPavel Dovgalyuk     replay_mutex_lock();
128c0c071d0SPavel Dovgalyuk     QTAILQ_INSERT_TAIL(&events_list, event, events);
129c0c071d0SPavel Dovgalyuk     replay_mutex_unlock();
130c0c071d0SPavel Dovgalyuk }
1318a354bd9SPavel Dovgalyuk 
1328a354bd9SPavel Dovgalyuk void replay_bh_schedule_event(QEMUBH *bh)
1338a354bd9SPavel Dovgalyuk {
1348a354bd9SPavel Dovgalyuk     if (replay_mode != REPLAY_MODE_NONE) {
1358a354bd9SPavel Dovgalyuk         uint64_t id = replay_get_current_step();
1368a354bd9SPavel Dovgalyuk         replay_add_event(REPLAY_ASYNC_EVENT_BH, bh, NULL, id);
1378a354bd9SPavel Dovgalyuk     } else {
1388a354bd9SPavel Dovgalyuk         qemu_bh_schedule(bh);
1398a354bd9SPavel Dovgalyuk     }
1408a354bd9SPavel Dovgalyuk }
141c0c071d0SPavel Dovgalyuk 
142*ee312992SPavel Dovgalyuk void replay_add_input_event(struct InputEvent *event)
143*ee312992SPavel Dovgalyuk {
144*ee312992SPavel Dovgalyuk     replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0);
145*ee312992SPavel Dovgalyuk }
146*ee312992SPavel Dovgalyuk 
147*ee312992SPavel Dovgalyuk void replay_add_input_sync_event(void)
148*ee312992SPavel Dovgalyuk {
149*ee312992SPavel Dovgalyuk     replay_add_event(REPLAY_ASYNC_EVENT_INPUT_SYNC, NULL, NULL, 0);
150*ee312992SPavel Dovgalyuk }
151*ee312992SPavel Dovgalyuk 
152c0c071d0SPavel Dovgalyuk static void replay_save_event(Event *event, int checkpoint)
153c0c071d0SPavel Dovgalyuk {
154c0c071d0SPavel Dovgalyuk     if (replay_mode != REPLAY_MODE_PLAY) {
155c0c071d0SPavel Dovgalyuk         /* put the event into the file */
156c0c071d0SPavel Dovgalyuk         replay_put_event(EVENT_ASYNC);
157c0c071d0SPavel Dovgalyuk         replay_put_byte(checkpoint);
158c0c071d0SPavel Dovgalyuk         replay_put_byte(event->event_kind);
159c0c071d0SPavel Dovgalyuk 
160c0c071d0SPavel Dovgalyuk         /* save event-specific data */
161c0c071d0SPavel Dovgalyuk         switch (event->event_kind) {
1628a354bd9SPavel Dovgalyuk         case REPLAY_ASYNC_EVENT_BH:
1638a354bd9SPavel Dovgalyuk             replay_put_qword(event->id);
1648a354bd9SPavel Dovgalyuk             break;
165*ee312992SPavel Dovgalyuk         case REPLAY_ASYNC_EVENT_INPUT:
166*ee312992SPavel Dovgalyuk             replay_save_input_event(event->opaque);
167*ee312992SPavel Dovgalyuk             break;
168*ee312992SPavel Dovgalyuk         case REPLAY_ASYNC_EVENT_INPUT_SYNC:
169*ee312992SPavel Dovgalyuk             break;
170c0c071d0SPavel Dovgalyuk         default:
171c0c071d0SPavel Dovgalyuk             error_report("Unknown ID %d of replay event", read_event_kind);
172c0c071d0SPavel Dovgalyuk             exit(1);
173c0c071d0SPavel Dovgalyuk         }
174c0c071d0SPavel Dovgalyuk     }
175c0c071d0SPavel Dovgalyuk }
176c0c071d0SPavel Dovgalyuk 
177c0c071d0SPavel Dovgalyuk /* Called with replay mutex locked */
178c0c071d0SPavel Dovgalyuk void replay_save_events(int checkpoint)
179c0c071d0SPavel Dovgalyuk {
180c0c071d0SPavel Dovgalyuk     while (!QTAILQ_EMPTY(&events_list)) {
181c0c071d0SPavel Dovgalyuk         Event *event = QTAILQ_FIRST(&events_list);
182c0c071d0SPavel Dovgalyuk         replay_save_event(event, checkpoint);
183c0c071d0SPavel Dovgalyuk 
184c0c071d0SPavel Dovgalyuk         replay_mutex_unlock();
185c0c071d0SPavel Dovgalyuk         replay_run_event(event);
186c0c071d0SPavel Dovgalyuk         replay_mutex_lock();
187c0c071d0SPavel Dovgalyuk         QTAILQ_REMOVE(&events_list, event, events);
188c0c071d0SPavel Dovgalyuk         g_free(event);
189c0c071d0SPavel Dovgalyuk     }
190c0c071d0SPavel Dovgalyuk }
191c0c071d0SPavel Dovgalyuk 
192c0c071d0SPavel Dovgalyuk static Event *replay_read_event(int checkpoint)
193c0c071d0SPavel Dovgalyuk {
194c0c071d0SPavel Dovgalyuk     Event *event;
195c0c071d0SPavel Dovgalyuk     if (read_event_kind == -1) {
196c0c071d0SPavel Dovgalyuk         read_checkpoint = replay_get_byte();
197c0c071d0SPavel Dovgalyuk         read_event_kind = replay_get_byte();
198c0c071d0SPavel Dovgalyuk         read_id = -1;
199c0c071d0SPavel Dovgalyuk         replay_check_error();
200c0c071d0SPavel Dovgalyuk     }
201c0c071d0SPavel Dovgalyuk 
202c0c071d0SPavel Dovgalyuk     if (checkpoint != read_checkpoint) {
203c0c071d0SPavel Dovgalyuk         return NULL;
204c0c071d0SPavel Dovgalyuk     }
205c0c071d0SPavel Dovgalyuk 
206c0c071d0SPavel Dovgalyuk     /* Events that has not to be in the queue */
207c0c071d0SPavel Dovgalyuk     switch (read_event_kind) {
2088a354bd9SPavel Dovgalyuk     case REPLAY_ASYNC_EVENT_BH:
2098a354bd9SPavel Dovgalyuk         if (read_id == -1) {
2108a354bd9SPavel Dovgalyuk             read_id = replay_get_qword();
2118a354bd9SPavel Dovgalyuk         }
2128a354bd9SPavel Dovgalyuk         break;
213*ee312992SPavel Dovgalyuk     case REPLAY_ASYNC_EVENT_INPUT:
214*ee312992SPavel Dovgalyuk         event = g_malloc0(sizeof(Event));
215*ee312992SPavel Dovgalyuk         event->event_kind = read_event_kind;
216*ee312992SPavel Dovgalyuk         event->opaque = replay_read_input_event();
217*ee312992SPavel Dovgalyuk         return event;
218*ee312992SPavel Dovgalyuk     case REPLAY_ASYNC_EVENT_INPUT_SYNC:
219*ee312992SPavel Dovgalyuk         event = g_malloc0(sizeof(Event));
220*ee312992SPavel Dovgalyuk         event->event_kind = read_event_kind;
221*ee312992SPavel Dovgalyuk         event->opaque = 0;
222*ee312992SPavel Dovgalyuk         return event;
223c0c071d0SPavel Dovgalyuk     default:
224c0c071d0SPavel Dovgalyuk         error_report("Unknown ID %d of replay event", read_event_kind);
225c0c071d0SPavel Dovgalyuk         exit(1);
226c0c071d0SPavel Dovgalyuk         break;
227c0c071d0SPavel Dovgalyuk     }
228c0c071d0SPavel Dovgalyuk 
229c0c071d0SPavel Dovgalyuk     QTAILQ_FOREACH(event, &events_list, events) {
230c0c071d0SPavel Dovgalyuk         if (event->event_kind == read_event_kind
231c0c071d0SPavel Dovgalyuk             && (read_id == -1 || read_id == event->id)) {
232c0c071d0SPavel Dovgalyuk             break;
233c0c071d0SPavel Dovgalyuk         }
234c0c071d0SPavel Dovgalyuk     }
235c0c071d0SPavel Dovgalyuk 
236c0c071d0SPavel Dovgalyuk     if (event) {
237c0c071d0SPavel Dovgalyuk         QTAILQ_REMOVE(&events_list, event, events);
238c0c071d0SPavel Dovgalyuk     } else {
239c0c071d0SPavel Dovgalyuk         return NULL;
240c0c071d0SPavel Dovgalyuk     }
241c0c071d0SPavel Dovgalyuk 
242c0c071d0SPavel Dovgalyuk     /* Read event-specific data */
243c0c071d0SPavel Dovgalyuk 
244c0c071d0SPavel Dovgalyuk     return event;
245c0c071d0SPavel Dovgalyuk }
246c0c071d0SPavel Dovgalyuk 
247c0c071d0SPavel Dovgalyuk /* Called with replay mutex locked */
248c0c071d0SPavel Dovgalyuk void replay_read_events(int checkpoint)
249c0c071d0SPavel Dovgalyuk {
250c0c071d0SPavel Dovgalyuk     while (replay_data_kind == EVENT_ASYNC) {
251c0c071d0SPavel Dovgalyuk         Event *event = replay_read_event(checkpoint);
252c0c071d0SPavel Dovgalyuk         if (!event) {
253c0c071d0SPavel Dovgalyuk             break;
254c0c071d0SPavel Dovgalyuk         }
255c0c071d0SPavel Dovgalyuk         replay_mutex_unlock();
256c0c071d0SPavel Dovgalyuk         replay_run_event(event);
257c0c071d0SPavel Dovgalyuk         replay_mutex_lock();
258c0c071d0SPavel Dovgalyuk 
259c0c071d0SPavel Dovgalyuk         g_free(event);
260c0c071d0SPavel Dovgalyuk         replay_finish_event();
261c0c071d0SPavel Dovgalyuk         read_event_kind = -1;
262c0c071d0SPavel Dovgalyuk     }
263c0c071d0SPavel Dovgalyuk }
264c0c071d0SPavel Dovgalyuk 
265c0c071d0SPavel Dovgalyuk void replay_init_events(void)
266c0c071d0SPavel Dovgalyuk {
267c0c071d0SPavel Dovgalyuk     read_event_kind = -1;
268c0c071d0SPavel Dovgalyuk }
269c0c071d0SPavel Dovgalyuk 
270c0c071d0SPavel Dovgalyuk void replay_finish_events(void)
271c0c071d0SPavel Dovgalyuk {
272c0c071d0SPavel Dovgalyuk     events_enabled = false;
273c0c071d0SPavel Dovgalyuk     replay_clear_events();
274c0c071d0SPavel Dovgalyuk }
275c0c071d0SPavel Dovgalyuk 
276c0c071d0SPavel Dovgalyuk bool replay_events_enabled(void)
277c0c071d0SPavel Dovgalyuk {
278c0c071d0SPavel Dovgalyuk     return events_enabled;
279c0c071d0SPavel Dovgalyuk }
280