Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  param_test.c   Sprache: C

 
// SPDX-License-Identifier: LGPL-2.1
#define _GNU_SOURCE
#include <assert.h>
#include <linux/membarrier.h>
#include <pthread.h>
#include <sched.h>
#include <stdatomic.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syscall.h>
#include <unistd.h>
#include <poll.h>
#include <sys/types.h>
#include <signal.h>
#include <errno.h>
#include <stddef.h>
#include <stdbool.h>

static inline pid_t rseq_gettid(void)
{
 return syscall(__NR_gettid);
}

#define NR_INJECT 9
static int loop_cnt[NR_INJECT + 1];

static int loop_cnt_1 asm("asm_loop_cnt_1") __attribute__((used));
static int loop_cnt_2 asm("asm_loop_cnt_2") __attribute__((used));
static int loop_cnt_3 asm("asm_loop_cnt_3") __attribute__((used));
static int loop_cnt_4 asm("asm_loop_cnt_4") __attribute__((used));
static int loop_cnt_5 asm("asm_loop_cnt_5") __attribute__((used));
static int loop_cnt_6 asm("asm_loop_cnt_6") __attribute__((used));

static int opt_modulo, verbose;

static int opt_yield, opt_signal, opt_sleep,
  opt_disable_rseq, opt_threads = 200,
  opt_disable_mod = 0, opt_test = 's';

static long long opt_reps = 5000;

static __thread __attribute__((tls_model("initial-exec")))
unsigned int signals_delivered;

#ifndef BENCHMARK

static __thread __attribute__((tls_model("initial-exec"), unused))
unsigned int yield_mod_cnt, nr_abort;

#define printf_verbose(fmt, ...)   \
 do {      \
  if (verbose)    \
   printf(fmt, ## __VA_ARGS__); \
 } while (0)

#ifdef __i386__

#define INJECT_ASM_REG "eax"

#define RSEQ_INJECT_CLOBBER \
 , INJECT_ASM_REG

#define RSEQ_INJECT_ASM(n) \
 "mov asm_loop_cnt_" #n ", %%" INJECT_ASM_REG "\n\t" \
 "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
 "jz 333f\n\t" \
 "222:\n\t" \
 "dec %%" INJECT_ASM_REG "\n\t" \
 "jnz 222b\n\t" \
 "333:\n\t"

#elif defined(__x86_64__)

#define INJECT_ASM_REG_P "rax"
#define INJECT_ASM_REG  "eax"

#define RSEQ_INJECT_CLOBBER \
 , INJECT_ASM_REG_P \
 , INJECT_ASM_REG

#define RSEQ_INJECT_ASM(n) \
 "lea asm_loop_cnt_" #n "(%%rip), %%" INJECT_ASM_REG_P "\n\t" \
 "mov (%%" INJECT_ASM_REG_P "), %%" INJECT_ASM_REG "\n\t" \
 "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
 "jz 333f\n\t" \
 "222:\n\t" \
 "dec %%" INJECT_ASM_REG "\n\t" \
 "jnz 222b\n\t" \
 "333:\n\t"

#elif defined(__s390__)

#define RSEQ_INJECT_INPUT \
 , [loop_cnt_1]"m"(loop_cnt[1]) \
 , [loop_cnt_2]"m"(loop_cnt[2]) \
 , [loop_cnt_3]"m"(loop_cnt[3]) \
 , [loop_cnt_4]"m"(loop_cnt[4]) \
 , [loop_cnt_5]"m"(loop_cnt[5]) \
 , [loop_cnt_6]"m"(loop_cnt[6])

#define INJECT_ASM_REG "r12"

#define RSEQ_INJECT_CLOBBER \
 , INJECT_ASM_REG

#define RSEQ_INJECT_ASM(n) \
 "l %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
 "ltr %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG "\n\t" \
 "je 333f\n\t" \
 "222:\n\t" \
 "ahi %%" INJECT_ASM_REG ", -1\n\t" \
 "jnz 222b\n\t" \
 "333:\n\t"

#elif defined(__ARMEL__)

#define RSEQ_INJECT_INPUT \
 , [loop_cnt_1]"m"(loop_cnt[1]) \
 , [loop_cnt_2]"m"(loop_cnt[2]) \
 , [loop_cnt_3]"m"(loop_cnt[3]) \
 , [loop_cnt_4]"m"(loop_cnt[4]) \
 , [loop_cnt_5]"m"(loop_cnt[5]) \
 , [loop_cnt_6]"m"(loop_cnt[6])

#define INJECT_ASM_REG "r4"

#define RSEQ_INJECT_CLOBBER \
 , INJECT_ASM_REG

#define RSEQ_INJECT_ASM(n) \
 "ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
 "cmp " INJECT_ASM_REG ", #0\n\t" \
 "beq 333f\n\t" \
 "222:\n\t" \
 "subs " INJECT_ASM_REG ", #1\n\t" \
 "bne 222b\n\t" \
 "333:\n\t"

#elif defined(__AARCH64EL__)

#define RSEQ_INJECT_INPUT \
 , [loop_cnt_1] "Qo" (loop_cnt[1]) \
 , [loop_cnt_2] "Qo" (loop_cnt[2]) \
 , [loop_cnt_3] "Qo" (loop_cnt[3]) \
 , [loop_cnt_4] "Qo" (loop_cnt[4]) \
 , [loop_cnt_5] "Qo" (loop_cnt[5]) \
 , [loop_cnt_6] "Qo" (loop_cnt[6])

#define INJECT_ASM_REG RSEQ_ASM_TMP_REG32

#define RSEQ_INJECT_ASM(n) \
 " ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n" \
 " cbz " INJECT_ASM_REG ", 333f\n"   \
 "222:\n"       \
 " sub " INJECT_ASM_REG ", " INJECT_ASM_REG ", #1\n" \
 " cbnz " INJECT_ASM_REG ", 222b\n"   \
 "333:\n"

#elif defined(__PPC__)

#define RSEQ_INJECT_INPUT \
 , [loop_cnt_1]"m"(loop_cnt[1]) \
 , [loop_cnt_2]"m"(loop_cnt[2]) \
 , [loop_cnt_3]"m"(loop_cnt[3]) \
 , [loop_cnt_4]"m"(loop_cnt[4]) \
 , [loop_cnt_5]"m"(loop_cnt[5]) \
 , [loop_cnt_6]"m"(loop_cnt[6])

#define INJECT_ASM_REG "r18"

#define RSEQ_INJECT_CLOBBER \
 , INJECT_ASM_REG

#define RSEQ_INJECT_ASM(n) \
 "lwz %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
 "cmpwi %%" INJECT_ASM_REG ", 0\n\t" \
 "beq 333f\n\t" \
 "222:\n\t" \
 "subic. %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG ", 1\n\t" \
 "bne 222b\n\t" \
 "333:\n\t"

#elif defined(__mips__)

#define RSEQ_INJECT_INPUT \
 , [loop_cnt_1]"m"(loop_cnt[1]) \
 , [loop_cnt_2]"m"(loop_cnt[2]) \
 , [loop_cnt_3]"m"(loop_cnt[3]) \
 , [loop_cnt_4]"m"(loop_cnt[4]) \
 , [loop_cnt_5]"m"(loop_cnt[5]) \
 , [loop_cnt_6]"m"(loop_cnt[6])

#define INJECT_ASM_REG "$5"

#define RSEQ_INJECT_CLOBBER \
 , INJECT_ASM_REG

#define RSEQ_INJECT_ASM(n) \
 "lw " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
 "beqz " INJECT_ASM_REG ", 333f\n\t" \
 "222:\n\t" \
 "addiu " INJECT_ASM_REG ", -1\n\t" \
 "bnez " INJECT_ASM_REG ", 222b\n\t" \
 "333:\n\t"
#elif defined(__riscv)

#define RSEQ_INJECT_INPUT \
 , [loop_cnt_1]"m"(loop_cnt[1]) \
 , [loop_cnt_2]"m"(loop_cnt[2]) \
 , [loop_cnt_3]"m"(loop_cnt[3]) \
 , [loop_cnt_4]"m"(loop_cnt[4]) \
 , [loop_cnt_5]"m"(loop_cnt[5]) \
 , [loop_cnt_6]"m"(loop_cnt[6])

#define INJECT_ASM_REG "t1"

#define RSEQ_INJECT_CLOBBER \
 , INJECT_ASM_REG

#define RSEQ_INJECT_ASM(n)     \
 "lw " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t"  \
 "beqz " INJECT_ASM_REG ", 333f\n\t"   \
 "222:\n\t"      \
 "addi " INJECT_ASM_REG "," INJECT_ASM_REG ", -1\n\t" \
 "bnez " INJECT_ASM_REG ", 222b\n\t"   \
 "333:\n\t"
#elif defined(__or1k__)

#define RSEQ_INJECT_INPUT \
 , [loop_cnt_1]"m"(loop_cnt[1]) \
 , [loop_cnt_2]"m"(loop_cnt[2]) \
 , [loop_cnt_3]"m"(loop_cnt[3]) \
 , [loop_cnt_4]"m"(loop_cnt[4]) \
 , [loop_cnt_5]"m"(loop_cnt[5]) \
 , [loop_cnt_6]"m"(loop_cnt[6])

#define INJECT_ASM_REG "r31"

#define RSEQ_INJECT_CLOBBER \
 , INJECT_ASM_REG

#define RSEQ_INJECT_ASM(n)     \
 "l.lwz " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
 "l.sfeqi " INJECT_ASM_REG ", 0\n\t"   \
 "l.bf 333f\n\t"      \
 " l.nop\n\t"      \
 "222:\n\t"      \
 "l.addi " INJECT_ASM_REG "," INJECT_ASM_REG ", -1\n\t" \
 "l.sfeqi " INJECT_ASM_REG ", 0\n\t"   \
 "l.bf 222f\n\t"      \
 " l.nop\n\t"      \
 "333:\n\t"
#else
#error unsupported target
#endif

#define RSEQ_INJECT_FAILED \
 nr_abort++;

#define RSEQ_INJECT_C(n) \
{ \
 int loc_i, loc_nr_loops = loop_cnt[n]; \
 \
 for (loc_i = 0; loc_i < loc_nr_loops; loc_i++) { \
  rseq_barrier(); \
 } \
 if (loc_nr_loops == -1 && opt_modulo) { \
  if (yield_mod_cnt == opt_modulo - 1) { \
   if (opt_sleep > 0) \
    poll(NULL, 0, opt_sleep); \
   if (opt_yield) \
    sched_yield(); \
   if (opt_signal) \
    raise(SIGUSR1); \
   yield_mod_cnt = 0; \
  } else { \
   yield_mod_cnt++; \
  } \
 } \
}

#else

#define printf_verbose(fmt, ...)

#endif /* BENCHMARK */

#include "rseq.h"

static enum rseq_mo opt_mo = RSEQ_MO_RELAXED;

#ifdef RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV
#define TEST_MEMBARRIER

static int sys_membarrier(int cmd, int flags, int cpu_id)
{
 return syscall(__NR_membarrier, cmd, flags, cpu_id);
}
#endif

#ifdef BUILDOPT_RSEQ_PERCPU_MM_CID
define RSEQ_PERCPU RSEQ_PERCPU_MM_CID
static
int get_current_cpu_id(void)
{
 return rseq_current_mm_cid();
}
static
bool rseq_validate_cpu_id(void)
{
 return rseq_mm_cid_available();
}
static
bool rseq_use_cpu_index(void)
{
 return false/* Use mm_cid */
}
ifdef TEST_MEMBARRIER
/*
 * Membarrier does not currently support targeting a mm_cid, so
 * issue the barrier on all cpus.
 */

static
int rseq_membarrier_expedited(int cpu)
{
 return sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ,
         0, 0);
}
endif /* TEST_MEMBARRIER */
#else
define RSEQ_PERCPU RSEQ_PERCPU_CPU_ID
static
int get_current_cpu_id(void)
{
 return rseq_cpu_start();
}
static
bool rseq_validate_cpu_id(void)
{
 return rseq_current_cpu_raw() >= 0;
}
static
bool rseq_use_cpu_index(void)
{
 return true/* Use cpu_id as index. */
}
ifdef TEST_MEMBARRIER
static
int rseq_membarrier_expedited(int cpu)
{
 return sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ,
         MEMBARRIER_CMD_FLAG_CPU, cpu);
}
endif /* TEST_MEMBARRIER */
#endif

struct percpu_lock_entry {
 intptr_t v;
} __attribute__((aligned(128)));

struct percpu_lock {
 struct percpu_lock_entry c[CPU_SETSIZE];
};

struct test_data_entry {
 intptr_t count;
} __attribute__((aligned(128)));

struct spinlock_test_data {
 struct percpu_lock lock;
 struct test_data_entry c[CPU_SETSIZE];
};

struct spinlock_thread_test_data {
 struct spinlock_test_data *data;
 long long reps;
 int reg;
};

struct inc_test_data {
 struct test_data_entry c[CPU_SETSIZE];
};

struct inc_thread_test_data {
 struct inc_test_data *data;
 long long reps;
 int reg;
};

struct percpu_list_node {
 intptr_t data;
 struct percpu_list_node *next;
};

struct percpu_list_entry {
 struct percpu_list_node *head;
} __attribute__((aligned(128)));

struct percpu_list {
 struct percpu_list_entry c[CPU_SETSIZE];
};

#define BUFFER_ITEM_PER_CPU 100

struct percpu_buffer_node {
 intptr_t data;
};

struct percpu_buffer_entry {
 intptr_t offset;
 intptr_t buflen;
 struct percpu_buffer_node **array;
} __attribute__((aligned(128)));

struct percpu_buffer {
 struct percpu_buffer_entry c[CPU_SETSIZE];
};

#define MEMCPY_BUFFER_ITEM_PER_CPU 100

struct percpu_memcpy_buffer_node {
 intptr_t data1;
 uint64_t data2;
};

struct percpu_memcpy_buffer_entry {
 intptr_t offset;
 intptr_t buflen;
 struct percpu_memcpy_buffer_node *array;
} __attribute__((aligned(128)));

struct percpu_memcpy_buffer {
 struct percpu_memcpy_buffer_entry c[CPU_SETSIZE];
};

/* A simple percpu spinlock. Grabs lock on current cpu. */
static int rseq_this_cpu_lock(struct percpu_lock *lock)
{
 int cpu;

 for (;;) {
  int ret;

  cpu = get_current_cpu_id();
  if (cpu < 0) {
   fprintf(stderr, "pid: %d: tid: %d, cpu: %d: cid: %d\n",
     getpid(), (int) rseq_gettid(), rseq_current_cpu_raw(), cpu);
   abort();
  }
  ret = rseq_cmpeqv_storev(RSEQ_MO_RELAXED, RSEQ_PERCPU,
      &lock->c[cpu].v,
      0, 1, cpu);
  if (rseq_likely(!ret))
   break;
  /* Retry if comparison fails or rseq aborts. */
 }
 /*
 * Acquire semantic when taking lock after control dependency.
 * Matches rseq_smp_store_release().
 */

 rseq_smp_acquire__after_ctrl_dep();
 return cpu;
}

static void rseq_percpu_unlock(struct percpu_lock *lock, int cpu)
{
 assert(lock->c[cpu].v == 1);
 /*
 * Release lock, with release semantic. Matches
 * rseq_smp_acquire__after_ctrl_dep().
 */

 rseq_smp_store_release(&lock->c[cpu].v, 0);
}

void *test_percpu_spinlock_thread(void *arg)
{
 struct spinlock_thread_test_data *thread_data = arg;
 struct spinlock_test_data *data = thread_data->data;
 long long i, reps;

 if (!opt_disable_rseq && thread_data->reg &&
     rseq_register_current_thread())
  abort();
 reps = thread_data->reps;
 for (i = 0; i < reps; i++) {
  int cpu = rseq_this_cpu_lock(&data->lock);
  data->c[cpu].count++;
  rseq_percpu_unlock(&data->lock, cpu);
#ifndef BENCHMARK
  if (i != 0 && !(i % (reps / 10)))
   printf_verbose("tid %d: count %lld\n",
           (int) rseq_gettid(), i);
#endif
 }
 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
         (int) rseq_gettid(), nr_abort, signals_delivered);
 if (!opt_disable_rseq && thread_data->reg &&
     rseq_unregister_current_thread())
  abort();
 return NULL;
}

/*
 * A simple test which implements a sharded counter using a per-cpu
 * lock.  Obviously real applications might prefer to simply use a
 * per-cpu increment; however, this is reasonable for a test and the
 * lock can be extended to synchronize more complicated operations.
 */

void test_percpu_spinlock(void)
{
 const int num_threads = opt_threads;
 int i, ret;
 uint64_t sum;
 pthread_t test_threads[num_threads];
 struct spinlock_test_data data;
 struct spinlock_thread_test_data thread_data[num_threads];

 memset(&data, 0, sizeof(data));
 for (i = 0; i < num_threads; i++) {
  thread_data[i].reps = opt_reps;
  if (opt_disable_mod <= 0 || (i % opt_disable_mod))
   thread_data[i].reg = 1;
  else
   thread_data[i].reg = 0;
  thread_data[i].data = &data;
  ret = pthread_create(&test_threads[i], NULL,
         test_percpu_spinlock_thread,
         &thread_data[i]);
  if (ret) {
   errno = ret;
   perror("pthread_create");
   abort();
  }
 }

 for (i = 0; i < num_threads; i++) {
  ret = pthread_join(test_threads[i], NULL);
  if (ret) {
   errno = ret;
   perror("pthread_join");
   abort();
  }
 }

 sum = 0;
 for (i = 0; i < CPU_SETSIZE; i++)
  sum += data.c[i].count;

 assert(sum == (uint64_t)opt_reps * num_threads);
}

void *test_percpu_inc_thread(void *arg)
{
 struct inc_thread_test_data *thread_data = arg;
 struct inc_test_data *data = thread_data->data;
 long long i, reps;

 if (!opt_disable_rseq && thread_data->reg &&
     rseq_register_current_thread())
  abort();
 reps = thread_data->reps;
 for (i = 0; i < reps; i++) {
  int ret;

  do {
   int cpu;

   cpu = get_current_cpu_id();
   ret = rseq_addv(RSEQ_MO_RELAXED, RSEQ_PERCPU,
     &data->c[cpu].count, 1, cpu);
  } while (rseq_unlikely(ret));
#ifndef BENCHMARK
  if (i != 0 && !(i % (reps / 10)))
   printf_verbose("tid %d: count %lld\n",
           (int) rseq_gettid(), i);
#endif
 }
 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
         (int) rseq_gettid(), nr_abort, signals_delivered);
 if (!opt_disable_rseq && thread_data->reg &&
     rseq_unregister_current_thread())
  abort();
 return NULL;
}

void test_percpu_inc(void)
{
 const int num_threads = opt_threads;
 int i, ret;
 uint64_t sum;
 pthread_t test_threads[num_threads];
 struct inc_test_data data;
 struct inc_thread_test_data thread_data[num_threads];

 memset(&data, 0, sizeof(data));
 for (i = 0; i < num_threads; i++) {
  thread_data[i].reps = opt_reps;
  if (opt_disable_mod <= 0 || (i % opt_disable_mod))
   thread_data[i].reg = 1;
  else
   thread_data[i].reg = 0;
  thread_data[i].data = &data;
  ret = pthread_create(&test_threads[i], NULL,
         test_percpu_inc_thread,
         &thread_data[i]);
  if (ret) {
   errno = ret;
   perror("pthread_create");
   abort();
  }
 }

 for (i = 0; i < num_threads; i++) {
  ret = pthread_join(test_threads[i], NULL);
  if (ret) {
   errno = ret;
   perror("pthread_join");
   abort();
  }
 }

 sum = 0;
 for (i = 0; i < CPU_SETSIZE; i++)
  sum += data.c[i].count;

 assert(sum == (uint64_t)opt_reps * num_threads);
}

void this_cpu_list_push(struct percpu_list *list,
   struct percpu_list_node *node,
   int *_cpu)
{
 int cpu;

 for (;;) {
  intptr_t *targetptr, newval, expect;
  int ret;

  cpu = get_current_cpu_id();
  /* Load list->c[cpu].head with single-copy atomicity. */
  expect = (intptr_t)RSEQ_READ_ONCE(list->c[cpu].head);
  newval = (intptr_t)node;
  targetptr = (intptr_t *)&list->c[cpu].head;
  node->next = (struct percpu_list_node *)expect;
  ret = rseq_cmpeqv_storev(RSEQ_MO_RELAXED, RSEQ_PERCPU,
      targetptr, expect, newval, cpu);
  if (rseq_likely(!ret))
   break;
  /* Retry if comparison fails or rseq aborts. */
 }
 if (_cpu)
  *_cpu = cpu;
}

/*
 * Unlike a traditional lock-less linked list; the availability of a
 * rseq primitive allows us to implement pop without concerns over
 * ABA-type races.
 */

struct percpu_list_node *this_cpu_list_pop(struct percpu_list *list,
        int *_cpu)
{
 struct percpu_list_node *node = NULL;
 int cpu;

 for (;;) {
  struct percpu_list_node *head;
  intptr_t *targetptr, expectnot, *load;
  long offset;
  int ret;

  cpu = get_current_cpu_id();
  targetptr = (intptr_t *)&list->c[cpu].head;
  expectnot = (intptr_t)NULL;
  offset = offsetof(struct percpu_list_node, next);
  load = (intptr_t *)&head;
  ret = rseq_cmpnev_storeoffp_load(RSEQ_MO_RELAXED, RSEQ_PERCPU,
       targetptr, expectnot,
       offset, load, cpu);
  if (rseq_likely(!ret)) {
   node = head;
   break;
  }
  if (ret > 0)
   break;
  /* Retry if rseq aborts. */
 }
 if (_cpu)
  *_cpu = cpu;
 return node;
}

/*
 * __percpu_list_pop is not safe against concurrent accesses. Should
 * only be used on lists that are not concurrently modified.
 */

struct percpu_list_node *__percpu_list_pop(struct percpu_list *list, int cpu)
{
 struct percpu_list_node *node;

 node = list->c[cpu].head;
 if (!node)
  return NULL;
 list->c[cpu].head = node->next;
 return node;
}

void *test_percpu_list_thread(void *arg)
{
 long long i, reps;
 struct percpu_list *list = (struct percpu_list *)arg;

 if (!opt_disable_rseq && rseq_register_current_thread())
  abort();

 reps = opt_reps;
 for (i = 0; i < reps; i++) {
  struct percpu_list_node *node;

  node = this_cpu_list_pop(list, NULL);
  if (opt_yield)
   sched_yield();  /* encourage shuffling */
  if (node)
   this_cpu_list_push(list, node, NULL);
 }

 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
         (int) rseq_gettid(), nr_abort, signals_delivered);
 if (!opt_disable_rseq && rseq_unregister_current_thread())
  abort();

 return NULL;
}

/* Simultaneous modification to a per-cpu linked list from many threads.  */
void test_percpu_list(void)
{
 const int num_threads = opt_threads;
 int i, j, ret;
 uint64_t sum = 0, expected_sum = 0;
 struct percpu_list list;
 pthread_t test_threads[num_threads];
 cpu_set_t allowed_cpus;

 memset(&list, 0, sizeof(list));

 /* Generate list entries for every usable cpu. */
 sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
 for (i = 0; i < CPU_SETSIZE; i++) {
  if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
   continue;
  for (j = 1; j <= 100; j++) {
   struct percpu_list_node *node;

   expected_sum += j;

   node = malloc(sizeof(*node));
   assert(node);
   node->data = j;
   node->next = list.c[i].head;
   list.c[i].head = node;
  }
 }

 for (i = 0; i < num_threads; i++) {
  ret = pthread_create(&test_threads[i], NULL,
         test_percpu_list_thread, &list);
  if (ret) {
   errno = ret;
   perror("pthread_create");
   abort();
  }
 }

 for (i = 0; i < num_threads; i++) {
  ret = pthread_join(test_threads[i], NULL);
  if (ret) {
   errno = ret;
   perror("pthread_join");
   abort();
  }
 }

 for (i = 0; i < CPU_SETSIZE; i++) {
  struct percpu_list_node *node;

  if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
   continue;

  while ((node = __percpu_list_pop(&list, i))) {
   sum += node->data;
   free(node);
  }
 }

 /*
 * All entries should now be accounted for (unless some external
 * actor is interfering with our allowed affinity while this
 * test is running).
 */

 assert(sum == expected_sum);
}

bool this_cpu_buffer_push(struct percpu_buffer *buffer,
     struct percpu_buffer_node *node,
     int *_cpu)
{
 bool result = false;
 int cpu;

 for (;;) {
  intptr_t *targetptr_spec, newval_spec;
  intptr_t *targetptr_final, newval_final;
  intptr_t offset;
  int ret;

  cpu = get_current_cpu_id();
  offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
  if (offset == buffer->c[cpu].buflen)
   break;
  newval_spec = (intptr_t)node;
  targetptr_spec = (intptr_t *)&buffer->c[cpu].array[offset];
  newval_final = offset + 1;
  targetptr_final = &buffer->c[cpu].offset;
  ret = rseq_cmpeqv_trystorev_storev(opt_mo, RSEQ_PERCPU,
   targetptr_final, offset, targetptr_spec,
   newval_spec, newval_final, cpu);
  if (rseq_likely(!ret)) {
   result = true;
   break;
  }
  /* Retry if comparison fails or rseq aborts. */
 }
 if (_cpu)
  *_cpu = cpu;
 return result;
}

struct percpu_buffer_node *this_cpu_buffer_pop(struct percpu_buffer *buffer,
            int *_cpu)
{
 struct percpu_buffer_node *head;
 int cpu;

 for (;;) {
  intptr_t *targetptr, newval;
  intptr_t offset;
  int ret;

  cpu = get_current_cpu_id();
  /* Load offset with single-copy atomicity. */
  offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
  if (offset == 0) {
   head = NULL;
   break;
  }
  head = RSEQ_READ_ONCE(buffer->c[cpu].array[offset - 1]);
  newval = offset - 1;
  targetptr = (intptr_t *)&buffer->c[cpu].offset;
  ret = rseq_cmpeqv_cmpeqv_storev(RSEQ_MO_RELAXED, RSEQ_PERCPU,
   targetptr, offset,
   (intptr_t *)&buffer->c[cpu].array[offset - 1],
   (intptr_t)head, newval, cpu);
  if (rseq_likely(!ret))
   break;
  /* Retry if comparison fails or rseq aborts. */
 }
 if (_cpu)
  *_cpu = cpu;
 return head;
}

/*
 * __percpu_buffer_pop is not safe against concurrent accesses. Should
 * only be used on buffers that are not concurrently modified.
 */

struct percpu_buffer_node *__percpu_buffer_pop(struct percpu_buffer *buffer,
            int cpu)
{
 struct percpu_buffer_node *head;
 intptr_t offset;

 offset = buffer->c[cpu].offset;
 if (offset == 0)
  return NULL;
 head = buffer->c[cpu].array[offset - 1];
 buffer->c[cpu].offset = offset - 1;
 return head;
}

void *test_percpu_buffer_thread(void *arg)
{
 long long i, reps;
 struct percpu_buffer *buffer = (struct percpu_buffer *)arg;

 if (!opt_disable_rseq && rseq_register_current_thread())
  abort();

 reps = opt_reps;
 for (i = 0; i < reps; i++) {
  struct percpu_buffer_node *node;

  node = this_cpu_buffer_pop(buffer, NULL);
  if (opt_yield)
   sched_yield();  /* encourage shuffling */
  if (node) {
   if (!this_cpu_buffer_push(buffer, node, NULL)) {
    /* Should increase buffer size. */
    abort();
   }
  }
 }

 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
         (int) rseq_gettid(), nr_abort, signals_delivered);
 if (!opt_disable_rseq && rseq_unregister_current_thread())
  abort();

 return NULL;
}

/* Simultaneous modification to a per-cpu buffer from many threads.  */
void test_percpu_buffer(void)
{
 const int num_threads = opt_threads;
 int i, j, ret;
 uint64_t sum = 0, expected_sum = 0;
 struct percpu_buffer buffer;
 pthread_t test_threads[num_threads];
 cpu_set_t allowed_cpus;

 memset(&buffer, 0, sizeof(buffer));

 /* Generate list entries for every usable cpu. */
 sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
 for (i = 0; i < CPU_SETSIZE; i++) {
  if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
   continue;
  /* Worse-case is every item in same CPU. */
  buffer.c[i].array =
   malloc(sizeof(*buffer.c[i].array) * CPU_SETSIZE *
          BUFFER_ITEM_PER_CPU);
  assert(buffer.c[i].array);
  buffer.c[i].buflen = CPU_SETSIZE * BUFFER_ITEM_PER_CPU;
  for (j = 1; j <= BUFFER_ITEM_PER_CPU; j++) {
   struct percpu_buffer_node *node;

   expected_sum += j;

   /*
 * We could theoretically put the word-sized
 * "data" directly in the buffer. However, we
 * want to model objects that would not fit
 * within a single word, so allocate an object
 * for each node.
 */

   node = malloc(sizeof(*node));
   assert(node);
   node->data = j;
   buffer.c[i].array[j - 1] = node;
   buffer.c[i].offset++;
  }
 }

 for (i = 0; i < num_threads; i++) {
  ret = pthread_create(&test_threads[i], NULL,
         test_percpu_buffer_thread, &buffer);
  if (ret) {
   errno = ret;
   perror("pthread_create");
   abort();
  }
 }

 for (i = 0; i < num_threads; i++) {
  ret = pthread_join(test_threads[i], NULL);
  if (ret) {
   errno = ret;
   perror("pthread_join");
   abort();
  }
 }

 for (i = 0; i < CPU_SETSIZE; i++) {
  struct percpu_buffer_node *node;

  if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
   continue;

  while ((node = __percpu_buffer_pop(&buffer, i))) {
   sum += node->data;
   free(node);
  }
  free(buffer.c[i].array);
 }

 /*
 * All entries should now be accounted for (unless some external
 * actor is interfering with our allowed affinity while this
 * test is running).
 */

 assert(sum == expected_sum);
}

bool this_cpu_memcpy_buffer_push(struct percpu_memcpy_buffer *buffer,
     struct percpu_memcpy_buffer_node item,
     int *_cpu)
{
 bool result = false;
 int cpu;

 for (;;) {
  intptr_t *targetptr_final, newval_final, offset;
  char *destptr, *srcptr;
  size_t copylen;
  int ret;

  cpu = get_current_cpu_id();
  /* Load offset with single-copy atomicity. */
  offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
  if (offset == buffer->c[cpu].buflen)
   break;
  destptr = (char *)&buffer->c[cpu].array[offset];
  srcptr = (char *)&item;
  /* copylen must be <= 4kB. */
  copylen = sizeof(item);
  newval_final = offset + 1;
  targetptr_final = &buffer->c[cpu].offset;
  ret = rseq_cmpeqv_trymemcpy_storev(
   opt_mo, RSEQ_PERCPU,
   targetptr_final, offset,
   destptr, srcptr, copylen,
   newval_final, cpu);
  if (rseq_likely(!ret)) {
   result = true;
   break;
  }
  /* Retry if comparison fails or rseq aborts. */
 }
 if (_cpu)
  *_cpu = cpu;
 return result;
}

bool this_cpu_memcpy_buffer_pop(struct percpu_memcpy_buffer *buffer,
    struct percpu_memcpy_buffer_node *item,
    int *_cpu)
{
 bool result = false;
 int cpu;

 for (;;) {
  intptr_t *targetptr_final, newval_final, offset;
  char *destptr, *srcptr;
  size_t copylen;
  int ret;

  cpu = get_current_cpu_id();
  /* Load offset with single-copy atomicity. */
  offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
  if (offset == 0)
   break;
  destptr = (char *)item;
  srcptr = (char *)&buffer->c[cpu].array[offset - 1];
  /* copylen must be <= 4kB. */
  copylen = sizeof(*item);
  newval_final = offset - 1;
  targetptr_final = &buffer->c[cpu].offset;
  ret = rseq_cmpeqv_trymemcpy_storev(RSEQ_MO_RELAXED, RSEQ_PERCPU,
   targetptr_final, offset, destptr, srcptr, copylen,
   newval_final, cpu);
  if (rseq_likely(!ret)) {
   result = true;
   break;
  }
  /* Retry if comparison fails or rseq aborts. */
 }
 if (_cpu)
  *_cpu = cpu;
 return result;
}

/*
 * __percpu_memcpy_buffer_pop is not safe against concurrent accesses. Should
 * only be used on buffers that are not concurrently modified.
 */

bool __percpu_memcpy_buffer_pop(struct percpu_memcpy_buffer *buffer,
    struct percpu_memcpy_buffer_node *item,
    int cpu)
{
 intptr_t offset;

 offset = buffer->c[cpu].offset;
 if (offset == 0)
  return false;
 memcpy(item, &buffer->c[cpu].array[offset - 1], sizeof(*item));
 buffer->c[cpu].offset = offset - 1;
 return true;
}

void *test_percpu_memcpy_buffer_thread(void *arg)
{
 long long i, reps;
 struct percpu_memcpy_buffer *buffer = (struct percpu_memcpy_buffer *)arg;

 if (!opt_disable_rseq && rseq_register_current_thread())
  abort();

 reps = opt_reps;
 for (i = 0; i < reps; i++) {
  struct percpu_memcpy_buffer_node item;
  bool result;

  result = this_cpu_memcpy_buffer_pop(buffer, &item, NULL);
  if (opt_yield)
   sched_yield();  /* encourage shuffling */
  if (result) {
   if (!this_cpu_memcpy_buffer_push(buffer, item, NULL)) {
    /* Should increase buffer size. */
    abort();
   }
  }
 }

 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
         (int) rseq_gettid(), nr_abort, signals_delivered);
 if (!opt_disable_rseq && rseq_unregister_current_thread())
  abort();

 return NULL;
}

/* Simultaneous modification to a per-cpu buffer from many threads.  */
void test_percpu_memcpy_buffer(void)
{
 const int num_threads = opt_threads;
 int i, j, ret;
 uint64_t sum = 0, expected_sum = 0;
 struct percpu_memcpy_buffer buffer;
 pthread_t test_threads[num_threads];
 cpu_set_t allowed_cpus;

 memset(&buffer, 0, sizeof(buffer));

 /* Generate list entries for every usable cpu. */
 sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
 for (i = 0; i < CPU_SETSIZE; i++) {
  if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
   continue;
  /* Worse-case is every item in same CPU. */
  buffer.c[i].array =
   malloc(sizeof(*buffer.c[i].array) * CPU_SETSIZE *
          MEMCPY_BUFFER_ITEM_PER_CPU);
  assert(buffer.c[i].array);
  buffer.c[i].buflen = CPU_SETSIZE * MEMCPY_BUFFER_ITEM_PER_CPU;
  for (j = 1; j <= MEMCPY_BUFFER_ITEM_PER_CPU; j++) {
   expected_sum += 2 * j + 1;

   /*
 * We could theoretically put the word-sized
 * "data" directly in the buffer. However, we
 * want to model objects that would not fit
 * within a single word, so allocate an object
 * for each node.
 */

   buffer.c[i].array[j - 1].data1 = j;
   buffer.c[i].array[j - 1].data2 = j + 1;
   buffer.c[i].offset++;
  }
 }

 for (i = 0; i < num_threads; i++) {
  ret = pthread_create(&test_threads[i], NULL,
         test_percpu_memcpy_buffer_thread,
         &buffer);
  if (ret) {
   errno = ret;
   perror("pthread_create");
   abort();
  }
 }

 for (i = 0; i < num_threads; i++) {
  ret = pthread_join(test_threads[i], NULL);
  if (ret) {
   errno = ret;
   perror("pthread_join");
   abort();
  }
 }

 for (i = 0; i < CPU_SETSIZE; i++) {
  struct percpu_memcpy_buffer_node item;

  if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
   continue;

  while (__percpu_memcpy_buffer_pop(&buffer, &item, i)) {
   sum += item.data1;
   sum += item.data2;
  }
  free(buffer.c[i].array);
 }

 /*
 * All entries should now be accounted for (unless some external
 * actor is interfering with our allowed affinity while this
 * test is running).
 */

 assert(sum == expected_sum);
}

static void test_signal_interrupt_handler(int signo)
{
 signals_delivered++;
}

static int set_signal_handler(void)
{
 int ret = 0;
 struct sigaction sa;
 sigset_t sigset;

 ret = sigemptyset(&sigset);
 if (ret < 0) {
  perror("sigemptyset");
  return ret;
 }

 sa.sa_handler = test_signal_interrupt_handler;
 sa.sa_mask = sigset;
 sa.sa_flags = 0;
 ret = sigaction(SIGUSR1, &sa, NULL);
 if (ret < 0) {
  perror("sigaction");
  return ret;
 }

 printf_verbose("Signal handler set for SIGUSR1\n");

 return ret;
}

/* Test MEMBARRIER_CMD_PRIVATE_RESTART_RSEQ_ON_CPU membarrier command. */
#ifdef TEST_MEMBARRIER
struct test_membarrier_thread_args {
 int stop;
 intptr_t percpu_list_ptr;
};

/* Worker threads modify data in their "active" percpu lists. */
void *test_membarrier_worker_thread(void *arg)
{
 struct test_membarrier_thread_args *args =
  (struct test_membarrier_thread_args *)arg;
 const int iters = opt_reps;
 int i;

 if (rseq_register_current_thread()) {
  fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
   errno, strerror(errno));
  abort();
 }

 /* Wait for initialization. */
 while (!__atomic_load_n(&args->percpu_list_ptr, __ATOMIC_ACQUIRE)) {}

 for (i = 0; i < iters; ++i) {
  int ret;

  do {
   int cpu = get_current_cpu_id();

   ret = rseq_offset_deref_addv(RSEQ_MO_RELAXED, RSEQ_PERCPU,
    &args->percpu_list_ptr,
    sizeof(struct percpu_list_entry) * cpu, 1, cpu);
  } while (rseq_unlikely(ret));
 }

 if (rseq_unregister_current_thread()) {
  fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
   errno, strerror(errno));
  abort();
 }
 return NULL;
}

void test_membarrier_init_percpu_list(struct percpu_list *list)
{
 int i;

 memset(list, 0, sizeof(*list));
 for (i = 0; i < CPU_SETSIZE; i++) {
  struct percpu_list_node *node;

  node = malloc(sizeof(*node));
  assert(node);
  node->data = 0;
  node->next = NULL;
  list->c[i].head = node;
 }
}

void test_membarrier_free_percpu_list(struct percpu_list *list)
{
 int i;

 for (i = 0; i < CPU_SETSIZE; i++)
  free(list->c[i].head);
}

/*
 * The manager thread swaps per-cpu lists that worker threads see,
 * and validates that there are no unexpected modifications.
 */

void *test_membarrier_manager_thread(void *arg)
{
 struct test_membarrier_thread_args *args =
  (struct test_membarrier_thread_args *)arg;
 struct percpu_list list_a, list_b;
 intptr_t expect_a = 0, expect_b = 0;
 int cpu_a = 0, cpu_b = 0;

 if (rseq_register_current_thread()) {
  fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
   errno, strerror(errno));
  abort();
 }

 /* Init lists. */
 test_membarrier_init_percpu_list(&list_a);
 test_membarrier_init_percpu_list(&list_b);

 __atomic_store_n(&args->percpu_list_ptr, (intptr_t)&list_a, __ATOMIC_RELEASE);

 while (!__atomic_load_n(&args->stop, __ATOMIC_ACQUIRE)) {
  /* list_a is "active". */
  cpu_a = rand() % CPU_SETSIZE;
  /*
 * As list_b is "inactive", we should never see changes
 * to list_b.
 */

  if (expect_b != __atomic_load_n(&list_b.c[cpu_b].head->data, __ATOMIC_ACQUIRE)) {
   fprintf(stderr, "Membarrier test failed\n");
   abort();
  }

  /* Make list_b "active". */
  __atomic_store_n(&args->percpu_list_ptr, (intptr_t)&list_b, __ATOMIC_RELEASE);
  if (rseq_membarrier_expedited(cpu_a) &&
    errno != ENXIO /* missing CPU */) {
   perror("sys_membarrier");
   abort();
  }
  /*
 * Cpu A should now only modify list_b, so the values
 * in list_a should be stable.
 */

  expect_a = __atomic_load_n(&list_a.c[cpu_a].head->data, __ATOMIC_ACQUIRE);

  cpu_b = rand() % CPU_SETSIZE;
  /*
 * As list_a is "inactive", we should never see changes
 * to list_a.
 */

  if (expect_a != __atomic_load_n(&list_a.c[cpu_a].head->data, __ATOMIC_ACQUIRE)) {
   fprintf(stderr, "Membarrier test failed\n");
   abort();
  }

  /* Make list_a "active". */
  __atomic_store_n(&args->percpu_list_ptr, (intptr_t)&list_a, __ATOMIC_RELEASE);
  if (rseq_membarrier_expedited(cpu_b) &&
    errno != ENXIO /* missing CPU*/) {
   perror("sys_membarrier");
   abort();
  }
  /* Remember a value from list_b. */
  expect_b = __atomic_load_n(&list_b.c[cpu_b].head->data, __ATOMIC_ACQUIRE);
 }

 test_membarrier_free_percpu_list(&list_a);
 test_membarrier_free_percpu_list(&list_b);

 if (rseq_unregister_current_thread()) {
  fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
   errno, strerror(errno));
  abort();
 }
 return NULL;
}

void test_membarrier(void)
{
 const int num_threads = opt_threads;
 struct test_membarrier_thread_args thread_args;
 pthread_t worker_threads[num_threads];
 pthread_t manager_thread;
 int i, ret;

 if (sys_membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ, 0, 0)) {
  perror("sys_membarrier");
  abort();
 }

 thread_args.stop = 0;
 thread_args.percpu_list_ptr = 0;
 ret = pthread_create(&manager_thread, NULL,
   test_membarrier_manager_thread, &thread_args);
 if (ret) {
  errno = ret;
  perror("pthread_create");
  abort();
 }

 for (i = 0; i < num_threads; i++) {
  ret = pthread_create(&worker_threads[i], NULL,
    test_membarrier_worker_thread, &thread_args);
  if (ret) {
   errno = ret;
   perror("pthread_create");
   abort();
  }
 }


 for (i = 0; i < num_threads; i++) {
  ret = pthread_join(worker_threads[i], NULL);
  if (ret) {
   errno = ret;
   perror("pthread_join");
   abort();
  }
 }

 __atomic_store_n(&thread_args.stop, 1, __ATOMIC_RELEASE);
 ret = pthread_join(manager_thread, NULL);
 if (ret) {
  errno = ret;
  perror("pthread_join");
  abort();
 }
}
#else /* TEST_MEMBARRIER */
void test_membarrier(void)
{
 fprintf(stderr, "rseq_offset_deref_addv is not implemented on this architecture. "
   "Skipping membarrier test.\n");
}
#endif

static void show_usage(int argc, char **argv)
{
 printf("Usage : %s \n",
  argv[0]);
 printf("OPTIONS:\n");
 printf(" [-1 loops] Number of loops for delay injection 1\n");
 printf(" [-2 loops] Number of loops for delay injection 2\n");
 printf(" [-3 loops] Number of loops for delay injection 3\n");
 printf(" [-4 loops] Number of loops for delay injection 4\n");
 printf(" [-5 loops] Number of loops for delay injection 5\n");
 printf(" [-6 loops] Number of loops for delay injection 6\n");
 printf(" [-7 loops] Number of loops for delay injection 7 (-1 to enable -m)\n");
 printf(" [-8 loops] Number of loops for delay injection 8 (-1 to enable -m)\n");
 printf(" [-9 loops] Number of loops for delay injection 9 (-1 to enable -m)\n");
 printf(" [-m N] Yield/sleep/kill every modulo N (default 0: disabled) (>= 0)\n");
 printf(" [-y] Yield\n");
 printf(" [-k] Kill thread with signal\n");
 printf(" [-s S] S: =0: disabled (default), >0: sleep time (ms)\n");
 printf(" [-t N] Number of threads (default 200)\n");
 printf(" [-r N] Number of repetitions per thread (default 5000)\n");
 printf(" [-d] Disable rseq system call (no initialization)\n");
 printf(" [-D M] Disable rseq for each M threads\n");
 printf(" [-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement, membarrie(r)\n");
 printf(" [-M] Push into buffer and memcpy buffer with memory barriers.\n");
 printf(" [-v] Verbose output.\n");
 printf(" [-h] Show this help.\n");
 printf("\n");
}

int main(int argc, char **argv)
{
 int i;

 for (i = 1; i < argc; i++) {
  if (argv[i][0] != '-')
   continue;
  switch (argv[i][1]) {
  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
   if (argc < i + 2) {
    show_usage(argc, argv);
    goto error;
   }
   loop_cnt[argv[i][1] - '0'] = atol(argv[i + 1]);
   i++;
   break;
  case 'm':
   if (argc < i + 2) {
    show_usage(argc, argv);
    goto error;
   }
   opt_modulo = atol(argv[i + 1]);
   if (opt_modulo < 0) {
    show_usage(argc, argv);
    goto error;
   }
   i++;
   break;
  case 's':
   if (argc < i + 2) {
    show_usage(argc, argv);
    goto error;
   }
   opt_sleep = atol(argv[i + 1]);
   if (opt_sleep < 0) {
    show_usage(argc, argv);
    goto error;
   }
   i++;
   break;
  case 'y':
   opt_yield = 1;
   break;
  case 'k':
   opt_signal = 1;
   break;
  case 'd':
   opt_disable_rseq = 1;
   break;
  case 'D':
   if (argc < i + 2) {
    show_usage(argc, argv);
    goto error;
   }
   opt_disable_mod = atol(argv[i + 1]);
   if (opt_disable_mod < 0) {
    show_usage(argc, argv);
    goto error;
   }
   i++;
   break;
  case 't':
   if (argc < i + 2) {
    show_usage(argc, argv);
    goto error;
   }
   opt_threads = atol(argv[i + 1]);
   if (opt_threads < 0) {
    show_usage(argc, argv);
    goto error;
   }
   i++;
   break;
  case 'r':
   if (argc < i + 2) {
    show_usage(argc, argv);
    goto error;
   }
   opt_reps = atoll(argv[i + 1]);
   if (opt_reps < 0) {
    show_usage(argc, argv);
    goto error;
   }
   i++;
   break;
  case 'h':
   show_usage(argc, argv);
   goto end;
  case 'T':
   if (argc < i + 2) {
    show_usage(argc, argv);
    goto error;
   }
   opt_test = *argv[i + 1];
   switch (opt_test) {
   case 's':
   case 'l':
   case 'i':
   case 'b':
   case 'm':
   case 'r':
    break;
   default:
    show_usage(argc, argv);
    goto error;
   }
   i++;
   break;
  case 'v':
   verbose = 1;
   break;
  case 'M':
   opt_mo = RSEQ_MO_RELEASE;
   break;
  default:
   show_usage(argc, argv);
   goto error;
  }
 }

 loop_cnt_1 = loop_cnt[1];
 loop_cnt_2 = loop_cnt[2];
 loop_cnt_3 = loop_cnt[3];
 loop_cnt_4 = loop_cnt[4];
 loop_cnt_5 = loop_cnt[5];
 loop_cnt_6 = loop_cnt[6];

 if (set_signal_handler())
  goto error;

 if (!opt_disable_rseq && rseq_register_current_thread())
  goto error;
 if (!opt_disable_rseq && !rseq_validate_cpu_id()) {
  fprintf(stderr, "Error: cpu id getter unavailable\n");
  goto error;
 }
 switch (opt_test) {
 case 's':
  printf_verbose("spinlock\n");
  test_percpu_spinlock();
  break;
 case 'l':
  printf_verbose("linked list\n");
  test_percpu_list();
  break;
 case 'b':
  printf_verbose("buffer\n");
  test_percpu_buffer();
  break;
 case 'm':
  printf_verbose("memcpy buffer\n");
  test_percpu_memcpy_buffer();
  break;
 case 'i':
  printf_verbose("counter increment\n");
  test_percpu_inc();
  break;
 case 'r':
  printf_verbose("membarrier\n");
  test_membarrier();
  break;
 }
 if (!opt_disable_rseq && rseq_unregister_current_thread())
  abort();
end:
 return 0;

error:
 return -1;
}

Messung V0.5
C=95 H=85 G=90

¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge