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