(root)/
util-linux-2.39/
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 $runstatedir (as defined by build-system) is /run. If you want
      22   * to overwrite the built-in default then use:
      23   *
      24   *	make uuidd uuidgen runstatedir=/var/run
      25   */
      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  
      35  #include "uuid.h"
      36  #include "c.h"
      37  #include "xalloc.h"
      38  #include "strutils.h"
      39  #include "nls.h"
      40  
      41  #define LOG(level,args) if (loglev >= level) { fprintf args; }
      42  
      43  static size_t nprocesses = 4;
      44  static size_t nthreads = 4;
      45  static size_t nobjects = 4096;
      46  static 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(void)
      77  {
      78  	printf("\n %s [options]\n", program_invocation_short_name);
      79  
      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");
      85  
      86  	exit(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 NULL;
     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 = 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  			errno = rc;
     172  			warn("%d: pthread_attr_init failed", proc->pid);
     173  			break;
     174  		}
     175  
     176  		th->index = index;
     177  		th->proc = proc;
     178  		rc = pthread_create(&th->tid, &th->thread_attr, &thread_body, th);
     179  
     180  		if (rc) {
     181  			errno = rc;
     182  			warn("%d: pthread_create failed", proc->pid);
     183  			break;
     184  		}
     185  
     186  		LOG(2, (stderr, "%d: started thread [tid=%jd,index=%zu]\n",
     187  		     proc->pid, (intmax_t) th->tid, th->index));
     188  		index += nobjects;
     189  		ncreated++;
     190  	}
     191  
     192  	if (ncreated != nthreads)
     193  		fprintf(stderr, "%d: %zu threads not created and ~%zu objects will be ignored\n",
     194  				proc->pid, nthreads - ncreated,
     195  				(nthreads - ncreated) * nobjects);
     196  
     197  	for (i = 0; i < ncreated; i++) {
     198  		thread_t *th = &threads[i];
     199  
     200  		rc = pthread_join(th->tid, (void *) &th->retval);
     201  		if (rc) {
     202  			errno = rc;
     203  			err(EXIT_FAILURE, "pthread_join failed");
     204  		}
     205  
     206  		LOG(2, (stderr, "%d: thread exited [tid=%jd,return=%d]\n",
     207  		     proc->pid, (intmax_t) th->tid, th->retval));
     208  	}
     209  
     210  	free(threads);
     211  }
     212  
     213  static void create_nprocesses(void)
     214  {
     215  	process_t *process;
     216  	size_t i;
     217  
     218  	process = xcalloc(nprocesses, sizeof(process_t));
     219  
     220  	for (i = 0; i < nprocesses; i++) {
     221  		process_t *proc = &process[i];
     222  
     223  		proc->pid = fork();
     224  		switch (proc->pid) {
     225  		case -1: /* error */
     226  			err(EXIT_FAILURE, "fork failed");
     227  			break;
     228  		case 0: /* child */
     229  			proc->pid = getpid();
     230  			create_nthreads(proc, i * nthreads * nobjects);
     231  			exit(EXIT_SUCCESS);
     232  			break;
     233  		default: /* parent */
     234  			LOG(2, (stderr, "started process [pid=%d]\n", proc->pid));
     235  			break;
     236  		}
     237  	}
     238  
     239  	for (i = 0; i < nprocesses; i++) {
     240  		process_t *proc = &process[i];
     241  
     242  		if (waitpid(proc->pid, &proc->status, 0) == (pid_t) - 1)
     243  			err(EXIT_FAILURE, "waitpid failed");
     244  		LOG(2,
     245  		    (stderr, "process exited [pid=%d,status=%d]\n",
     246  		     proc->pid, proc->status));
     247  	}
     248  
     249  	free(process);
     250  }
     251  
     252  static void object_dump(size_t idx, object_t *obj)
     253  {
     254  	char uuid_string[UUID_STR_LEN], *p;
     255  
     256  	p = uuid_string;
     257  	object_uuid_to_string(obj, &p);
     258  
     259  	fprintf(stderr, "object[%zu]: {\n", idx);
     260  	fprintf(stderr, "  uuid:    <%s>\n", p);
     261  	fprintf(stderr, "  idx:     %zu\n", obj->idx);
     262  	fprintf(stderr, "  process: %d\n", (int) obj->pid);
     263  	fprintf(stderr, "  thread:  %jd\n", (intmax_t) obj->tid);
     264  	fprintf(stderr, "}\n");
     265  }
     266  
     267  #define MSG_TRY_HELP "Try '-h' for help."
     268  
     269  int main(int argc, char *argv[])
     270  {
     271  	size_t i, nfailed = 0, nignored = 0;
     272  	int c;
     273  
     274  	while (((c = getopt(argc, argv, "p:t:o:l:h")) != -1)) {
     275  		switch (c) {
     276  		case 'p':
     277  			nprocesses = strtou32_or_err(optarg, "invalid nprocesses number argument");
     278  			break;
     279  		case 't':
     280  			nthreads = strtou32_or_err(optarg, "invalid nthreads number argument");
     281  			break;
     282  		case 'o':
     283  			nobjects = strtou32_or_err(optarg, "invalid nobjects number argument");
     284  			break;
     285  		case 'l':
     286  			loglev = strtou32_or_err(optarg, "invalid log level argument");
     287  			break;
     288  		case 'h':
     289  			usage();
     290  			break;
     291  		default:
     292  			fprintf(stderr, MSG_TRY_HELP);
     293  			exit(EXIT_FAILURE);
     294  		}
     295  	}
     296  
     297  	if (optind != argc)
     298  		errx(EXIT_FAILURE, "bad usage\n" MSG_TRY_HELP);
     299  
     300  	if (loglev == 1)
     301  		fprintf(stderr, "requested: %zu processes, %zu threads, %zu objects per thread (%zu objects = %zu bytes)\n",
     302  				nprocesses, nthreads, nobjects,
     303  				nprocesses * nthreads * nobjects,
     304  				nprocesses * nthreads * nobjects * sizeof(object_t));
     305  
     306  	allocate_segment(&shmem_id, (void **)&objects,
     307  			 nprocesses * nthreads * nobjects, sizeof(object_t));
     308  
     309  	create_nprocesses();
     310  
     311  	if (loglev >= 3) {
     312  		for (i = 0; i < nprocesses * nthreads * nobjects; i++)
     313  			object_dump(i, &objects[i]);
     314  	}
     315  
     316  	qsort(objects, nprocesses * nthreads * nobjects, sizeof(object_t),
     317  	      object_uuid_compare);
     318  
     319  	for (i = 0; i < nprocesses * nthreads * nobjects - 1; i++) {
     320  		object_t *obj1 = &objects[i],
     321  			 *obj2 = &objects[i + 1];
     322  
     323  		if (!obj1->tid) {
     324  			LOG(3, (stderr, "ignore unused object #%zu\n", i));
     325  			nignored++;
     326  			continue;
     327  		}
     328  
     329  		if (object_uuid_compare(obj1, obj2) == 0) {
     330  			if (loglev >= 1)
     331  				fprintf(stderr, "nobjects #%zu and #%zu have duplicate UUIDs\n",
     332  					i, i + 1);
     333  					object_dump(i, obj1),
     334  					object_dump(i + 1, obj2);
     335  			nfailed++;
     336  		}
     337  	}
     338  
     339  	remove_segment(shmem_id, objects);
     340  	if (nignored)
     341  		printf("%zu objects ignored\n", nignored);
     342  	if (!nfailed)
     343  		printf("test successful (no duplicate UUIDs found)\n");
     344  	else
     345  		printf("test failed (found %zu duplicate UUIDs)\n", nfailed);
     346  
     347  	return nfailed ? EXIT_FAILURE : EXIT_SUCCESS;
     348  }