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