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