Home | History | Annotate | Download | only in gdbserver_tests
      1 #define _GNU_SOURCE
      2 #include <errno.h>
      3 #include <string.h>
      4 #include <pthread.h>
      5 #include <stdlib.h>
      6 #include <stdio.h>
      7 #include <fcntl.h>
      8 #include <unistd.h>
      9 #include <sys/types.h>
     10 #include <sys/syscall.h>
     11 #include <sched.h>
     12 #include <signal.h>
     13 static int loops = 15; // each thread+main will do this amount of loop
     14 static int sleepms = 1000; // in each loop, will sleep "sleepms" milliseconds
     15 static int burn = 0; // after each sleep, will burn cpu in a tight 'burn' loop
     16 static void setup_sigusr_handler(void); // sigusr1 and 2 sigaction setup.
     17 
     18 static pid_t gettid()
     19 {
     20 #ifdef __NR_gettid
     21    return syscall(__NR_gettid);
     22 #else
     23    return getpid();
     24 #endif
     25 }
     26 // will be invoked from gdb.
     27 static void whoami(char *msg) __attribute__((unused));
     28 static void whoami(char *msg)
     29 {
     30    fprintf(stderr, "pid %ld Thread %ld %s\n", (long) getpid(), (long) gettid(),
     31            msg);
     32    fflush(stderr);
     33 }
     34 
     35 
     36 static void do_burn ()
     37 {
     38    int i;
     39    int loopnr = 0;
     40    // one single line for the below, to ensure interrupt on this line.
     41    for (i = 0; i < burn; i++) loopnr++;
     42 }
     43 
     44 static int thread_ready = 0;
     45 static pthread_cond_t ready = PTHREAD_COND_INITIALIZER;
     46 static pthread_mutex_t ready_mutex = PTHREAD_MUTEX_INITIALIZER;
     47 static void signal_ready (void)
     48 {
     49    int rc;
     50    rc = pthread_mutex_lock(&ready_mutex);
     51    if (rc != 0)
     52       fprintf(stderr, "signal_ready lock error %d_n", rc);
     53    thread_ready = 1;
     54    rc = pthread_cond_signal(&ready);
     55    if (rc != 0)
     56       fprintf(stderr, "signal_ready signal error %d_n", rc);
     57    rc = pthread_mutex_unlock(&ready_mutex);
     58    if (rc != 0)
     59       fprintf(stderr, "signal_ready unlock error %d_n", rc);
     60 }
     61 
     62 struct spec {
     63    char *name;
     64    int sleep;
     65    int burn;
     66    int t;
     67 };
     68 static struct timeval t[4];
     69 static int nr_sleeper_or_burner = 0;
     70 static volatile int report_finished = 1;
     71 // set to 0 to have no finish msg (as order is non-deterministic)
     72 static void *sleeper_or_burner(void *v)
     73 {
     74    int i = 0;
     75    struct spec* s = (struct spec*)v;
     76    int ret;
     77    fprintf(stderr, "%s ready to sleep and/or burn\n", s->name);
     78    fflush (stderr);
     79    signal_ready();
     80    nr_sleeper_or_burner++;
     81 
     82    for (i = 0; i < loops; i++) {
     83       if (sleepms > 0 && s->sleep) {
     84          t[s->t].tv_sec = sleepms / 1000;
     85          t[s->t].tv_usec = (sleepms % 1000) * 1000;
     86          ret = select (0, NULL, NULL, NULL, &t[s->t]);
     87          /* We only expect a timeout result or EINTR from the above. */
     88          if (ret != 0 && errno != EINTR)
     89             perror("unexpected result from select");
     90       }
     91       if (burn > 0 && s->burn)
     92          do_burn();
     93    }
     94    if (report_finished) {
     95       fprintf(stderr, "%s finished to sleep and/or burn\n", s->name);
     96       fflush (stderr);
     97    }
     98    return NULL;
     99 }
    100 
    101 // wait till a thread signals it is ready
    102 static void wait_ready(void)
    103 {
    104    int rc;
    105    rc = pthread_mutex_lock(&ready_mutex);
    106    if (rc != 0)
    107       fprintf(stderr, "wait_ready lock error %d_n", rc);
    108    while (! thread_ready && rc == 0) {
    109       rc = pthread_cond_wait(&ready, &ready_mutex);
    110       if (rc != 0)
    111          fprintf(stderr, "wait_ready wait error %d_n", rc);
    112    }
    113    thread_ready = 0;
    114    rc = pthread_mutex_unlock(&ready_mutex);
    115    if (rc != 0)
    116       fprintf(stderr, "wait_ready unlock error %d_n", rc);
    117 }
    118 
    119 // We will lock ourselves on one single cpu.
    120 // This bypasses the unfairness of the Valgrind scheduler
    121 // when a multi-cpu machine has enough cpu to run all the
    122 // threads wanting to burn cpu.
    123 static void setaffinity(void)
    124 {
    125 #ifdef VGO_linux
    126    cpu_set_t single_cpu;
    127    CPU_ZERO(&single_cpu);
    128    CPU_SET(1, &single_cpu);
    129    (void) sched_setaffinity(0, sizeof(single_cpu), &single_cpu);
    130 #endif
    131    // GDBTD: equivalent for Darwin ?
    132 }
    133 
    134 int main (int argc, char *argv[])
    135 {
    136   char *threads_spec;
    137   pthread_t ebbr, egll, zzzz;
    138   struct spec b, l, p, m;
    139   char *some_mem __attribute__((unused)) = malloc(100);
    140   if (argc > 5 && atoi(argv[5])) setaffinity();
    141   setup_sigusr_handler();
    142   if (argc > 1)
    143      loops = atoi(argv[1]);
    144 
    145   if (argc > 2)
    146      sleepms = atoi(argv[2]);
    147 
    148   if (argc > 3)
    149      burn = atoll(argv[3]);
    150 
    151   if (argc > 4)
    152      threads_spec = argv[4];
    153   else
    154      threads_spec = "BSBSBSBS";
    155 
    156   fprintf(stderr, "loops/sleep_ms/burn/threads_spec/affinity:  %d %d %d %s %d\n",
    157           loops, sleepms, burn, threads_spec, argc > 5 && atoi(argv[5]));
    158   fflush(stderr);
    159 
    160   b.name = "Brussels";
    161   b.burn = *threads_spec++ == 'B';
    162   b.sleep = *threads_spec++ == 'S';
    163   b.t = -1;
    164   if (b.burn || b.sleep) {
    165      b.t = 1;
    166      pthread_create(&ebbr, NULL, sleeper_or_burner, &b);
    167      wait_ready();
    168   }
    169 
    170   l.name = "London";
    171   l.burn = *threads_spec++ == 'B';
    172   l.sleep = *threads_spec++ == 'S';
    173   l.t = -1;
    174   if (l.burn || l.sleep) {
    175      l.t = 2;
    176      pthread_create(&egll, NULL, sleeper_or_burner, &l);
    177      wait_ready();
    178   }
    179 
    180   p.name = "Petaouchnok";
    181   p.burn = *threads_spec++ == 'B';
    182   p.sleep = *threads_spec++ == 'S';
    183   p.t = -1;
    184   if (p.burn || p.sleep) {
    185      p.t = 3;
    186      pthread_create(&zzzz, NULL, sleeper_or_burner, &p);
    187      wait_ready();
    188   }
    189 
    190   m.name = "main";
    191   m.burn = *threads_spec++ == 'B';
    192   m.sleep = *threads_spec++ == 'S';
    193   m.t = 0;
    194   sleeper_or_burner(&m);
    195 
    196   if (b.t != -1) pthread_join(ebbr, NULL);
    197   if (l.t != -1) pthread_join(egll, NULL);
    198   if (p.t != -1) pthread_join(zzzz, NULL);
    199 
    200   return 0;
    201 }
    202 
    203 static int sigusr1_received = 0;
    204 static void sigusr1_handler(int signr)
    205 {
    206    sigusr1_received++;
    207 }
    208 static void setup_sigusr_handler(void)
    209 {
    210    struct sigaction sa;
    211    sa.sa_handler = sigusr1_handler;
    212    sigemptyset(&sa.sa_mask);
    213    sa.sa_flags = 0;
    214 
    215    if (sigaction (SIGUSR1, &sa, NULL) != 0)
    216       perror("sigaction SIGUSR1");
    217 
    218    sa.sa_handler = SIG_IGN;
    219    if (sigaction (SIGUSR2, &sa, NULL) != 0)
    220       perror("sigaction SIGUSR2");
    221 }
    222 
    223