]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/test_uuidd.c
Merge branch 'master' of https://github.com/Rufflewind/util-linux
[thirdparty/util-linux.git] / misc-utils / test_uuidd.c
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>
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
25 */
26 #include <error.h>
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
36 #include "uuid.h"
37 #include "c.h"
38 #include "xalloc.h"
39 #include "strutils.h"
40
41 #define LOG(level,args) if (loglev >= level) { fprintf args; }
42
43 size_t nprocesses = 4;
44 size_t nthreads = 4;
45 size_t nobjects = 4096;
46 size_t loglev = 1;
47
48 struct processentry {
49 pid_t pid;
50 int status;
51 };
52 typedef struct processentry process_t;
53
54 struct threadentry {
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() */
60 };
61 typedef struct threadentry thread_t;
62
63 /* this is in shared memory, keep it as small as possible */
64 struct objectentry {
65 uuid_t uuid;
66 pthread_t tid;
67 pid_t pid;
68 size_t idx;
69 };
70 typedef struct objectentry object_t;
71
72 static int shmem_id;
73 static object_t *objects;
74
75
76 static void __attribute__((__noreturn__)) usage(FILE *out)
77 {
78 fprintf(out, "\n %s [options]\n", program_invocation_short_name);
79
80 fprintf(out, " -p <num> number of of nprocesses (default:%zu)\n", nprocesses);
81 fprintf(out, " -t <num> number of nthreads (default:%zu)\n", nthreads);
82 fprintf(out, " -o <num> number of nobjects (default:%zu)\n", nobjects);
83 fprintf(out, " -l <level> log level (default:%zu)\n", loglev);
84 fprintf(out, " -h display help\n");
85
86 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
87 }
88
89 static void allocate_segment(int *id, void **address, size_t number, size_t size)
90 {
91 *id = shmget(IPC_PRIVATE, number * size, IPC_CREAT | 0600);
92 if (*id == -1)
93 err(EXIT_FAILURE, "shmget failed to create %zu bytes shared memory", number * size);
94
95 *address = shmat(*id, NULL, 0);
96 if (*address == (void *)-1)
97 err(EXIT_FAILURE, "shmat failed");
98
99 LOG(2, (stderr,
100 "allocate shared memory segment [id=%d,address=0x%p]\n",
101 *id, *address));
102
103 memset(*address, 0, number * size);
104 }
105
106 static void remove_segment(int id, void *address)
107 {
108 if (shmdt(address) == -1)
109 err(EXIT_FAILURE, "shmdt failed");
110 if (shmctl(id, IPC_RMID, NULL) == -1)
111 err(EXIT_FAILURE, "shmctl failed");
112 LOG(2,
113 (stderr,
114 "remove shared memory segment [id=%d,address=0x%p]\n",
115 id, address));
116 }
117
118 static void object_uuid_create(object_t * object)
119 {
120 uuid_generate_time(object->uuid);
121 }
122
123 static void object_uuid_to_string(object_t * object, char **string_uuid)
124 {
125 uuid_unparse(object->uuid, *string_uuid);
126 }
127
128 static int object_uuid_compare(const void *object1, const void *object2)
129 {
130 uuid_t *uuid1 = &((object_t *) object1)->uuid,
131 *uuid2 = &((object_t *) object2)->uuid;
132
133 return uuid_compare(*uuid1, *uuid2);
134 }
135
136 static void *create_uuids(thread_t *th)
137 {
138 size_t i;
139
140 for (i = th->index; i < th->index + nobjects; i++) {
141 object_t *obj = &objects[i];
142
143 object_uuid_create(obj);
144 obj->tid = th->tid;
145 obj->pid = th->proc->pid;
146 obj->idx = th->index + i;;
147 }
148 return 0;
149 }
150
151 static void *thread_body(void *arg)
152 {
153 thread_t *th = (thread_t *) arg;
154
155 return create_uuids(th);
156 }
157
158 static void create_nthreads(process_t *proc, size_t index)
159 {
160 thread_t *threads;
161 size_t i, ncreated = 0;
162 int rc;
163
164 threads = (thread_t *) xcalloc(nthreads, sizeof(thread_t));
165
166 for (i = 0; i < nthreads; i++) {
167 thread_t *th = &threads[i];
168
169 rc = pthread_attr_init(&th->thread_attr);
170 if (rc) {
171 error(0, rc, "%d: pthread_attr_init failed", proc->pid);
172 break;
173 }
174
175 th->index = index;
176 th->proc = proc;
177 rc = pthread_create(&th->tid, &th->thread_attr, &thread_body, th);
178
179 if (rc) {
180 error(0, rc, "%d: pthread_create failed", proc->pid);
181 break;
182 }
183
184 LOG(2, (stderr, "%d: started thread [tid=%d,index=%zu]\n",
185 proc->pid, (int) th->tid, th->index));
186 index += nobjects;
187 ncreated++;
188 }
189
190 if (ncreated != nthreads)
191 fprintf(stderr, "%d: %zu threads not craeted and ~%zu objects will be ignored\n",
192 proc->pid, nthreads - ncreated,
193 (nthreads - ncreated) * nobjects);
194
195 for (i = 0; i < ncreated; i++) {
196 thread_t *th = &threads[i];
197
198 rc = pthread_join(th->tid, (void *) &th->retval);
199 if (rc)
200 error(EXIT_FAILURE, rc, "pthread_join failed");
201
202 LOG(2, (stderr, "%d: thread exited [tid=%d,return=%d]\n",
203 proc->pid, (int) th->tid, th->retval));
204 }
205 }
206
207 static void create_nprocesses(void)
208 {
209 process_t *process;
210 size_t i;
211
212 process = (process_t *) xcalloc(nprocesses, sizeof(process_t));
213
214 for (i = 0; i < nprocesses; i++) {
215 process_t *proc = &process[i];
216
217 proc->pid = fork();
218 switch (proc->pid) {
219 case -1: /* error */
220 err(EXIT_FAILURE, "fork failed");
221 break;
222 case 0: /* child */
223 proc->pid = getpid();
224 create_nthreads(proc, i * nthreads * nobjects);
225 exit(EXIT_SUCCESS);
226 break;
227 default: /* parent */
228 LOG(2, (stderr, "started process [pid=%d]\n", proc->pid));
229 break;
230 }
231 }
232
233 for (i = 0; i < nprocesses; i++) {
234 process_t *proc = &process[i];
235
236 if (waitpid(proc->pid, &proc->status, 0) == (pid_t) - 1)
237 err(EXIT_FAILURE, "waitpid failed");
238 LOG(2,
239 (stderr, "process exited [pid=%d,status=%d]\n",
240 proc->pid, proc->status));
241 }
242 }
243
244 static void object_dump(size_t idx, object_t *obj)
245 {
246 char uuid_string[37], *p;
247
248 p = uuid_string;
249 object_uuid_to_string(obj, &p);
250
251 fprintf(stderr, "object[%zu]: {\n", idx);
252 fprintf(stderr, " uuid: <%s>\n", p);
253 fprintf(stderr, " idx: %zu\n", obj->idx);
254 fprintf(stderr, " process: %d\n", (int) obj->pid);
255 fprintf(stderr, " thread: %d\n", (int) obj->tid);
256 fprintf(stderr, "}\n");
257 }
258
259 int main(int argc, char *argv[])
260 {
261 size_t i, nfailed = 0, nignored = 0;
262 int c;
263
264 while (((c = getopt(argc, argv, "p:t:o:l:h")) != -1)) {
265 switch (c) {
266 case 'p':
267 nprocesses = strtou32_or_err(optarg, "invalid nprocesses number argument");
268 break;
269 case 't':
270 nthreads = strtou32_or_err(optarg, "invalid nthreads number argument");
271 break;
272 case 'o':
273 nobjects = strtou32_or_err(optarg, "invalid nobjects number argument");
274 break;
275 case 'l':
276 loglev = strtou32_or_err(optarg, "invalid log level argument");
277 break;
278 case 'h':
279 usage(stdout);
280 break;
281 default:
282 usage(stderr);
283 break;
284 }
285 }
286
287 if (optind != argc)
288 usage(stderr);
289
290 if (loglev == 1)
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));
295
296 allocate_segment(&shmem_id, (void **)&objects,
297 nprocesses * nthreads * nobjects, sizeof(object_t));
298
299 create_nprocesses();
300
301 if (loglev >= 3) {
302 for (i = 0; i < nprocesses * nthreads * nobjects; i++)
303 object_dump(i, &objects[i]);
304 }
305
306 qsort(objects, nprocesses * nthreads * nobjects, sizeof(object_t),
307 object_uuid_compare);
308
309 for (i = 0; i < nprocesses * nthreads * nobjects - 1; i++) {
310 object_t *obj1 = &objects[i],
311 *obj2 = &objects[i + 1];
312
313 if (!obj1->tid) {
314 LOG(3, (stderr, "ignore unused object #%zu\n", i));
315 nignored++;
316 continue;
317 }
318
319 if (object_uuid_compare(obj1, obj2) == 0) {
320 if (loglev >= 1)
321 fprintf(stderr, "nobjects #%zu and #%zu have duplicate UUIDs\n",
322 i, i + 1);
323 object_dump(i, obj1),
324 object_dump(i + 1, obj2);
325 nfailed++;
326 }
327 }
328
329 remove_segment(shmem_id, objects);
330 if (nignored)
331 printf("%zu objects ignored\n", nignored);
332 if (!nfailed)
333 printf("test successful (no duplicate UUIDs found)\n");
334 else
335 printf("test failed (found %zu duplicate UUIDs)\n", nfailed);
336
337 return nfailed ? EXIT_FAILURE : EXIT_SUCCESS;
338 }