]>
Commit | Line | Data |
---|---|---|
681c28a3 | 1 | #include "qemu/osdep.h" |
dd0029c0 JS |
2 | #include <sys/wait.h> |
3 | ||
4 | #include "libqtest.h" | |
5 | #include "libqos/libqos.h" | |
6 | #include "libqos/pci.h" | |
dd0029c0 JS |
7 | |
8 | /*** Test Setup & Teardown ***/ | |
9 | ||
10 | /** | |
11 | * Launch QEMU with the given command line, | |
12 | * and then set up interrupts and our guest malloc interface. | |
13 | */ | |
90e5add6 | 14 | QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap) |
dd0029c0 | 15 | { |
dd0029c0 | 16 | char *cmdline; |
dd0029c0 | 17 | |
f1518d11 | 18 | struct QOSState *qs = g_malloc(sizeof(QOSState)); |
dd0029c0 | 19 | |
f1518d11 | 20 | cmdline = g_strdup_vprintf(cmdline_fmt, ap); |
dd0029c0 | 21 | qs->qts = qtest_start(cmdline); |
90e5add6 | 22 | qs->ops = ops; |
2ecd7e2f LV |
23 | if (ops) { |
24 | if (ops->init_allocator) { | |
25 | qs->alloc = ops->init_allocator(ALLOC_NO_FLAGS); | |
26 | } | |
27 | if (ops->qpci_init && qs->alloc) { | |
28 | qs->pcibus = ops->qpci_init(qs->alloc); | |
29 | } | |
90e5add6 | 30 | } |
dd0029c0 JS |
31 | |
32 | g_free(cmdline); | |
33 | return qs; | |
34 | } | |
35 | ||
f1518d11 JS |
36 | /** |
37 | * Launch QEMU with the given command line, | |
38 | * and then set up interrupts and our guest malloc interface. | |
39 | */ | |
90e5add6 | 40 | QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...) |
f1518d11 JS |
41 | { |
42 | QOSState *qs; | |
43 | va_list ap; | |
44 | ||
45 | va_start(ap, cmdline_fmt); | |
90e5add6 | 46 | qs = qtest_vboot(ops, cmdline_fmt, ap); |
f1518d11 JS |
47 | va_end(ap); |
48 | ||
49 | return qs; | |
50 | } | |
51 | ||
dd0029c0 JS |
52 | /** |
53 | * Tear down the QEMU instance. | |
54 | */ | |
61ae5cf3 | 55 | void qtest_common_shutdown(QOSState *qs) |
dd0029c0 | 56 | { |
2ecd7e2f LV |
57 | if (qs->ops) { |
58 | if (qs->pcibus && qs->ops->qpci_free) { | |
59 | qs->ops->qpci_free(qs->pcibus); | |
60 | qs->pcibus = NULL; | |
61 | } | |
62 | if (qs->alloc && qs->ops->uninit_allocator) { | |
63 | qs->ops->uninit_allocator(qs->alloc); | |
64 | qs->alloc = NULL; | |
65 | } | |
dd0029c0 JS |
66 | } |
67 | qtest_quit(qs->qts); | |
68 | g_free(qs); | |
69 | } | |
122fdf2d | 70 | |
61ae5cf3 LV |
71 | void qtest_shutdown(QOSState *qs) |
72 | { | |
73 | if (qs->ops && qs->ops->shutdown) { | |
74 | qs->ops->shutdown(qs); | |
75 | } else { | |
76 | qtest_common_shutdown(qs); | |
77 | } | |
78 | } | |
79 | ||
085248ae JS |
80 | void set_context(QOSState *s) |
81 | { | |
82 | global_qtest = s->qts; | |
83 | } | |
84 | ||
85 | static QDict *qmp_execute(const char *command) | |
86 | { | |
87 | char *fmt; | |
88 | QDict *rsp; | |
89 | ||
90 | fmt = g_strdup_printf("{ 'execute': '%s' }", command); | |
91 | rsp = qmp(fmt); | |
92 | g_free(fmt); | |
93 | ||
94 | return rsp; | |
95 | } | |
96 | ||
97 | void migrate(QOSState *from, QOSState *to, const char *uri) | |
98 | { | |
99 | const char *st; | |
100 | char *s; | |
101 | QDict *rsp, *sub; | |
102 | bool running; | |
103 | ||
104 | set_context(from); | |
105 | ||
106 | /* Is the machine currently running? */ | |
107 | rsp = qmp_execute("query-status"); | |
108 | g_assert(qdict_haskey(rsp, "return")); | |
109 | sub = qdict_get_qdict(rsp, "return"); | |
110 | g_assert(qdict_haskey(sub, "running")); | |
111 | running = qdict_get_bool(sub, "running"); | |
112 | QDECREF(rsp); | |
113 | ||
114 | /* Issue the migrate command. */ | |
115 | s = g_strdup_printf("{ 'execute': 'migrate'," | |
116 | "'arguments': { 'uri': '%s' } }", | |
117 | uri); | |
118 | rsp = qmp(s); | |
119 | g_free(s); | |
120 | g_assert(qdict_haskey(rsp, "return")); | |
121 | QDECREF(rsp); | |
122 | ||
123 | /* Wait for STOP event, but only if we were running: */ | |
124 | if (running) { | |
125 | qmp_eventwait("STOP"); | |
126 | } | |
127 | ||
128 | /* If we were running, we can wait for an event. */ | |
129 | if (running) { | |
130 | migrate_allocator(from->alloc, to->alloc); | |
131 | set_context(to); | |
132 | qmp_eventwait("RESUME"); | |
133 | return; | |
134 | } | |
135 | ||
136 | /* Otherwise, we need to wait: poll until migration is completed. */ | |
137 | while (1) { | |
138 | rsp = qmp_execute("query-migrate"); | |
139 | g_assert(qdict_haskey(rsp, "return")); | |
140 | sub = qdict_get_qdict(rsp, "return"); | |
141 | g_assert(qdict_haskey(sub, "status")); | |
142 | st = qdict_get_str(sub, "status"); | |
143 | ||
144 | /* "setup", "active", "completed", "failed", "cancelled" */ | |
145 | if (strcmp(st, "completed") == 0) { | |
146 | QDECREF(rsp); | |
147 | break; | |
148 | } | |
149 | ||
150 | if ((strcmp(st, "setup") == 0) || (strcmp(st, "active") == 0)) { | |
151 | QDECREF(rsp); | |
152 | g_usleep(5000); | |
153 | continue; | |
154 | } | |
155 | ||
156 | fprintf(stderr, "Migration did not complete, status: %s\n", st); | |
157 | g_assert_not_reached(); | |
158 | } | |
159 | ||
160 | migrate_allocator(from->alloc, to->alloc); | |
161 | set_context(to); | |
162 | } | |
163 | ||
cb11e7b2 JS |
164 | bool have_qemu_img(void) |
165 | { | |
166 | char *rpath; | |
167 | const char *path = getenv("QTEST_QEMU_IMG"); | |
168 | if (!path) { | |
169 | return false; | |
170 | } | |
171 | ||
172 | rpath = realpath(path, NULL); | |
173 | if (!rpath) { | |
174 | return false; | |
175 | } else { | |
176 | free(rpath); | |
177 | return true; | |
178 | } | |
179 | } | |
180 | ||
122fdf2d JS |
181 | void mkimg(const char *file, const char *fmt, unsigned size_mb) |
182 | { | |
183 | gchar *cli; | |
184 | bool ret; | |
185 | int rc; | |
186 | GError *err = NULL; | |
187 | char *qemu_img_path; | |
188 | gchar *out, *out2; | |
cb11e7b2 | 189 | char *qemu_img_abs_path; |
122fdf2d JS |
190 | |
191 | qemu_img_path = getenv("QTEST_QEMU_IMG"); | |
cb11e7b2 JS |
192 | g_assert(qemu_img_path); |
193 | qemu_img_abs_path = realpath(qemu_img_path, NULL); | |
194 | g_assert(qemu_img_abs_path); | |
122fdf2d | 195 | |
cb11e7b2 | 196 | cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path, |
122fdf2d JS |
197 | fmt, file, size_mb); |
198 | ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err); | |
199 | if (err) { | |
200 | fprintf(stderr, "%s\n", err->message); | |
201 | g_error_free(err); | |
202 | } | |
203 | g_assert(ret && !err); | |
204 | ||
205 | /* In glib 2.34, we have g_spawn_check_exit_status. in 2.12, we don't. | |
206 | * glib 2.43.91 implementation assumes that any non-zero is an error for | |
207 | * windows, but uses extra precautions for Linux. However, | |
208 | * 0 is only possible if the program exited normally, so that should be | |
209 | * sufficient for our purposes on all platforms, here. */ | |
210 | if (rc) { | |
211 | fprintf(stderr, "qemu-img returned status code %d\n", rc); | |
212 | } | |
213 | g_assert(!rc); | |
214 | ||
215 | g_free(out); | |
216 | g_free(out2); | |
217 | g_free(cli); | |
cb11e7b2 | 218 | free(qemu_img_abs_path); |
122fdf2d JS |
219 | } |
220 | ||
221 | void mkqcow2(const char *file, unsigned size_mb) | |
222 | { | |
223 | return mkimg(file, "qcow2", size_mb); | |
224 | } | |
72c85e94 JS |
225 | |
226 | void prepare_blkdebug_script(const char *debug_fn, const char *event) | |
227 | { | |
228 | FILE *debug_file = fopen(debug_fn, "w"); | |
229 | int ret; | |
230 | ||
231 | fprintf(debug_file, "[inject-error]\n"); | |
232 | fprintf(debug_file, "event = \"%s\"\n", event); | |
233 | fprintf(debug_file, "errno = \"5\"\n"); | |
234 | fprintf(debug_file, "state = \"1\"\n"); | |
235 | fprintf(debug_file, "immediately = \"off\"\n"); | |
236 | fprintf(debug_file, "once = \"on\"\n"); | |
237 | ||
238 | fprintf(debug_file, "[set-state]\n"); | |
239 | fprintf(debug_file, "event = \"%s\"\n", event); | |
240 | fprintf(debug_file, "new_state = \"2\"\n"); | |
241 | fflush(debug_file); | |
242 | g_assert(!ferror(debug_file)); | |
243 | ||
244 | ret = fclose(debug_file); | |
245 | g_assert(ret == 0); | |
246 | } | |
ab4f7057 JS |
247 | |
248 | void generate_pattern(void *buffer, size_t len, size_t cycle_len) | |
249 | { | |
250 | int i, j; | |
251 | unsigned char *tx = (unsigned char *)buffer; | |
252 | unsigned char p; | |
253 | size_t *sx; | |
254 | ||
255 | /* Write an indicative pattern that varies and is unique per-cycle */ | |
256 | p = rand() % 256; | |
257 | for (i = 0; i < len; i++) { | |
258 | tx[i] = p++ % 256; | |
259 | if (i % cycle_len == 0) { | |
260 | p = rand() % 256; | |
261 | } | |
262 | } | |
263 | ||
264 | /* force uniqueness by writing an id per-cycle */ | |
265 | for (i = 0; i < len / cycle_len; i++) { | |
266 | j = i * cycle_len; | |
267 | if (j + sizeof(*sx) <= len) { | |
268 | sx = (size_t *)&tx[j]; | |
269 | *sx = i; | |
270 | } | |
271 | } | |
272 | } |