| 1 | /* Stress test for Xen Store: multiple people hammering transactions */ |
|---|
| 2 | #include "xs.h" |
|---|
| 3 | #include "utils.h" |
|---|
| 4 | #include <stdlib.h> |
|---|
| 5 | #include <stdio.h> |
|---|
| 6 | #include <sys/types.h> |
|---|
| 7 | #include <sys/wait.h> |
|---|
| 8 | #include <sys/stat.h> |
|---|
| 9 | #include <fcntl.h> |
|---|
| 10 | #include <unistd.h> |
|---|
| 11 | #include <errno.h> |
|---|
| 12 | |
|---|
| 13 | #define NUM_HANDLES 2 |
|---|
| 14 | #define DIR_FANOUT 3 |
|---|
| 15 | #define DIR_DEPTH 3 |
|---|
| 16 | |
|---|
| 17 | /* How often to print progress */ |
|---|
| 18 | static int print; |
|---|
| 19 | |
|---|
| 20 | /* Layout looks like /<num>/<num>/count. */ |
|---|
| 21 | static void work(unsigned int cycles, unsigned int childnum) |
|---|
| 22 | { |
|---|
| 23 | unsigned int i; |
|---|
| 24 | struct xs_handle *handles[NUM_HANDLES]; |
|---|
| 25 | char id; |
|---|
| 26 | |
|---|
| 27 | if (childnum < 10) |
|---|
| 28 | id = '0' + childnum; |
|---|
| 29 | else |
|---|
| 30 | id = 'A' + childnum - 10; |
|---|
| 31 | |
|---|
| 32 | for (i = 0; i < NUM_HANDLES; i++) { |
|---|
| 33 | handles[i] = xs_daemon_open(); |
|---|
| 34 | if (!handles[i]) |
|---|
| 35 | barf_perror("Opening handle %i", i); |
|---|
| 36 | } |
|---|
| 37 | |
|---|
| 38 | srandom(childnum); |
|---|
| 39 | for (i = 0; i < cycles; i++) { |
|---|
| 40 | unsigned int j, len; |
|---|
| 41 | char file[100] = ""; |
|---|
| 42 | char *contents, tmp[100]; |
|---|
| 43 | struct xs_handle *h = handles[random() % NUM_HANDLES]; |
|---|
| 44 | |
|---|
| 45 | for (j = 0; j < DIR_DEPTH; j++) |
|---|
| 46 | sprintf(file + strlen(file), "/%li", |
|---|
| 47 | random()%DIR_FANOUT); |
|---|
| 48 | |
|---|
| 49 | if (!xs_transaction_start(h)) |
|---|
| 50 | barf_perror("%i: starting transaction %i", |
|---|
| 51 | childnum, i); |
|---|
| 52 | |
|---|
| 53 | sprintf(file + strlen(file), "/count"); |
|---|
| 54 | contents = xs_read(h, file, &len); |
|---|
| 55 | if (!contents) |
|---|
| 56 | barf_perror("%i: can't read %s iter %i", |
|---|
| 57 | childnum, file, i); |
|---|
| 58 | sprintf(tmp, "%i", atoi(contents) + 1); |
|---|
| 59 | if (!xs_write(h, file, tmp, strlen(tmp)+1)) |
|---|
| 60 | barf_perror("%i: can't write %s iter %i", |
|---|
| 61 | childnum, file, i); |
|---|
| 62 | |
|---|
| 63 | /* Abandon 1 in 10 */ |
|---|
| 64 | if (random() % 10 == 0) { |
|---|
| 65 | if (!xs_transaction_end(h, true)) |
|---|
| 66 | barf_perror("%i: can't abort transact", |
|---|
| 67 | childnum); |
|---|
| 68 | i--; |
|---|
| 69 | } else { |
|---|
| 70 | if (!xs_transaction_end(h, false)) { |
|---|
| 71 | if (errno == EAGAIN) { |
|---|
| 72 | write(STDOUT_FILENO, "!", 1); |
|---|
| 73 | i--; |
|---|
| 74 | } else |
|---|
| 75 | barf_perror("%i: can't commit trans", |
|---|
| 76 | childnum); |
|---|
| 77 | } else { |
|---|
| 78 | /* Offset when we print . so kids don't all |
|---|
| 79 | * print at once. */ |
|---|
| 80 | if ((i + print/(childnum+1)) % print == 0) |
|---|
| 81 | write(STDOUT_FILENO, &id, 1); |
|---|
| 82 | } |
|---|
| 83 | } |
|---|
| 84 | } |
|---|
| 85 | } |
|---|
| 86 | |
|---|
| 87 | static void create_dirs(struct xs_handle *h, const char *base, int togo) |
|---|
| 88 | { |
|---|
| 89 | unsigned int i; |
|---|
| 90 | char filename[100]; |
|---|
| 91 | |
|---|
| 92 | if (togo == 0) { |
|---|
| 93 | sprintf(filename, "%s/count", base); |
|---|
| 94 | if (!xs_write(h, filename, "0", 1)) |
|---|
| 95 | barf_perror("Writing to %s", filename); |
|---|
| 96 | return; |
|---|
| 97 | } |
|---|
| 98 | |
|---|
| 99 | for (i = 0; i < DIR_FANOUT; i++) { |
|---|
| 100 | sprintf(filename, "%s/%i", base, i); |
|---|
| 101 | if (!xs_mkdir(h, filename)) |
|---|
| 102 | barf_perror("xs_mkdir %s", filename); |
|---|
| 103 | create_dirs(h, filename, togo-1); |
|---|
| 104 | } |
|---|
| 105 | } |
|---|
| 106 | |
|---|
| 107 | static unsigned int add_count(struct xs_handle *h, const char *base, int togo) |
|---|
| 108 | { |
|---|
| 109 | unsigned int i, count; |
|---|
| 110 | char filename[100]; |
|---|
| 111 | |
|---|
| 112 | if (togo == 0) { |
|---|
| 113 | char *answer; |
|---|
| 114 | unsigned int len; |
|---|
| 115 | |
|---|
| 116 | sprintf(filename, "%s/count", base); |
|---|
| 117 | answer = xs_read(h, filename, &len); |
|---|
| 118 | if (!answer) |
|---|
| 119 | barf_perror("Reading %s", filename); |
|---|
| 120 | count = atoi(answer); |
|---|
| 121 | free(answer); |
|---|
| 122 | return count; |
|---|
| 123 | } |
|---|
| 124 | |
|---|
| 125 | count = 0; |
|---|
| 126 | for (i = 0; i < DIR_FANOUT; i++) { |
|---|
| 127 | sprintf(filename, "%s/%i", base, i); |
|---|
| 128 | count += add_count(h, filename, togo-1); |
|---|
| 129 | } |
|---|
| 130 | return count; |
|---|
| 131 | } |
|---|
| 132 | |
|---|
| 133 | static void setup(void) |
|---|
| 134 | { |
|---|
| 135 | struct xs_handle *h; |
|---|
| 136 | |
|---|
| 137 | /* Do setup. */ |
|---|
| 138 | h = xs_daemon_open(); |
|---|
| 139 | if (!h) |
|---|
| 140 | barf_perror("Contacting daemon"); |
|---|
| 141 | create_dirs(h, "", DIR_DEPTH); |
|---|
| 142 | xs_daemon_close(h); |
|---|
| 143 | } |
|---|
| 144 | |
|---|
| 145 | static unsigned int tally_counts(void) |
|---|
| 146 | { |
|---|
| 147 | struct xs_handle *h; |
|---|
| 148 | unsigned int ret; |
|---|
| 149 | |
|---|
| 150 | h = xs_daemon_open(); |
|---|
| 151 | if (!h) |
|---|
| 152 | barf_perror("Contacting daemon"); |
|---|
| 153 | |
|---|
| 154 | ret = add_count(h, "", DIR_DEPTH); |
|---|
| 155 | xs_daemon_close(h); |
|---|
| 156 | return ret; |
|---|
| 157 | } |
|---|
| 158 | |
|---|
| 159 | int main(int argc, char *argv[]) |
|---|
| 160 | { |
|---|
| 161 | unsigned int i; |
|---|
| 162 | bool failed = false; |
|---|
| 163 | int kids[10]; |
|---|
| 164 | |
|---|
| 165 | if (argc != 2) |
|---|
| 166 | barf("Usage: xs_stress <iterations>"); |
|---|
| 167 | |
|---|
| 168 | printf("Setting up directories...\n"); |
|---|
| 169 | setup(); |
|---|
| 170 | |
|---|
| 171 | print = atoi(argv[1]) / 76; |
|---|
| 172 | if (!print) |
|---|
| 173 | print = 1; |
|---|
| 174 | |
|---|
| 175 | printf("Running %i children...\n", ARRAY_SIZE(kids)); |
|---|
| 176 | for (i = 0; i < ARRAY_SIZE(kids); i++) { |
|---|
| 177 | kids[i] = fork(); |
|---|
| 178 | if (kids[i] == -1) |
|---|
| 179 | barf_perror("fork"); |
|---|
| 180 | if (kids[i] == 0) { |
|---|
| 181 | work(atoi(argv[1]) / ARRAY_SIZE(kids), i); |
|---|
| 182 | exit(0); |
|---|
| 183 | } |
|---|
| 184 | } |
|---|
| 185 | |
|---|
| 186 | for (i = 0; i < ARRAY_SIZE(kids); i++) { |
|---|
| 187 | int status; |
|---|
| 188 | if (waitpid(kids[i], &status, 0) == -1) |
|---|
| 189 | barf_perror("waitpid"); |
|---|
| 190 | if (!WIFEXITED(status)) |
|---|
| 191 | barf("Kid %i died via signal %i\n", |
|---|
| 192 | i, WTERMSIG(status)); |
|---|
| 193 | if (WEXITSTATUS(status) != 0) { |
|---|
| 194 | printf("Child %i exited %i\n", i, WEXITSTATUS(status)); |
|---|
| 195 | failed = true; |
|---|
| 196 | } |
|---|
| 197 | } |
|---|
| 198 | if (failed) |
|---|
| 199 | exit(1); |
|---|
| 200 | |
|---|
| 201 | printf("\nCounting results...\n"); |
|---|
| 202 | i = tally_counts(); |
|---|
| 203 | if (i != (unsigned)atoi(argv[1])) |
|---|
| 204 | barf("Total counts %i not %s", i, argv[1]); |
|---|
| 205 | printf("Success!\n"); |
|---|
| 206 | exit(0); |
|---|
| 207 | } |
|---|