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