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