2015-04-24 05:40:51 -05:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
|
|
|
|
* Huschaam Hussain <Huschaam.Hussain@hp.com>
|
|
|
|
* TSG Solution Alliances Engineering
|
|
|
|
* SAP Technology Group
|
|
|
|
*
|
|
|
|
* Copyright (C) 2015 Karel Zak <kzak@redhat.com>
|
|
|
|
*/
|
|
|
|
#include <error.h>
|
2015-04-24 04:28:47 -05:00
|
|
|
#include <libgen.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/shm.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
|
|
|
#include <uuid/uuid.h>
|
|
|
|
|
2015-04-24 05:40:51 -05:00
|
|
|
#include "c.h"
|
|
|
|
#include "xalloc.h"
|
|
|
|
#include "strutils.h"
|
|
|
|
|
2015-04-24 04:28:47 -05:00
|
|
|
#define LOG(level,args) if (logging >= level) { fprintf args; }
|
|
|
|
|
|
|
|
pid_t pid;
|
|
|
|
|
2015-04-24 06:28:12 -05:00
|
|
|
size_t processes = 4;
|
|
|
|
size_t threads = 4;
|
|
|
|
size_t objects = 4096;
|
|
|
|
size_t logging = 1;
|
|
|
|
|
|
|
|
struct processentry {
|
|
|
|
pid_t pid;
|
|
|
|
int status;
|
|
|
|
};
|
|
|
|
typedef struct processentry process_t;
|
2015-04-24 04:28:47 -05:00
|
|
|
|
|
|
|
struct threadentry {
|
2015-04-24 06:28:12 -05:00
|
|
|
process_t *proc;
|
|
|
|
pthread_t tid; /* pthread_self() / phtread_create() */
|
|
|
|
pthread_attr_t thread_attr;
|
|
|
|
size_t index; /* index in object[] */
|
|
|
|
int retval; /* pthread exit() */
|
2015-04-24 04:28:47 -05:00
|
|
|
};
|
|
|
|
typedef struct threadentry thread_t;
|
|
|
|
|
|
|
|
|
|
|
|
struct objectentry {
|
2015-04-24 06:28:12 -05:00
|
|
|
uuid_t uuid;
|
|
|
|
thread_t *thread;
|
|
|
|
size_t id;
|
2015-04-24 04:28:47 -05:00
|
|
|
};
|
|
|
|
typedef struct objectentry object_t;
|
|
|
|
|
2015-04-24 06:28:12 -05:00
|
|
|
static int shmem_id;
|
|
|
|
static object_t *object;
|
2015-04-24 04:28:47 -05:00
|
|
|
|
|
|
|
|
2015-04-24 05:40:51 -05:00
|
|
|
static void __attribute__((__noreturn__)) usage(FILE *out)
|
2015-04-24 04:28:47 -05:00
|
|
|
{
|
2015-04-24 05:40:51 -05:00
|
|
|
fprintf(out, "\n %s [options]\n", program_invocation_short_name);
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 06:28:12 -05:00
|
|
|
fprintf(out, " -p <num> number of of processes (default:%zu)\n", processes);
|
|
|
|
fprintf(out, " -t <num> number of threads (default:%zu)\n", threads);
|
|
|
|
fprintf(out, " -o <num> number of objects (default:%zu)\n", objects);
|
|
|
|
fprintf(out, " -l <level> log level (default:%zu)\n", logging);
|
2015-04-24 05:40:51 -05:00
|
|
|
fprintf(out, " -h display help\n");
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 05:40:51 -05:00
|
|
|
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
|
|
|
|
}
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 05:40:51 -05:00
|
|
|
static void allocate_segment(int *id, void **address, size_t number, size_t size)
|
2015-04-24 04:28:47 -05:00
|
|
|
{
|
|
|
|
*id = shmget(IPC_PRIVATE, number * size, IPC_CREAT | 0600);
|
|
|
|
if (*id == -1)
|
2015-04-24 05:40:51 -05:00
|
|
|
err(EXIT_FAILURE, "shmget failed");
|
|
|
|
|
2015-04-24 04:28:47 -05:00
|
|
|
*address = shmat(*id, NULL, 0);
|
|
|
|
if (*address == (void *)-1)
|
2015-04-24 05:40:51 -05:00
|
|
|
err(EXIT_FAILURE, "shmat failed");
|
|
|
|
|
|
|
|
LOG(2, (stderr,
|
|
|
|
"%d: allocate shared memory segment [id=%d,address=0x%p]\n",
|
|
|
|
pid, *id, *address));
|
|
|
|
}
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 05:40:51 -05:00
|
|
|
static void remove_segment(int id, void *address)
|
2015-04-24 04:28:47 -05:00
|
|
|
{
|
|
|
|
if (shmdt(address) == -1)
|
2015-04-24 05:40:51 -05:00
|
|
|
err(EXIT_FAILURE, "shmdt failed");
|
2015-04-24 04:28:47 -05:00
|
|
|
if (shmctl(id, IPC_RMID, NULL) == -1)
|
2015-04-24 05:40:51 -05:00
|
|
|
err(EXIT_FAILURE, "shmctl failed");
|
2015-04-24 04:28:47 -05:00
|
|
|
LOG(2,
|
|
|
|
(stderr,
|
2015-04-24 05:40:51 -05:00
|
|
|
"%d: remove shared memory segment [id=%d,address=0x%p]\n",
|
|
|
|
pid, id, address));
|
|
|
|
}
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 05:40:51 -05:00
|
|
|
static void object_uuid_create(object_t * object)
|
2015-04-24 04:28:47 -05:00
|
|
|
{
|
|
|
|
uuid_generate_time(object->uuid);
|
2015-04-24 05:40:51 -05:00
|
|
|
}
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 06:28:12 -05:00
|
|
|
static void object_uuid_to_string(object_t * object, char **string_uuid)
|
2015-04-24 04:28:47 -05:00
|
|
|
{
|
|
|
|
uuid_unparse(object->uuid, *string_uuid);
|
2015-04-24 05:40:51 -05:00
|
|
|
}
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 05:40:51 -05:00
|
|
|
static int object_uuid_compare(const void *object1, const void *object2)
|
2015-04-24 04:28:47 -05:00
|
|
|
{
|
2015-04-24 06:28:12 -05:00
|
|
|
uuid_t *uuid1 = &((object_t *) object1)->uuid,
|
|
|
|
*uuid2 = &((object_t *) object2)->uuid;
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 06:28:12 -05:00
|
|
|
return uuid_compare(*uuid1, *uuid2);
|
2015-04-24 05:40:51 -05:00
|
|
|
}
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 06:28:12 -05:00
|
|
|
static void *create_uuids(thread_t *th)
|
2015-04-24 04:28:47 -05:00
|
|
|
{
|
2015-04-24 06:28:12 -05:00
|
|
|
size_t i;
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 06:28:12 -05:00
|
|
|
for (i = th->index; i < th->index + objects; i++) {
|
2015-04-24 04:28:47 -05:00
|
|
|
object_uuid_create(&object[i]);
|
2015-04-24 06:28:12 -05:00
|
|
|
object[i].thread = th;
|
|
|
|
object[i].id = i - th->index;
|
2015-04-24 05:40:51 -05:00
|
|
|
}
|
2015-04-24 06:28:12 -05:00
|
|
|
return 0;
|
2015-04-24 05:40:51 -05:00
|
|
|
}
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 06:28:12 -05:00
|
|
|
static void *thread_body(void *arg)
|
|
|
|
{
|
|
|
|
thread_t *th = (thread_t *) arg;
|
|
|
|
|
|
|
|
return create_uuids(th);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void create_threads(process_t *proc, size_t index)
|
2015-04-24 04:28:47 -05:00
|
|
|
{
|
|
|
|
thread_t *thread;
|
2015-04-24 06:28:12 -05:00
|
|
|
size_t i, result;
|
2015-04-24 05:40:51 -05:00
|
|
|
pid_t pid = getpid();
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 05:40:51 -05:00
|
|
|
thread = (thread_t *) xcalloc(threads, sizeof(thread_t));
|
2015-04-24 04:28:47 -05:00
|
|
|
for (i = 0; i < threads; i++) {
|
|
|
|
result = pthread_attr_init(&thread[i].thread_attr);
|
2015-04-24 05:40:51 -05:00
|
|
|
if (result)
|
|
|
|
error(EXIT_FAILURE, result, "pthread_attr_init failed");
|
|
|
|
|
2015-04-24 06:28:12 -05:00
|
|
|
thread[i].index = index;
|
|
|
|
thread[i].proc = proc;
|
|
|
|
result = pthread_create(&thread[i].tid,
|
|
|
|
&thread[i].thread_attr,
|
|
|
|
&thread_body,
|
|
|
|
&thread[i]);
|
2015-04-24 05:40:51 -05:00
|
|
|
if (result)
|
|
|
|
error(EXIT_FAILURE, result, "pthread_create failed");
|
|
|
|
|
2015-04-24 04:28:47 -05:00
|
|
|
LOG(2,
|
2015-04-24 06:28:12 -05:00
|
|
|
(stderr, "%d: started thread [tid=%d,index=%zu]\n",
|
|
|
|
pid, (int) thread[i].tid, thread[i].index));
|
2015-04-24 05:40:51 -05:00
|
|
|
index += objects;
|
|
|
|
}
|
|
|
|
|
2015-04-24 04:28:47 -05:00
|
|
|
for (i = 0; i < threads; i++) {
|
2015-04-24 06:28:12 -05:00
|
|
|
result = pthread_join(thread[i].tid, (void *)&thread[i].retval);
|
2015-04-24 05:40:51 -05:00
|
|
|
if (result)
|
|
|
|
error(EXIT_FAILURE, result, "pthread_join failed");
|
2015-04-24 04:28:47 -05:00
|
|
|
LOG(2,
|
2015-04-24 06:28:12 -05:00
|
|
|
(stderr, "%d: thread exited [tid=%d,return=%d]\n",
|
|
|
|
pid, (int) thread[i].tid, thread[i].retval));
|
2015-04-24 05:40:51 -05:00
|
|
|
}
|
2015-04-24 04:28:47 -05:00
|
|
|
free(thread);
|
2015-04-24 05:40:51 -05:00
|
|
|
}
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 06:28:12 -05:00
|
|
|
static void create_processes(void)
|
2015-04-24 04:28:47 -05:00
|
|
|
{
|
|
|
|
process_t *process;
|
2015-04-24 06:28:12 -05:00
|
|
|
size_t i;
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 05:40:51 -05:00
|
|
|
process = (process_t *) xcalloc(processes, sizeof(process_t));
|
2015-04-24 04:28:47 -05:00
|
|
|
for (i = 0; i < processes; i++) {
|
|
|
|
process[i].pid = fork();
|
|
|
|
switch (process[i].pid) {
|
|
|
|
case -1:
|
2015-04-24 05:40:51 -05:00
|
|
|
err(EXIT_FAILURE, "fork failed");
|
2015-04-24 04:28:47 -05:00
|
|
|
break;
|
|
|
|
case 0:
|
2015-04-24 06:28:12 -05:00
|
|
|
create_threads(&process[i], i * threads * objects);
|
2015-04-24 05:40:51 -05:00
|
|
|
exit(EXIT_SUCCESS);
|
2015-04-24 04:28:47 -05:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG(2,
|
2015-04-24 05:40:51 -05:00
|
|
|
(stderr, "%d: started process [pid=%d]\n",
|
|
|
|
pid, process[i].pid));
|
2015-04-24 04:28:47 -05:00
|
|
|
break;
|
2015-04-24 05:40:51 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-24 04:28:47 -05:00
|
|
|
for (i = 0; i < processes; i++) {
|
|
|
|
if (waitpid(process[i].pid, &process[i].status, 0) ==
|
|
|
|
(pid_t) - 1)
|
2015-04-24 05:40:51 -05:00
|
|
|
err(EXIT_FAILURE, "waitpid failed");
|
|
|
|
|
2015-04-24 04:28:47 -05:00
|
|
|
LOG(2,
|
2015-04-24 05:40:51 -05:00
|
|
|
(stderr, "%d: process exited [pid=%d,status=%d]\n",
|
|
|
|
pid, process[i].pid, process[i].status));
|
|
|
|
}
|
2015-04-24 04:28:47 -05:00
|
|
|
free(process);
|
2015-04-24 05:40:51 -05:00
|
|
|
}
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 06:28:12 -05:00
|
|
|
static void object_dump(size_t i)
|
2015-04-24 04:28:47 -05:00
|
|
|
{
|
2015-04-24 06:28:12 -05:00
|
|
|
char uuid_string[37], *p;
|
2015-04-24 04:28:47 -05:00
|
|
|
|
|
|
|
p = uuid_string;
|
|
|
|
object_uuid_to_string(&object[i], &p);
|
2015-04-24 06:28:12 -05:00
|
|
|
fprintf(stderr, "%d: object[%zu]={uuid=<%s>,pid=%d,tid=%d,id=%zu}\n",
|
|
|
|
pid, i, p, object[i].thread->proc->pid, (int) object[i].thread->tid, object[i].id);
|
2015-04-24 05:40:51 -05:00
|
|
|
}
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 05:40:51 -05:00
|
|
|
int main(int argc, char *argv[])
|
2015-04-24 04:28:47 -05:00
|
|
|
{
|
2015-04-24 06:28:12 -05:00
|
|
|
size_t i, count;
|
2015-04-24 05:40:51 -05:00
|
|
|
int c;
|
|
|
|
|
|
|
|
while (((c = getopt(argc, argv, "p:t:o:l:h")) != -1)) {
|
|
|
|
switch (c) {
|
|
|
|
case 'p':
|
|
|
|
processes = strtou32_or_err(optarg, "invalid processes number argument");
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
threads = strtou32_or_err(optarg, "invalid threads number argument");
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
objects = strtou32_or_err(optarg, "invalid objects number argument");
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
logging = strtou32_or_err(optarg, "invalid log level argument");
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
usage(stdout);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage(stderr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (optind != argc)
|
|
|
|
usage(stderr);
|
|
|
|
|
|
|
|
if (logging == 1)
|
2015-04-24 06:28:12 -05:00
|
|
|
fprintf(stderr, "requested: %zu processes, %zu threads, %zu objects\n",
|
2015-04-24 05:40:51 -05:00
|
|
|
processes, threads, objects);
|
|
|
|
|
2015-04-24 04:28:47 -05:00
|
|
|
|
2015-04-24 06:28:12 -05:00
|
|
|
allocate_segment(&shmem_id, (void **)&object,
|
2015-04-24 04:28:47 -05:00
|
|
|
processes * threads * objects, sizeof(object_t));
|
|
|
|
create_processes();
|
|
|
|
if (logging >= 3) {
|
|
|
|
for (i = 0; i < processes * threads * objects; i++) {
|
|
|
|
object_dump(i);
|
2015-04-24 05:40:51 -05:00
|
|
|
}
|
|
|
|
}
|
2015-04-24 04:28:47 -05:00
|
|
|
qsort(object, processes * threads * objects, sizeof(object_t),
|
|
|
|
object_uuid_compare);
|
2015-04-24 05:40:51 -05:00
|
|
|
LOG(2, (stdout, "%d: qsort() done\n", pid));
|
2015-04-24 04:28:47 -05:00
|
|
|
count = 0;
|
|
|
|
for (i = 0; i < processes * threads * objects - 1; i++) {
|
|
|
|
if (object_uuid_compare(&object[i], &object[i + 1]) == 0) {
|
|
|
|
if (logging >= 1) {
|
2015-04-24 06:28:12 -05:00
|
|
|
fprintf(stderr, "%d: objects #%zu and #%zu have duplicate UUIDs\n",
|
|
|
|
pid, i, i + 1);
|
2015-04-24 04:28:47 -05:00
|
|
|
object_dump(i);
|
|
|
|
object_dump(i + 1);
|
2015-04-24 05:40:51 -05:00
|
|
|
}
|
2015-04-24 04:28:47 -05:00
|
|
|
count = count + 1;
|
2015-04-24 05:40:51 -05:00
|
|
|
}
|
|
|
|
}
|
2015-04-24 06:28:12 -05:00
|
|
|
remove_segment(shmem_id, object);
|
|
|
|
if (count == 0)
|
|
|
|
printf("test successful (no duplicate UUIDs found)\n");
|
|
|
|
else
|
|
|
|
printf("test failed (found %zu duplicate UUIDs)\n", count);
|
2015-04-24 05:40:51 -05:00
|
|
|
}
|