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