]>
Commit | Line | Data |
---|---|---|
0f2b00ce KZ |
1 | /* |
2 | * Copyright (C) 2006 Hewlett-Packard Development Company, L.P. | |
3 | * Huschaam Hussain <Huschaam.Hussain@hp.com> | |
4 | * TSG Solution Alliances Engineering | |
5 | * SAP Technology Group | |
6 | * | |
7 | * Copyright (C) 2015 Karel Zak <kzak@redhat.com> | |
28f47fd2 KZ |
8 | * |
9 | * | |
10 | * The test heavily uses shared memory, to enlarge maximal size of shared | |
11 | * segment use: | |
12 | * | |
13 | * echo "4294967295" > /proc/sys/kernel/shmm | |
14 | * | |
15 | * The test is compiled against in-tree libuuid, if you want to test uuidd | |
16 | * installed to the system then make sure that libuuid uses the same socket | |
17 | * like the running uuidd. You can start the uuidd manually, for example: | |
18 | * | |
19 | * uuidd --debug --no-fork --no-pid --socket /run/uuidd/request | |
20 | * | |
21 | * if the $localstatedir (as defined by build-system) is /run. If you want | |
22 | * to overwrite the built-in default then use: | |
23 | * | |
24 | * make uuidd uuidgen localstatedir=/var | |
0f2b00ce KZ |
25 | */ |
26 | #include <error.h> | |
963f128c HH |
27 | #include <pthread.h> |
28 | #include <stdio.h> | |
29 | #include <stdlib.h> | |
30 | #include <string.h> | |
31 | #include <unistd.h> | |
32 | #include <sys/shm.h> | |
33 | #include <sys/types.h> | |
34 | #include <sys/wait.h> | |
35 | ||
adbc73eb | 36 | #include "uuid.h" |
0f2b00ce KZ |
37 | #include "c.h" |
38 | #include "xalloc.h" | |
39 | #include "strutils.h" | |
677ec86c | 40 | #include "nls.h" |
0f2b00ce | 41 | |
17930612 | 42 | #define LOG(level,args) if (loglev >= level) { fprintf args; } |
963f128c | 43 | |
17930612 KZ |
44 | size_t nprocesses = 4; |
45 | size_t nthreads = 4; | |
46 | size_t nobjects = 4096; | |
47 | size_t loglev = 1; | |
f4be1406 KZ |
48 | |
49 | struct processentry { | |
50 | pid_t pid; | |
51 | int status; | |
52 | }; | |
53 | typedef struct processentry process_t; | |
963f128c HH |
54 | |
55 | struct threadentry { | |
f4be1406 KZ |
56 | process_t *proc; |
57 | pthread_t tid; /* pthread_self() / phtread_create() */ | |
58 | pthread_attr_t thread_attr; | |
59 | size_t index; /* index in object[] */ | |
60 | int retval; /* pthread exit() */ | |
963f128c HH |
61 | }; |
62 | typedef struct threadentry thread_t; | |
63 | ||
4d524158 | 64 | /* this is in shared memory, keep it as small as possible */ |
963f128c | 65 | struct objectentry { |
f4be1406 | 66 | uuid_t uuid; |
a539def5 KZ |
67 | pthread_t tid; |
68 | pid_t pid; | |
69 | size_t idx; | |
963f128c HH |
70 | }; |
71 | typedef struct objectentry object_t; | |
72 | ||
f4be1406 | 73 | static int shmem_id; |
1d27cd11 | 74 | static object_t *objects; |
963f128c | 75 | |
963f128c | 76 | |
0f2b00ce | 77 | static void __attribute__((__noreturn__)) usage(FILE *out) |
963f128c | 78 | { |
0f2b00ce | 79 | fprintf(out, "\n %s [options]\n", program_invocation_short_name); |
963f128c | 80 | |
17930612 KZ |
81 | fprintf(out, " -p <num> number of of nprocesses (default:%zu)\n", nprocesses); |
82 | fprintf(out, " -t <num> number of nthreads (default:%zu)\n", nthreads); | |
83 | fprintf(out, " -o <num> number of nobjects (default:%zu)\n", nobjects); | |
84 | fprintf(out, " -l <level> log level (default:%zu)\n", loglev); | |
0f2b00ce | 85 | fprintf(out, " -h display help\n"); |
963f128c | 86 | |
0f2b00ce KZ |
87 | exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); |
88 | } | |
963f128c | 89 | |
0f2b00ce | 90 | static void allocate_segment(int *id, void **address, size_t number, size_t size) |
963f128c HH |
91 | { |
92 | *id = shmget(IPC_PRIVATE, number * size, IPC_CREAT | 0600); | |
93 | if (*id == -1) | |
1d27cd11 | 94 | err(EXIT_FAILURE, "shmget failed to create %zu bytes shared memory", number * size); |
0f2b00ce | 95 | |
963f128c HH |
96 | *address = shmat(*id, NULL, 0); |
97 | if (*address == (void *)-1) | |
0f2b00ce KZ |
98 | err(EXIT_FAILURE, "shmat failed"); |
99 | ||
100 | LOG(2, (stderr, | |
7eeb7e1d KZ |
101 | "allocate shared memory segment [id=%d,address=0x%p]\n", |
102 | *id, *address)); | |
4d524158 KZ |
103 | |
104 | memset(*address, 0, number * size); | |
0f2b00ce | 105 | } |
963f128c | 106 | |
0f2b00ce | 107 | static void remove_segment(int id, void *address) |
963f128c HH |
108 | { |
109 | if (shmdt(address) == -1) | |
0f2b00ce | 110 | err(EXIT_FAILURE, "shmdt failed"); |
963f128c | 111 | if (shmctl(id, IPC_RMID, NULL) == -1) |
0f2b00ce | 112 | err(EXIT_FAILURE, "shmctl failed"); |
963f128c HH |
113 | LOG(2, |
114 | (stderr, | |
7eeb7e1d KZ |
115 | "remove shared memory segment [id=%d,address=0x%p]\n", |
116 | id, address)); | |
0f2b00ce | 117 | } |
963f128c | 118 | |
0f2b00ce | 119 | static void object_uuid_create(object_t * object) |
963f128c HH |
120 | { |
121 | uuid_generate_time(object->uuid); | |
0f2b00ce | 122 | } |
963f128c | 123 | |
f4be1406 | 124 | static void object_uuid_to_string(object_t * object, char **string_uuid) |
963f128c HH |
125 | { |
126 | uuid_unparse(object->uuid, *string_uuid); | |
0f2b00ce | 127 | } |
963f128c | 128 | |
0f2b00ce | 129 | static int object_uuid_compare(const void *object1, const void *object2) |
963f128c | 130 | { |
f4be1406 KZ |
131 | uuid_t *uuid1 = &((object_t *) object1)->uuid, |
132 | *uuid2 = &((object_t *) object2)->uuid; | |
963f128c | 133 | |
f4be1406 | 134 | return uuid_compare(*uuid1, *uuid2); |
0f2b00ce | 135 | } |
963f128c | 136 | |
f4be1406 | 137 | static void *create_uuids(thread_t *th) |
963f128c | 138 | { |
f4be1406 | 139 | size_t i; |
963f128c | 140 | |
17930612 | 141 | for (i = th->index; i < th->index + nobjects; i++) { |
1d27cd11 KZ |
142 | object_t *obj = &objects[i]; |
143 | ||
144 | object_uuid_create(obj); | |
a539def5 KZ |
145 | obj->tid = th->tid; |
146 | obj->pid = th->proc->pid; | |
147 | obj->idx = th->index + i;; | |
0f2b00ce | 148 | } |
f4be1406 | 149 | return 0; |
0f2b00ce | 150 | } |
963f128c | 151 | |
f4be1406 KZ |
152 | static void *thread_body(void *arg) |
153 | { | |
154 | thread_t *th = (thread_t *) arg; | |
155 | ||
156 | return create_uuids(th); | |
157 | } | |
158 | ||
17930612 | 159 | static void create_nthreads(process_t *proc, size_t index) |
963f128c | 160 | { |
1d27cd11 | 161 | thread_t *threads; |
4d524158 | 162 | size_t i, ncreated = 0; |
1d27cd11 KZ |
163 | int rc; |
164 | ||
fea1cbf7 | 165 | threads = xcalloc(nthreads, sizeof(thread_t)); |
963f128c | 166 | |
17930612 | 167 | for (i = 0; i < nthreads; i++) { |
1d27cd11 | 168 | thread_t *th = &threads[i]; |
0f2b00ce | 169 | |
1d27cd11 | 170 | rc = pthread_attr_init(&th->thread_attr); |
4d524158 KZ |
171 | if (rc) { |
172 | error(0, rc, "%d: pthread_attr_init failed", proc->pid); | |
173 | break; | |
174 | } | |
1d27cd11 KZ |
175 | |
176 | th->index = index; | |
177 | th->proc = proc; | |
178 | rc = pthread_create(&th->tid, &th->thread_attr, &thread_body, th); | |
179 | ||
4d524158 KZ |
180 | if (rc) { |
181 | error(0, rc, "%d: pthread_create failed", proc->pid); | |
182 | break; | |
183 | } | |
1d27cd11 KZ |
184 | |
185 | LOG(2, (stderr, "%d: started thread [tid=%d,index=%zu]\n", | |
186 | proc->pid, (int) th->tid, th->index)); | |
17930612 | 187 | index += nobjects; |
4d524158 | 188 | ncreated++; |
0f2b00ce KZ |
189 | } |
190 | ||
4d524158 | 191 | if (ncreated != nthreads) |
9e930041 | 192 | fprintf(stderr, "%d: %zu threads not created and ~%zu objects will be ignored\n", |
4d524158 KZ |
193 | proc->pid, nthreads - ncreated, |
194 | (nthreads - ncreated) * nobjects); | |
195 | ||
196 | for (i = 0; i < ncreated; i++) { | |
1d27cd11 KZ |
197 | thread_t *th = &threads[i]; |
198 | ||
199 | rc = pthread_join(th->tid, (void *) &th->retval); | |
200 | if (rc) | |
201 | error(EXIT_FAILURE, rc, "pthread_join failed"); | |
202 | ||
203 | LOG(2, (stderr, "%d: thread exited [tid=%d,return=%d]\n", | |
204 | proc->pid, (int) th->tid, th->retval)); | |
0f2b00ce | 205 | } |
0f2b00ce | 206 | } |
963f128c | 207 | |
17930612 | 208 | static void create_nprocesses(void) |
963f128c HH |
209 | { |
210 | process_t *process; | |
f4be1406 | 211 | size_t i; |
963f128c | 212 | |
fea1cbf7 | 213 | process = xcalloc(nprocesses, sizeof(process_t)); |
1d27cd11 | 214 | |
17930612 | 215 | for (i = 0; i < nprocesses; i++) { |
1d27cd11 KZ |
216 | process_t *proc = &process[i]; |
217 | ||
218 | proc->pid = fork(); | |
219 | switch (proc->pid) { | |
7eeb7e1d | 220 | case -1: /* error */ |
0f2b00ce | 221 | err(EXIT_FAILURE, "fork failed"); |
963f128c | 222 | break; |
7eeb7e1d | 223 | case 0: /* child */ |
1d27cd11 KZ |
224 | proc->pid = getpid(); |
225 | create_nthreads(proc, i * nthreads * nobjects); | |
0f2b00ce | 226 | exit(EXIT_SUCCESS); |
963f128c | 227 | break; |
7eeb7e1d | 228 | default: /* parent */ |
1d27cd11 | 229 | LOG(2, (stderr, "started process [pid=%d]\n", proc->pid)); |
963f128c | 230 | break; |
0f2b00ce KZ |
231 | } |
232 | } | |
233 | ||
17930612 | 234 | for (i = 0; i < nprocesses; i++) { |
1d27cd11 | 235 | process_t *proc = &process[i]; |
0f2b00ce | 236 | |
1d27cd11 KZ |
237 | if (waitpid(proc->pid, &proc->status, 0) == (pid_t) - 1) |
238 | err(EXIT_FAILURE, "waitpid failed"); | |
963f128c | 239 | LOG(2, |
7eeb7e1d | 240 | (stderr, "process exited [pid=%d,status=%d]\n", |
1d27cd11 | 241 | proc->pid, proc->status)); |
0f2b00ce | 242 | } |
0f2b00ce | 243 | } |
963f128c | 244 | |
1d27cd11 | 245 | static void object_dump(size_t idx, object_t *obj) |
963f128c | 246 | { |
f4be1406 | 247 | char uuid_string[37], *p; |
963f128c HH |
248 | |
249 | p = uuid_string; | |
1d27cd11 KZ |
250 | object_uuid_to_string(obj, &p); |
251 | ||
032776e8 KZ |
252 | fprintf(stderr, "object[%zu]: {\n", idx); |
253 | fprintf(stderr, " uuid: <%s>\n", p); | |
a539def5 KZ |
254 | fprintf(stderr, " idx: %zu\n", obj->idx); |
255 | fprintf(stderr, " process: %d\n", (int) obj->pid); | |
256 | fprintf(stderr, " thread: %d\n", (int) obj->tid); | |
032776e8 | 257 | fprintf(stderr, "}\n"); |
0f2b00ce | 258 | } |
963f128c | 259 | |
0f2b00ce | 260 | int main(int argc, char *argv[]) |
963f128c | 261 | { |
1d27cd11 | 262 | size_t i, nfailed = 0, nignored = 0; |
0f2b00ce KZ |
263 | int c; |
264 | ||
265 | while (((c = getopt(argc, argv, "p:t:o:l:h")) != -1)) { | |
266 | switch (c) { | |
267 | case 'p': | |
17930612 | 268 | nprocesses = strtou32_or_err(optarg, "invalid nprocesses number argument"); |
0f2b00ce KZ |
269 | break; |
270 | case 't': | |
17930612 | 271 | nthreads = strtou32_or_err(optarg, "invalid nthreads number argument"); |
0f2b00ce KZ |
272 | break; |
273 | case 'o': | |
17930612 | 274 | nobjects = strtou32_or_err(optarg, "invalid nobjects number argument"); |
0f2b00ce KZ |
275 | break; |
276 | case 'l': | |
17930612 | 277 | loglev = strtou32_or_err(optarg, "invalid log level argument"); |
0f2b00ce KZ |
278 | break; |
279 | case 'h': | |
280 | usage(stdout); | |
281 | break; | |
282 | default: | |
677ec86c | 283 | errtryh(EXIT_FAILURE); |
0f2b00ce KZ |
284 | } |
285 | } | |
286 | ||
287 | if (optind != argc) | |
288 | usage(stderr); | |
289 | ||
17930612 | 290 | if (loglev == 1) |
1d27cd11 KZ |
291 | fprintf(stderr, "requested: %zu processes, %zu threads, %zu objects per thread (%zu objects = %zu bytes)\n", |
292 | nprocesses, nthreads, nobjects, | |
293 | nprocesses * nthreads * nobjects, | |
294 | nprocesses * nthreads * nobjects * sizeof(object_t)); | |
0f2b00ce | 295 | |
1d27cd11 | 296 | allocate_segment(&shmem_id, (void **)&objects, |
17930612 | 297 | nprocesses * nthreads * nobjects, sizeof(object_t)); |
1d27cd11 | 298 | |
17930612 | 299 | create_nprocesses(); |
1d27cd11 | 300 | |
17930612 KZ |
301 | if (loglev >= 3) { |
302 | for (i = 0; i < nprocesses * nthreads * nobjects; i++) | |
1d27cd11 | 303 | object_dump(i, &objects[i]); |
0f2b00ce | 304 | } |
7eeb7e1d | 305 | |
1d27cd11 | 306 | qsort(objects, nprocesses * nthreads * nobjects, sizeof(object_t), |
963f128c | 307 | object_uuid_compare); |
1d27cd11 | 308 | |
17930612 | 309 | for (i = 0; i < nprocesses * nthreads * nobjects - 1; i++) { |
1d27cd11 KZ |
310 | object_t *obj1 = &objects[i], |
311 | *obj2 = &objects[i + 1]; | |
312 | ||
a539def5 | 313 | if (!obj1->tid) { |
1d27cd11 KZ |
314 | LOG(3, (stderr, "ignore unused object #%zu\n", i)); |
315 | nignored++; | |
316 | continue; | |
317 | } | |
318 | ||
319 | if (object_uuid_compare(obj1, obj2) == 0) { | |
17930612 KZ |
320 | if (loglev >= 1) |
321 | fprintf(stderr, "nobjects #%zu and #%zu have duplicate UUIDs\n", | |
7eeb7e1d | 322 | i, i + 1); |
1d27cd11 KZ |
323 | object_dump(i, obj1), |
324 | object_dump(i + 1, obj2); | |
325 | nfailed++; | |
0f2b00ce KZ |
326 | } |
327 | } | |
1d27cd11 KZ |
328 | |
329 | remove_segment(shmem_id, objects); | |
330 | if (nignored) | |
cfa4171a | 331 | printf("%zu objects ignored\n", nignored); |
1d27cd11 | 332 | if (!nfailed) |
f4be1406 KZ |
333 | printf("test successful (no duplicate UUIDs found)\n"); |
334 | else | |
1d27cd11 | 335 | printf("test failed (found %zu duplicate UUIDs)\n", nfailed); |
4d524158 KZ |
336 | |
337 | return nfailed ? EXIT_FAILURE : EXIT_SUCCESS; | |
0f2b00ce | 338 | } |