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