master
c 322 lines 7.64 KB
Raw
1 /*
2 * replay-internal.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 "system/replay.h"
14 #include "system/runstate.h"
15 #include "replay-internal.h"
16 #include "qemu/error-report.h"
17 #include "qemu/main-loop.h"
18 #include "trace.h"
19
20 /* Mutex to protect reading and writing events to the log.
21 data_kind and has_unread_data are also protected
22 by this mutex.
23 It also protects replay events queue which stores events to be
24 written or read to the log. */
25 static QemuMutex lock;
26 /* Condition and queue for fair ordering of mutex lock requests. */
27 static QemuCond mutex_cond;
28 static unsigned long mutex_head, mutex_tail;
29
30 /* File for replay writing */
31 static bool write_error;
32 FILE *replay_file;
33
34 static void replay_write_error(void)
35 {
36 if (!write_error) {
37 error_report("replay write error");
38 write_error = true;
39 }
40 }
41
42 static void replay_read_error(void)
43 {
44 error_report("error reading the replay data");
45 exit(1);
46 }
47
48 static void replay_putc(uint8_t byte)
49 {
50 if (replay_file) {
51 if (putc(byte, replay_file) == EOF) {
52 replay_write_error();
53 }
54 }
55 }
56
57 void replay_put_byte(uint8_t byte)
58 {
59 trace_replay_put_byte(byte);
60 replay_putc(byte);
61 }
62
63 void replay_put_event(uint8_t event)
64 {
65 trace_replay_put_event(event);
66 assert(event < EVENT_COUNT);
67 replay_putc(event);
68 }
69
70
71 void replay_put_word(uint16_t word)
72 {
73 trace_replay_put_word(word);
74 replay_putc(word >> 8);
75 replay_putc(word);
76 }
77
78 void replay_put_dword(uint32_t dword)
79 {
80 int i;
81
82 trace_replay_put_dword(dword);
83 for (i = 24; i >= 0; i -= 8) {
84 replay_putc(dword >> i);
85 }
86 }
87
88 void replay_put_qword(int64_t qword)
89 {
90 int i;
91
92 trace_replay_put_qword(qword);
93 for (i = 56; i >= 0; i -= 8) {
94 replay_putc(qword >> i);
95 }
96 }
97
98 void replay_put_array(const uint8_t *buf, size_t size)
99 {
100 if (replay_file) {
101 replay_put_dword(size);
102 if (fwrite(buf, 1, size, replay_file) != size) {
103 replay_write_error();
104 }
105 }
106 }
107
108 static uint8_t replay_getc(void)
109 {
110 uint8_t byte = 0;
111 if (replay_file) {
112 int r = getc(replay_file);
113 if (r == EOF) {
114 replay_read_error();
115 }
116 byte = r;
117 }
118 return byte;
119 }
120
121 uint8_t replay_get_byte(void)
122 {
123 uint8_t byte = replay_getc();
124 trace_replay_get_byte(byte);
125 return byte;
126 }
127
128 uint16_t replay_get_word(void)
129 {
130 uint16_t word = 0;
131 if (replay_file) {
132 word = replay_getc();
133 word = (word << 8) + replay_getc();
134 }
135
136 trace_replay_get_word(word);
137 return word;
138 }
139
140 uint32_t replay_get_dword(void)
141 {
142 uint32_t dword = 0;
143 int i;
144
145 if (replay_file) {
146 for (i = 24; i >= 0; i -= 8) {
147 dword |= replay_getc() << i;
148 }
149 }
150
151 trace_replay_get_dword(dword);
152 return dword;
153 }
154
155 int64_t replay_get_qword(void)
156 {
157 uint64_t qword = 0;
158 int i;
159
160 if (replay_file) {
161 for (i = 56; i >= 0; i -= 8) {
162 qword |= (uint64_t)replay_getc() << i;
163 }
164 }
165
166 trace_replay_get_qword(qword);
167 return qword;
168 }
169
170 void replay_get_array(uint8_t *buf, size_t *size)
171 {
172 if (replay_file) {
173 *size = replay_get_dword();
174 if (fread(buf, 1, *size, replay_file) != *size) {
175 replay_read_error();
176 }
177 }
178 }
179
180 void replay_get_array_alloc(uint8_t **buf, size_t *size)
181 {
182 if (replay_file) {
183 *size = replay_get_dword();
184 *buf = g_malloc(*size);
185 if (fread(*buf, 1, *size, replay_file) != *size) {
186 replay_read_error();
187 }
188 }
189 }
190
191 void replay_check_error(void)
192 {
193 if (replay_file) {
194 if (feof(replay_file)) {
195 error_report("replay file is over");
196 qemu_system_vmstop_request_prepare();
197 qemu_system_vmstop_request(RUN_STATE_PAUSED);
198 } else if (ferror(replay_file)) {
199 error_report("replay file is over or something goes wrong");
200 qemu_system_vmstop_request_prepare();
201 qemu_system_vmstop_request(RUN_STATE_INTERNAL_ERROR);
202 }
203 }
204 }
205
206 void replay_fetch_data_kind(void)
207 {
208 trace_replay_fetch_data_kind();
209 if (replay_file) {
210 if (!replay_state.has_unread_data) {
211 replay_state.data_kind = replay_getc();
212 replay_state.current_event++;
213 trace_replay_get_event(replay_state.current_event, replay_state.data_kind);
214 if (replay_state.data_kind == EVENT_INSTRUCTION) {
215 replay_state.instruction_count = replay_get_dword();
216 }
217 replay_check_error();
218 replay_state.has_unread_data = true;
219 if (replay_state.data_kind >= EVENT_COUNT) {
220 error_report("Replay: unknown event kind %d",
221 replay_state.data_kind);
222 exit(1);
223 }
224 }
225 }
226 }
227
228 void replay_finish_event(void)
229 {
230 replay_state.has_unread_data = false;
231 replay_fetch_data_kind();
232 }
233
234 static __thread bool replay_locked;
235
236 void replay_mutex_init(void)
237 {
238 qemu_mutex_init(&lock);
239 qemu_cond_init(&mutex_cond);
240 /* Hold the mutex while we start-up */
241 replay_locked = true;
242 ++mutex_tail;
243 }
244
245 bool replay_mutex_locked(void)
246 {
247 return replay_locked;
248 }
249
250 /* Ordering constraints, replay_lock must be taken before BQL */
251 void replay_mutex_lock(void)
252 {
253 if (replay_mode != REPLAY_MODE_NONE) {
254 unsigned long id;
255 g_assert(!bql_locked());
256 g_assert(!replay_mutex_locked());
257 qemu_mutex_lock(&lock);
258 id = mutex_tail++;
259 while (id != mutex_head) {
260 qemu_cond_wait(&mutex_cond, &lock);
261 }
262 replay_locked = true;
263 qemu_mutex_unlock(&lock);
264 }
265 }
266
267 void replay_mutex_unlock(void)
268 {
269 if (replay_mode != REPLAY_MODE_NONE) {
270 g_assert(replay_mutex_locked());
271 qemu_mutex_lock(&lock);
272 ++mutex_head;
273 replay_locked = false;
274 qemu_cond_broadcast(&mutex_cond);
275 qemu_mutex_unlock(&lock);
276 }
277 }
278
279 void replay_advance_current_icount(uint64_t current_icount)
280 {
281 int diff = (int)(current_icount - replay_state.current_icount);
282
283 /* Time can only go forward */
284 trace_replay_advance_current_icount(replay_state.current_icount, diff);
285 assert(diff >= 0);
286
287 if (replay_mode == REPLAY_MODE_RECORD) {
288 if (diff > 0) {
289 replay_put_event(EVENT_INSTRUCTION);
290 replay_put_dword(diff);
291 replay_state.current_icount += diff;
292 }
293 } else if (replay_mode == REPLAY_MODE_PLAY) {
294 if (diff > 0) {
295 replay_state.instruction_count -= diff;
296 replay_state.current_icount += diff;
297 if (replay_state.instruction_count == 0) {
298 assert(replay_state.data_kind == EVENT_INSTRUCTION);
299 replay_finish_event();
300 /* Wake up iothread. This is required because
301 timers will not expire until clock counters
302 will be read from the log. */
303 qemu_notify_event();
304 }
305 }
306 /* Execution reached the break step */
307 if (replay_break_icount == replay_state.current_icount) {
308 /* Cannot make callback directly from the vCPU thread */
309 timer_mod_ns(replay_break_timer,
310 qemu_clock_get_ns(QEMU_CLOCK_REALTIME));
311 }
312 }
313 }
314
315 /*! Saves cached instructions. */
316 void replay_save_instructions(void)
317 {
318 if (replay_file && replay_mode == REPLAY_MODE_RECORD) {
319 g_assert(replay_mutex_locked());
320 replay_advance_current_icount(replay_get_current_icount());
321 }
322 }