xref: /openbmc/qemu/replay/replay-internal.c (revision 2df1eb27)
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             replay_state.current_event++;
179             if (replay_state.data_kind == EVENT_INSTRUCTION) {
180                 replay_state.instruction_count = replay_get_dword();
181             }
182             replay_check_error();
183             replay_state.has_unread_data = true;
184             if (replay_state.data_kind >= EVENT_COUNT) {
185                 error_report("Replay: unknown event kind %d",
186                              replay_state.data_kind);
187                 exit(1);
188             }
189         }
190     }
191 }
192 
193 void replay_finish_event(void)
194 {
195     replay_state.has_unread_data = false;
196     replay_fetch_data_kind();
197 }
198 
199 static __thread bool replay_locked;
200 
201 void replay_mutex_init(void)
202 {
203     qemu_mutex_init(&lock);
204     qemu_cond_init(&mutex_cond);
205     /* Hold the mutex while we start-up */
206     replay_locked = true;
207     ++mutex_tail;
208 }
209 
210 bool replay_mutex_locked(void)
211 {
212     return replay_locked;
213 }
214 
215 /* Ordering constraints, replay_lock must be taken before BQL */
216 void replay_mutex_lock(void)
217 {
218     if (replay_mode != REPLAY_MODE_NONE) {
219         unsigned long id;
220         g_assert(!bql_locked());
221         g_assert(!replay_mutex_locked());
222         qemu_mutex_lock(&lock);
223         id = mutex_tail++;
224         while (id != mutex_head) {
225             qemu_cond_wait(&mutex_cond, &lock);
226         }
227         replay_locked = true;
228         qemu_mutex_unlock(&lock);
229     }
230 }
231 
232 void replay_mutex_unlock(void)
233 {
234     if (replay_mode != REPLAY_MODE_NONE) {
235         g_assert(replay_mutex_locked());
236         qemu_mutex_lock(&lock);
237         ++mutex_head;
238         replay_locked = false;
239         qemu_cond_broadcast(&mutex_cond);
240         qemu_mutex_unlock(&lock);
241     }
242 }
243 
244 void replay_advance_current_icount(uint64_t current_icount)
245 {
246     int diff = (int)(current_icount - replay_state.current_icount);
247 
248     /* Time can only go forward */
249     assert(diff >= 0);
250 
251     if (replay_mode == REPLAY_MODE_RECORD) {
252         if (diff > 0) {
253             replay_put_event(EVENT_INSTRUCTION);
254             replay_put_dword(diff);
255             replay_state.current_icount += diff;
256         }
257     } else if (replay_mode == REPLAY_MODE_PLAY) {
258         if (diff > 0) {
259             replay_state.instruction_count -= diff;
260             replay_state.current_icount += diff;
261             if (replay_state.instruction_count == 0) {
262                 assert(replay_state.data_kind == EVENT_INSTRUCTION);
263                 replay_finish_event();
264                 /* Wake up iothread. This is required because
265                     timers will not expire until clock counters
266                     will be read from the log. */
267                 qemu_notify_event();
268             }
269         }
270         /* Execution reached the break step */
271         if (replay_break_icount == replay_state.current_icount) {
272             /* Cannot make callback directly from the vCPU thread */
273             timer_mod_ns(replay_break_timer,
274                 qemu_clock_get_ns(QEMU_CLOCK_REALTIME));
275         }
276     }
277 }
278 
279 /*! Saves cached instructions. */
280 void replay_save_instructions(void)
281 {
282     if (replay_file && replay_mode == REPLAY_MODE_RECORD) {
283         g_assert(replay_mutex_locked());
284         replay_advance_current_icount(replay_get_current_icount());
285     }
286 }
287