xref: /openbmc/qemu/replay/replay-internal.c (revision 9468484fe904ab4691de6d9c34616667f377ceac)
1c92079f4SPavel Dovgalyuk /*
2c92079f4SPavel Dovgalyuk  * replay-internal.c
3c92079f4SPavel Dovgalyuk  *
4c92079f4SPavel Dovgalyuk  * Copyright (c) 2010-2015 Institute for System Programming
5c92079f4SPavel Dovgalyuk  *                         of the Russian Academy of Sciences.
6c92079f4SPavel Dovgalyuk  *
7c92079f4SPavel Dovgalyuk  * This work is licensed under the terms of the GNU GPL, version 2 or later.
8c92079f4SPavel Dovgalyuk  * See the COPYING file in the top-level directory.
9c92079f4SPavel Dovgalyuk  *
10c92079f4SPavel Dovgalyuk  */
11c92079f4SPavel Dovgalyuk 
12d38ea87aSPeter Maydell #include "qemu/osdep.h"
1326bc60acSPavel Dovgalyuk #include "sysemu/replay.h"
1454d31236SMarkus Armbruster #include "sysemu/runstate.h"
15c92079f4SPavel Dovgalyuk #include "replay-internal.h"
16c92079f4SPavel Dovgalyuk #include "qemu/error-report.h"
17db725815SMarkus Armbruster #include "qemu/main-loop.h"
18c92079f4SPavel Dovgalyuk 
19c16861efSPavel Dovgalyuk /* Mutex to protect reading and writing events to the log.
20f186d64dSPavel Dovgalyuk    data_kind and has_unread_data are also protected
21c16861efSPavel Dovgalyuk    by this mutex.
22c16861efSPavel Dovgalyuk    It also protects replay events queue which stores events to be
23c16861efSPavel Dovgalyuk    written or read to the log. */
24c16861efSPavel Dovgalyuk static QemuMutex lock;
25ddf63df7SPavel Dovgalyuk /* Condition and queue for fair ordering of mutex lock requests. */
26ddf63df7SPavel Dovgalyuk static QemuCond mutex_cond;
27ddf63df7SPavel Dovgalyuk static unsigned long mutex_head, mutex_tail;
28c16861efSPavel Dovgalyuk 
29c92079f4SPavel Dovgalyuk /* File for replay writing */
306dc0f529SPavel Dovgalyuk static bool write_error;
31c92079f4SPavel Dovgalyuk FILE *replay_file;
32c92079f4SPavel Dovgalyuk 
replay_write_error(void)336dc0f529SPavel Dovgalyuk static void replay_write_error(void)
346dc0f529SPavel Dovgalyuk {
356dc0f529SPavel Dovgalyuk     if (!write_error) {
366dc0f529SPavel Dovgalyuk         error_report("replay write error");
376dc0f529SPavel Dovgalyuk         write_error = true;
386dc0f529SPavel Dovgalyuk     }
396dc0f529SPavel Dovgalyuk }
406dc0f529SPavel Dovgalyuk 
replay_read_error(void)410b570077SPeter Maydell static void replay_read_error(void)
420b570077SPeter Maydell {
430b570077SPeter Maydell     error_report("error reading the replay data");
440b570077SPeter Maydell     exit(1);
450b570077SPeter Maydell }
460b570077SPeter Maydell 
replay_put_byte(uint8_t byte)47c92079f4SPavel Dovgalyuk void replay_put_byte(uint8_t byte)
48c92079f4SPavel Dovgalyuk {
49c92079f4SPavel Dovgalyuk     if (replay_file) {
506dc0f529SPavel Dovgalyuk         if (putc(byte, replay_file) == EOF) {
516dc0f529SPavel Dovgalyuk             replay_write_error();
526dc0f529SPavel Dovgalyuk         }
53c92079f4SPavel Dovgalyuk     }
54c92079f4SPavel Dovgalyuk }
55c92079f4SPavel Dovgalyuk 
replay_put_event(uint8_t event)56c92079f4SPavel Dovgalyuk void replay_put_event(uint8_t event)
57c92079f4SPavel Dovgalyuk {
5826bc60acSPavel Dovgalyuk     assert(event < EVENT_COUNT);
59c92079f4SPavel Dovgalyuk     replay_put_byte(event);
60c92079f4SPavel Dovgalyuk }
61c92079f4SPavel Dovgalyuk 
62c92079f4SPavel Dovgalyuk 
replay_put_word(uint16_t word)63c92079f4SPavel Dovgalyuk void replay_put_word(uint16_t word)
64c92079f4SPavel Dovgalyuk {
65c92079f4SPavel Dovgalyuk     replay_put_byte(word >> 8);
66c92079f4SPavel Dovgalyuk     replay_put_byte(word);
67c92079f4SPavel Dovgalyuk }
68c92079f4SPavel Dovgalyuk 
replay_put_dword(uint32_t dword)69c92079f4SPavel Dovgalyuk void replay_put_dword(uint32_t dword)
70c92079f4SPavel Dovgalyuk {
71c92079f4SPavel Dovgalyuk     replay_put_word(dword >> 16);
72c92079f4SPavel Dovgalyuk     replay_put_word(dword);
73c92079f4SPavel Dovgalyuk }
74c92079f4SPavel Dovgalyuk 
replay_put_qword(int64_t qword)75c92079f4SPavel Dovgalyuk void replay_put_qword(int64_t qword)
76c92079f4SPavel Dovgalyuk {
77c92079f4SPavel Dovgalyuk     replay_put_dword(qword >> 32);
78c92079f4SPavel Dovgalyuk     replay_put_dword(qword);
79c92079f4SPavel Dovgalyuk }
80c92079f4SPavel Dovgalyuk 
replay_put_array(const uint8_t * buf,size_t size)81c92079f4SPavel Dovgalyuk void replay_put_array(const uint8_t *buf, size_t size)
82c92079f4SPavel Dovgalyuk {
83c92079f4SPavel Dovgalyuk     if (replay_file) {
84c92079f4SPavel Dovgalyuk         replay_put_dword(size);
856dc0f529SPavel Dovgalyuk         if (fwrite(buf, 1, size, replay_file) != size) {
866dc0f529SPavel Dovgalyuk             replay_write_error();
876dc0f529SPavel Dovgalyuk         }
88c92079f4SPavel Dovgalyuk     }
89c92079f4SPavel Dovgalyuk }
90c92079f4SPavel Dovgalyuk 
replay_get_byte(void)91c92079f4SPavel Dovgalyuk uint8_t replay_get_byte(void)
92c92079f4SPavel Dovgalyuk {
93c92079f4SPavel Dovgalyuk     uint8_t byte = 0;
94c92079f4SPavel Dovgalyuk     if (replay_file) {
950b570077SPeter Maydell         int r = getc(replay_file);
960b570077SPeter Maydell         if (r == EOF) {
970b570077SPeter Maydell             replay_read_error();
980b570077SPeter Maydell         }
990b570077SPeter Maydell         byte = r;
100c92079f4SPavel Dovgalyuk     }
101c92079f4SPavel Dovgalyuk     return byte;
102c92079f4SPavel Dovgalyuk }
103c92079f4SPavel Dovgalyuk 
replay_get_word(void)104c92079f4SPavel Dovgalyuk uint16_t replay_get_word(void)
105c92079f4SPavel Dovgalyuk {
106c92079f4SPavel Dovgalyuk     uint16_t word = 0;
107c92079f4SPavel Dovgalyuk     if (replay_file) {
108c92079f4SPavel Dovgalyuk         word = replay_get_byte();
109c92079f4SPavel Dovgalyuk         word = (word << 8) + replay_get_byte();
110c92079f4SPavel Dovgalyuk     }
111c92079f4SPavel Dovgalyuk 
112c92079f4SPavel Dovgalyuk     return word;
113c92079f4SPavel Dovgalyuk }
114c92079f4SPavel Dovgalyuk 
replay_get_dword(void)115c92079f4SPavel Dovgalyuk uint32_t replay_get_dword(void)
116c92079f4SPavel Dovgalyuk {
117c92079f4SPavel Dovgalyuk     uint32_t dword = 0;
118c92079f4SPavel Dovgalyuk     if (replay_file) {
119c92079f4SPavel Dovgalyuk         dword = replay_get_word();
120c92079f4SPavel Dovgalyuk         dword = (dword << 16) + replay_get_word();
121c92079f4SPavel Dovgalyuk     }
122c92079f4SPavel Dovgalyuk 
123c92079f4SPavel Dovgalyuk     return dword;
124c92079f4SPavel Dovgalyuk }
125c92079f4SPavel Dovgalyuk 
replay_get_qword(void)126c92079f4SPavel Dovgalyuk int64_t replay_get_qword(void)
127c92079f4SPavel Dovgalyuk {
128c92079f4SPavel Dovgalyuk     int64_t qword = 0;
129c92079f4SPavel Dovgalyuk     if (replay_file) {
130c92079f4SPavel Dovgalyuk         qword = replay_get_dword();
131c92079f4SPavel Dovgalyuk         qword = (qword << 32) + replay_get_dword();
132c92079f4SPavel Dovgalyuk     }
133c92079f4SPavel Dovgalyuk 
134c92079f4SPavel Dovgalyuk     return qword;
135c92079f4SPavel Dovgalyuk }
136c92079f4SPavel Dovgalyuk 
replay_get_array(uint8_t * buf,size_t * size)137c92079f4SPavel Dovgalyuk void replay_get_array(uint8_t *buf, size_t *size)
138c92079f4SPavel Dovgalyuk {
139c92079f4SPavel Dovgalyuk     if (replay_file) {
140c92079f4SPavel Dovgalyuk         *size = replay_get_dword();
141c92079f4SPavel Dovgalyuk         if (fread(buf, 1, *size, replay_file) != *size) {
1420b570077SPeter Maydell             replay_read_error();
143c92079f4SPavel Dovgalyuk         }
144c92079f4SPavel Dovgalyuk     }
145c92079f4SPavel Dovgalyuk }
146c92079f4SPavel Dovgalyuk 
replay_get_array_alloc(uint8_t ** buf,size_t * size)147c92079f4SPavel Dovgalyuk void replay_get_array_alloc(uint8_t **buf, size_t *size)
148c92079f4SPavel Dovgalyuk {
149c92079f4SPavel Dovgalyuk     if (replay_file) {
150c92079f4SPavel Dovgalyuk         *size = replay_get_dword();
151c92079f4SPavel Dovgalyuk         *buf = g_malloc(*size);
152c92079f4SPavel Dovgalyuk         if (fread(*buf, 1, *size, replay_file) != *size) {
1530b570077SPeter Maydell             replay_read_error();
154c92079f4SPavel Dovgalyuk         }
155c92079f4SPavel Dovgalyuk     }
156c92079f4SPavel Dovgalyuk }
157c92079f4SPavel Dovgalyuk 
replay_check_error(void)158c92079f4SPavel Dovgalyuk void replay_check_error(void)
159c92079f4SPavel Dovgalyuk {
160c92079f4SPavel Dovgalyuk     if (replay_file) {
161c92079f4SPavel Dovgalyuk         if (feof(replay_file)) {
162c92079f4SPavel Dovgalyuk             error_report("replay file is over");
163c92079f4SPavel Dovgalyuk             qemu_system_vmstop_request_prepare();
164c92079f4SPavel Dovgalyuk             qemu_system_vmstop_request(RUN_STATE_PAUSED);
165c92079f4SPavel Dovgalyuk         } else if (ferror(replay_file)) {
166c92079f4SPavel Dovgalyuk             error_report("replay file is over or something goes wrong");
167c92079f4SPavel Dovgalyuk             qemu_system_vmstop_request_prepare();
168c92079f4SPavel Dovgalyuk             qemu_system_vmstop_request(RUN_STATE_INTERNAL_ERROR);
169c92079f4SPavel Dovgalyuk         }
170c92079f4SPavel Dovgalyuk     }
171c92079f4SPavel Dovgalyuk }
172c92079f4SPavel Dovgalyuk 
replay_fetch_data_kind(void)173c92079f4SPavel Dovgalyuk void replay_fetch_data_kind(void)
174c92079f4SPavel Dovgalyuk {
175c92079f4SPavel Dovgalyuk     if (replay_file) {
176f186d64dSPavel Dovgalyuk         if (!replay_state.has_unread_data) {
177f186d64dSPavel Dovgalyuk             replay_state.data_kind = replay_get_byte();
178dcda7321SAlex Bennée             replay_state.current_event++;
179f186d64dSPavel Dovgalyuk             if (replay_state.data_kind == EVENT_INSTRUCTION) {
18013f26713SPavel Dovgalyuk                 replay_state.instruction_count = replay_get_dword();
18126bc60acSPavel Dovgalyuk             }
182c92079f4SPavel Dovgalyuk             replay_check_error();
1832b7a58b6SAlex Bennée             replay_state.has_unread_data = true;
184f186d64dSPavel Dovgalyuk             if (replay_state.data_kind >= EVENT_COUNT) {
185f186d64dSPavel Dovgalyuk                 error_report("Replay: unknown event kind %d",
186f186d64dSPavel Dovgalyuk                              replay_state.data_kind);
18726bc60acSPavel Dovgalyuk                 exit(1);
18826bc60acSPavel Dovgalyuk             }
189c92079f4SPavel Dovgalyuk         }
190c92079f4SPavel Dovgalyuk     }
191c92079f4SPavel Dovgalyuk }
192c92079f4SPavel Dovgalyuk 
replay_finish_event(void)193c92079f4SPavel Dovgalyuk void replay_finish_event(void)
194c92079f4SPavel Dovgalyuk {
1952b7a58b6SAlex Bennée     replay_state.has_unread_data = false;
196c92079f4SPavel Dovgalyuk     replay_fetch_data_kind();
197c92079f4SPavel Dovgalyuk }
198c16861efSPavel Dovgalyuk 
199180d30beSAlex Bennée static __thread bool replay_locked;
200180d30beSAlex Bennée 
replay_mutex_init(void)201c16861efSPavel Dovgalyuk void replay_mutex_init(void)
202c16861efSPavel Dovgalyuk {
203c16861efSPavel Dovgalyuk     qemu_mutex_init(&lock);
204ddf63df7SPavel Dovgalyuk     qemu_cond_init(&mutex_cond);
205d759c951SAlex Bennée     /* Hold the mutex while we start-up */
206d759c951SAlex Bennée     replay_locked = true;
207ddf63df7SPavel Dovgalyuk     ++mutex_tail;
208c16861efSPavel Dovgalyuk }
209c16861efSPavel Dovgalyuk 
replay_mutex_locked(void)210a36544d3SAlex Bennée bool replay_mutex_locked(void)
211180d30beSAlex Bennée {
212180d30beSAlex Bennée     return replay_locked;
213180d30beSAlex Bennée }
214180d30beSAlex Bennée 
215d759c951SAlex Bennée /* Ordering constraints, replay_lock must be taken before BQL */
replay_mutex_lock(void)216c16861efSPavel Dovgalyuk void replay_mutex_lock(void)
217c16861efSPavel Dovgalyuk {
218d759c951SAlex Bennée     if (replay_mode != REPLAY_MODE_NONE) {
219ddf63df7SPavel Dovgalyuk         unsigned long id;
220*195801d7SStefan Hajnoczi         g_assert(!bql_locked());
221180d30beSAlex Bennée         g_assert(!replay_mutex_locked());
222c16861efSPavel Dovgalyuk         qemu_mutex_lock(&lock);
223ddf63df7SPavel Dovgalyuk         id = mutex_tail++;
224ddf63df7SPavel Dovgalyuk         while (id != mutex_head) {
225ddf63df7SPavel Dovgalyuk             qemu_cond_wait(&mutex_cond, &lock);
226ddf63df7SPavel Dovgalyuk         }
227180d30beSAlex Bennée         replay_locked = true;
228ddf63df7SPavel Dovgalyuk         qemu_mutex_unlock(&lock);
229c16861efSPavel Dovgalyuk     }
230d759c951SAlex Bennée }
231c16861efSPavel Dovgalyuk 
replay_mutex_unlock(void)232c16861efSPavel Dovgalyuk void replay_mutex_unlock(void)
233c16861efSPavel Dovgalyuk {
234d759c951SAlex Bennée     if (replay_mode != REPLAY_MODE_NONE) {
235180d30beSAlex Bennée         g_assert(replay_mutex_locked());
236ddf63df7SPavel Dovgalyuk         qemu_mutex_lock(&lock);
237ddf63df7SPavel Dovgalyuk         ++mutex_head;
238180d30beSAlex Bennée         replay_locked = false;
239ddf63df7SPavel Dovgalyuk         qemu_cond_broadcast(&mutex_cond);
240c16861efSPavel Dovgalyuk         qemu_mutex_unlock(&lock);
241c16861efSPavel Dovgalyuk     }
242d759c951SAlex Bennée }
24326bc60acSPavel Dovgalyuk 
replay_advance_current_icount(uint64_t current_icount)24413f26713SPavel Dovgalyuk void replay_advance_current_icount(uint64_t current_icount)
24526bc60acSPavel Dovgalyuk {
24613f26713SPavel Dovgalyuk     int diff = (int)(current_icount - replay_state.current_icount);
247982263ceSAlex Bennée 
248982263ceSAlex Bennée     /* Time can only go forward */
249982263ceSAlex Bennée     assert(diff >= 0);
250982263ceSAlex Bennée 
251366a85e4SPavel Dovgalyuk     if (replay_mode == REPLAY_MODE_RECORD) {
25226bc60acSPavel Dovgalyuk         if (diff > 0) {
25326bc60acSPavel Dovgalyuk             replay_put_event(EVENT_INSTRUCTION);
25426bc60acSPavel Dovgalyuk             replay_put_dword(diff);
25513f26713SPavel Dovgalyuk             replay_state.current_icount += diff;
25626bc60acSPavel Dovgalyuk         }
257366a85e4SPavel Dovgalyuk     } else if (replay_mode == REPLAY_MODE_PLAY) {
258366a85e4SPavel Dovgalyuk         if (diff > 0) {
259366a85e4SPavel Dovgalyuk             replay_state.instruction_count -= diff;
260366a85e4SPavel Dovgalyuk             replay_state.current_icount += diff;
261366a85e4SPavel Dovgalyuk             if (replay_state.instruction_count == 0) {
262366a85e4SPavel Dovgalyuk                 assert(replay_state.data_kind == EVENT_INSTRUCTION);
263366a85e4SPavel Dovgalyuk                 replay_finish_event();
264366a85e4SPavel Dovgalyuk                 /* Wake up iothread. This is required because
265366a85e4SPavel Dovgalyuk                     timers will not expire until clock counters
266366a85e4SPavel Dovgalyuk                     will be read from the log. */
267366a85e4SPavel Dovgalyuk                 qemu_notify_event();
268366a85e4SPavel Dovgalyuk             }
269366a85e4SPavel Dovgalyuk         }
270366a85e4SPavel Dovgalyuk         /* Execution reached the break step */
271366a85e4SPavel Dovgalyuk         if (replay_break_icount == replay_state.current_icount) {
272366a85e4SPavel Dovgalyuk             /* Cannot make callback directly from the vCPU thread */
273366a85e4SPavel Dovgalyuk             timer_mod_ns(replay_break_timer,
274366a85e4SPavel Dovgalyuk                 qemu_clock_get_ns(QEMU_CLOCK_REALTIME));
275366a85e4SPavel Dovgalyuk         }
276366a85e4SPavel Dovgalyuk     }
27726bc60acSPavel Dovgalyuk }
27874c0b816SPaolo Bonzini 
27974c0b816SPaolo Bonzini /*! Saves cached instructions. */
replay_save_instructions(void)28074c0b816SPaolo Bonzini void replay_save_instructions(void)
28174c0b816SPaolo Bonzini {
28274c0b816SPaolo Bonzini     if (replay_file && replay_mode == REPLAY_MODE_RECORD) {
28374c0b816SPaolo Bonzini         g_assert(replay_mutex_locked());
28413f26713SPavel Dovgalyuk         replay_advance_current_icount(replay_get_current_icount());
28574c0b816SPaolo Bonzini     }
28626bc60acSPavel Dovgalyuk }
287