Skip to content
Snippets Groups Projects
threadpool_lib.c 6.96 KiB
Newer Older
  • Learn to ignore specific revisions
  • #define _GNU_SOURCE 1
    #include <signal.h>
    #include <ucontext.h>
    
    #include <pthread.h>
    
    spruett3's avatar
    spruett3 committed
    #include <time.h>
    #include <sys/time.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    
    spruett3's avatar
    spruett3 committed
    #include <sys/resource.h>
    
    #include "threadpool_lib.h"
    
    // http://www.guyrutenberg.com/2007/09/22/profiling-code-using-clock_gettime/
    struct timespec timespec_diff(struct timespec start, struct timespec end)
    {
        struct timespec temp;
        if ((end.tv_nsec-start.tv_nsec)<0) {
            temp.tv_sec = end.tv_sec-start.tv_sec-1;
            temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
        } else {
            temp.tv_sec = end.tv_sec-start.tv_sec;
            temp.tv_nsec = end.tv_nsec-start.tv_nsec;
        }
        return temp;
    }
    
    void timespec_print(struct timespec ts, char *buf, size_t buflen)
    {
        snprintf(buf, buflen, "%lld.%.9ld", (long long)ts.tv_sec, ts.tv_nsec);
    }
    
    /**
     * Count number of threads by scanning /proc/self/status
     * for the Threads: ... line
     */
    int
    count_number_of_threads(void)
    {
        FILE * p = fopen("/proc/self/status", "r");
        while (!feof(p)) {
            int threadsleft;
            char buf[128];
    
            if (fgets(buf, sizeof buf, p) == NULL)
                continue;
    
    spruett3's avatar
    spruett3 committed
            if (sscanf(buf, "Threads: %d\n", &threadsleft) != 1)
                continue;
    
            fclose(p);
            return threadsleft;
        }
        printf("Internal error, please send email to gback@cs.vt.edu\n");
        abort();
    }
    
    // int rc = getrusage(RUSAGE_CHILDREN, &usage);
    //           struct rusage {
    //               struct timeval ru_utime; /* user time used */
    //               struct timeval ru_stime; /* system time used */
    //               long   ru_maxrss;        /* maximum resident set size */
    //               long   ru_ixrss;         /* integral shared memory size */
    //               long   ru_idrss;         /* integral unshared data size */
    //               long   ru_isrss;         /* integral unshared stack size */
    //               long   ru_minflt;        /* page reclaims */
    //               long   ru_majflt;        /* page faults */
    //               long   ru_nswap;         /* swaps */
    //               long   ru_inblock;       /* block input operations */
    //               long   ru_oublock;       /* block output operations */
    //               long   ru_msgsnd;        /* messages sent */
    //               long   ru_msgrcv;        /* messages received */
    //               long   ru_nsignals;      /* signals received */
    //               long   ru_nvcsw;         /* voluntary context switches */
    //               long   ru_nivcsw;        /* involuntary context switches */
    //           };
    //
    static void print_rusage_as_json(FILE *output, struct rusage *usage)
    {
        fprintf(output, "\"ru_utime\" : %ld.%06ld, \"ru_stime\" : %ld.%06ld, \"ru_nvcsw\" : %ld, \"ru_nivcsw\" : %ld",
            usage->ru_utime.tv_sec, usage->ru_utime.tv_usec,
            usage->ru_stime.tv_sec, usage->ru_stime.tv_usec,
            usage->ru_nvcsw, usage->ru_nivcsw
        );
    }
    
    static void print_rusage_to_human(FILE *output, struct rusage *usage)
    {
        fprintf(output, "user time: %ld.%06lds\nsystem time: %ld.%06lds\n",
            usage->ru_utime.tv_sec, usage->ru_utime.tv_usec,
            usage->ru_stime.tv_sec, usage->ru_stime.tv_usec
        );
    }
    
    /* Compute the diff of interesting parameters in two rusage structs */
    static void rusagesub(struct rusage *end, struct rusage *start, struct rusage *diff)
    {
        diff->ru_nvcsw = end->ru_nvcsw - start->ru_nvcsw;
        diff->ru_nivcsw = end->ru_nivcsw - start->ru_nivcsw;
        timersub(&end->ru_utime, &start->ru_utime, &diff->ru_utime);
        timersub(&end->ru_stime, &start->ru_stime, &diff->ru_stime);
    }
    
    struct benchmark_data {
        struct rusage rstart, rend, rdiff;
        struct timeval start, end, diff;
    };
    
    struct benchmark_data * start_benchmark(void)
    {
        struct benchmark_data * bdata = malloc(sizeof *bdata);
        
        int rc = getrusage(RUSAGE_SELF, &bdata->rstart);
        if (rc == -1)
            perror("getrusage");
    
        gettimeofday(&bdata->start, NULL);
        return bdata;
    }
    
    void stop_benchmark(struct benchmark_data * bdata)
    {
        gettimeofday(&bdata->end, NULL);
        int rc = getrusage(RUSAGE_SELF, &bdata->rend);
        if (rc == -1)
            perror("getrusage");
    
        rusagesub(&bdata->rend, &bdata->rstart, &bdata->rdiff);
        timersub(&bdata->end, &bdata->start, &bdata->diff);
    }
    
    void report_benchmark_results(struct benchmark_data *bdata)
    {
        char buf[80];
        snprintf(buf, sizeof buf, "runresult.%d.json", getppid());
        FILE * f = fopen(buf, "w");
        if (f == NULL) {
            perror("fopen");
            abort();
        }
    
        // fprintf(stderr, "Writing %s\n", buf);
        fprintf(f, "{");
        print_rusage_as_json(f, &bdata->rdiff);
        fprintf(f, ", \"realtime\" : %ld.%06ld", bdata->diff.tv_sec, bdata->diff.tv_usec);
        fprintf(f, "}");
        fclose(f);
    }
    
    void report_benchmark_results_to_human(FILE *f, struct benchmark_data *bdata)
    {
        // fprintf(stderr, "Writing %s\n", buf);
        print_rusage_to_human(f, &bdata->rdiff);
        fprintf(f, "real time: %ld.%06lds\n", bdata->diff.tv_sec, bdata->diff.tv_usec);
    }
    
    
    /* FIXME: this code is Linux/64bit only. */
    static void
    catch_segfault(int signo, siginfo_t *info, void * _ctxt)
    {
        ucontext_t * ctxt = _ctxt;
        uintptr_t faultaddr = (uintptr_t) info->si_addr;
        uintptr_t rsp = (uintptr_t) ctxt->uc_mcontext.gregs[REG_RSP];
        uintptr_t rip = (uintptr_t) ctxt->uc_mcontext.gregs[REG_RIP];
    
        fprintf(stderr, "\n\n----------------------------------------------------------------------\n\n");
        fprintf(stderr, "Segmentation Fault, faultaddr=0x%lx, rip=0x%lx, rsp=0x%lx\n",
            faultaddr, rip, rsp);
        if (abs(faultaddr - rsp) < 2048) {
            int page_size = 4096; 
            pthread_attr_t attr;
            void * stackaddr;
            size_t stacksize;
            pthread_getattr_np(pthread_self(), &attr);      // non portable
            pthread_attr_getstack( &attr, &stackaddr, &stacksize );
            stackaddr += page_size;                         // subtract out guard page
            stacksize -= page_size;
            fprintf(stderr, "Likely stack overflow.\nThe current thread's stack bottom is at %p.\n", stackaddr); 
            fprintf(stderr, "Try using a larger stack size; read pthread_attr_setstacksize(3)\n");
            fprintf(stderr, "Current thread stack size is %ld\n", stacksize);
        }
    
        fprintf(stderr, "Exiting now; the calling shell will not print 'Segmentation Fault'\n");
        exit(EXIT_FAILURE);
    }
    
    /*
     * Fork/Join frameworks with child stealing may suffer from high stack
     * consumption.
     * Install a handler to detect stack overflow.
     */
    void install_stack_overflow_handler(void) 
    {
        struct sigaction act;
    #define ALT_STACK_SIZE 16*1024
    
        stack_t ss = {
            .ss_flags = 0,
            .ss_sp = malloc(ALT_STACK_SIZE),
            .ss_size = ALT_STACK_SIZE
        };
        if (sigaltstack(&ss, NULL)) {
            perror ("sigaltstack");
            exit (EXIT_FAILURE); 
        }
    
        act.sa_sigaction = catch_segfault;
        act.sa_flags = SA_SIGINFO | SA_ONSTACK;
        sigemptyset (&act.sa_mask);
        int status = sigaction (SIGSEGV, &act, NULL);
        if (status) {
            perror ("sigaction");
            exit (EXIT_FAILURE); 
        }
    }