Exemple #1
0
void
term_init(void)
{
        setupterm(NULL, 1, NULL);
        if (tcgetattr(0, &term_init_termios) < 0)
                epanic("failed to get terminal attributes");

        // Handle terminal resize
        struct sigaction act = {
                .sa_handler = term_on_sigwinch
        };
        if (sigaction(SIGWINCH, &act, NULL) < 0)
                epanic("failed to install SIGWINCH handler");

        atexit(term_reset);
        term_initialized = true;

        // Enter cursor mode
        putp(enter_ca_mode);
        // Enter invisible mode
        putp(cursor_invisible);
        // Disable echo and enter canonical (aka cbreak) mode so we
        // get input without waiting for newline
        struct termios tc = term_init_termios;
        tc.c_lflag &= ~(ICANON | ECHO);
        tc.c_iflag &= ~ICRNL;
        tc.c_lflag |= ISIG;
        tc.c_cc[VMIN] = 1;
        tc.c_cc[VTIME] = 0;
        if (tcsetattr(0, TCSAFLUSH, &tc) < 0)
                epanic("failed to set terminal attributes");
}
Exemple #2
0
void
cpustats_read(struct cpustats *out)
{
        // On kernels prior to 2.6.37, this can take a long time on
        // large systems because updating IRQ counts is slow.  See
        //   https://lkml.org/lkml/2010/9/29/259

        int i;
        for (i = 0; i < cpustats_cpus; i++)
                out->cpus[i].online = false;

        out->online = out->max = 0;
        out->real = time_usec() * sysconf(_SC_CLK_TCK) / 1000000;

        if ((readn_str(cpustats_fd, cpustats_buf, cpustats_buf_size)) < 0)
                epanic("failed to read %s/stat", proc_path);

        char *pos = cpustats_buf;
        while (strncmp(pos, "cpu", 3) == 0) {
                pos += 3;

                struct cpustat *st;
                int cpu = -1;
                if (*pos == ' ') {
                        // Aggregate line
                        st = &out->avg;
                } else if (isdigit(*pos)) {
                        cpu = strtol(pos, &pos, 10);
                        if (cpu >= cpustats_cpus)
                                goto next;
                        st = &out->cpus[cpu];
                } else {
                        goto next;
                }

                // Earlier versions of Linux only reported user, nice,
                // sys, and idle.
                st->iowait = st->irq = st->softirq = 0;
                unsigned long long toss;
                if (sscanf(pos, " %llu %llu %llu %llu %llu %llu %llu",
                           &st->user, &st->nice, &st->sys, &toss,
                           &st->iowait, &st->irq, &st->softirq) < 4)
                        continue;
                st->online = true;
                if (cpu != -1)
                        out->online++;
                if (cpu > out->max)
                        out->max = cpu;

        next:
                // Go to the next line
                while (*pos && *pos != '\n')
                        pos++;
                if (*pos) pos++;
        }

        if ((lseek(cpustats_fd, 0, SEEK_SET)) < 0)
                epanic("failed to seek %s/stat", proc_path);
}
Exemple #3
0
void
cpustats_loadavg(float load[3])
{
        if ((readn_str(cpustats_load_fd, cpustats_buf, cpustats_buf_size)) < 0)
                epanic("failed to read %s/loadavg", proc_path);
        if (sscanf(cpustats_buf, "%f %f %f",
                   &load[0], &load[1], &load[2]) != 3)
                epanic("failed to parse %s/loadavg", proc_path);
        if ((lseek(cpustats_load_fd, 0, SEEK_SET)) < 0)
                epanic("failed to seek %s/loadavg", proc_path);
}
Exemple #4
0
struct cpustats*
cpustats_alloc(void)
{
        struct cpustats *res = malloc(sizeof *res);
        if (!res)
                epanic("allocating cpustats");
        memset(res, 0, sizeof *res);
        res->cpus = malloc(cpustats_cpus * sizeof *res->cpus);
        if (!res->cpus)
                epanic("allocating per-CPU cputats");
        memset(res->cpus, 0, cpustats_cpus * sizeof *res->cpus);
        return res;
}
Exemple #5
0
void
CPU_SetAffinity(CPU_Set_t *cs)
{
        int res = numa_sched_setaffinity(0, cs);
        if (res < 0)
                epanic("numa_sched_setaffinity");
}
Exemple #6
0
static void
ui_init_panes(int n)
{
        free(ui_panes);
        ui_num_panes = n;
        if (!(ui_panes = malloc(n * sizeof *ui_panes)))
                epanic("allocating panes");
        
}
Exemple #7
0
CPU_Set_t *
CPU_GetAffinity(void)
{
        struct bitmask *cs = numa_allocate_cpumask();
        int res = numa_sched_getaffinity(0, cs);
        if (res < 0)
                epanic("numa_sched_getaffinity");
        return cs;
}
Exemple #8
0
void
cpustats_init(void)
{
        if ((cpustats_fd = open("/proc/stat", O_RDONLY)) < 0)
                epanic("failed to open /proc/stat");
        if ((cpustats_load_fd = open("/proc/loadavg", O_RDONLY)) < 0)
                epanic("failed to open /proc/loadavg");

        // Find the maximum number of CPU's we'll need
        char *poss = read_all("/sys/devices/system/cpu/possible");
        cpustats_cpus = cpuset_max(poss) + 1;
        free(poss);

        // Allocate a big buffer to read /proc/stat in to
        cpustats_buf_size = cpustats_cpus * 128;
        if (!(cpustats_buf = malloc(cpustats_buf_size)))
                epanic("allocating cpustats file buffer");
}
Exemple #9
0
void
CPU_Bind(int cpu)
{
        CPU_Set_t *cs = numa_allocate_cpumask();
        numa_bitmask_setbit(cs, cpu);
        int res = numa_sched_setaffinity(0, cs);
        if (res < 0)
                epanic("bindToCPU(%d)", cpu);
        CPU_FreeSet(cs);
}
Exemple #10
0
char *
read_all(const char *path)
{
        FILE *fp = fopen(path, "rb");
        if (!fp)
                epanic("failed to open %s", path);
        if (fseek(fp, 0, SEEK_END) < 0)
                epanic("failed to seek in %s", path);
        long len = ftell(fp);
        char *buf = malloc(len + 1);
        if (!buf)
                epanic("read_all");
        rewind(fp);
        size_t rlen = fread(buf, 1, len, fp);
        if ((errno = ferror(fp)))
                epanic("failed to read %s", path);
        buf[rlen] = 0;
        fclose(fp);
        return buf;
}
Exemple #11
0
void
cpustats_init(void)
{
        cpustats_findproc();

        // Find the maximum number of CPU's we'll need
#ifdef __FreeBSD__
        size_t oldlenp = sizeof(cpustats_cpus);
        if (sysctlbyname("kern.smp.maxcpus", &cpustats_cpus, &oldlenp, NULL, 0))
                epanic("failed to read kern.smp.maxcpus sysctl");
#else
        char *poss = read_all("/sys/devices/system/cpu/possible");
        cpustats_cpus = cpuset_max(poss) + 1;
        free(poss);
#endif //__FreeBSD__

        // Allocate a big buffer to read /proc/stat in to
        cpustats_buf_size = cpustats_cpus * 128;
        if (!(cpustats_buf = malloc(cpustats_buf_size)))
                epanic("allocating cpustats file buffer");
}
Exemple #12
0
int
main(int argc, char **argv)
{
        bool force_ascii = false;
        int delay = 500;

        int opt;
        while ((opt = getopt(argc, argv, "ad:h")) != -1) {
                switch (opt) {
                case 'a':
                        force_ascii = true;
                        break;
                case 'd':
                {
                        char *end;
                        float val = strtof(optarg, &end);
                        if (*end) {
                                fprintf(stderr, "Delay argument (-d) requires "
                                        "a number\n");
                                exit(2);
                        }
                        delay = 1000 * val;
                        break;
                }
                default:
                        fprintf(stderr, "Usage: %s [-a] [-d delay]\n", argv[0]);
                        if (opt == 'h') {
                                fprintf(stderr,
                                        "\n"
                                        "Display CPU usage as a bar chart.\n"
                                        "\n"
                                        "Options:\n"
                                        "  -a       Use ASCII-only bars (instead of Unicode)\n"
                                        "  -d SECS  Specify delay between updates (decimals accepted)\n"
                                        "\n"
                                        "If your bars look funky, use -a or specify LANG=C.\n"
                                        "\n"
                                        "For kernels prior to 2.6.37, using a small delay on a large system can\n"
                                        "induce significant system time overhead.\n");
                                exit(0);
                        }
                        exit(2);
                }
        }
        if (optind < argc) {
                fprintf(stderr, "Unexpected arguments\n");
                exit(2);
        }

        struct sigaction sa = {
                .sa_handler = on_sigint
        };
        sigaction(SIGINT, &sa, NULL);

        cpustats_init();
        term_init();
        ui_init(force_ascii);

        struct cpustats *before = cpustats_alloc(),
                *after = cpustats_alloc(),
                *delta = cpustats_alloc(),
                *prevLayout = cpustats_alloc();

        cpustats_read(before);
        cpustats_subtract(prevLayout, before, before);
        ui_layout(prevLayout);
        fflush(stdout);
        while (!need_exit) {
                // Sleep or take input
                struct pollfd pollfd = {
                        .fd = 0,
                        .events = POLLIN
                };
                if (poll(&pollfd, 1, delay) < 0 && errno != EINTR)
                        epanic("poll failed");
                if (pollfd.revents & POLLIN) {
                        char ch = 0;
                        if (read(0, &ch, 1) < 0)
                                epanic("read failed");
                        if (ch == 'q')
                                break;
                }

                // Get new statistics
                cpustats_read(after);
                cpustats_subtract(delta, after, before);

                // Recompute the layout if necessary
                if (term_check_resize() || !cpustats_sets_equal(delta, prevLayout))
                        ui_layout(delta);

                // Show the load average
                float loadavg[3];
                cpustats_loadavg(loadavg);
                ui_show_load(loadavg);

                if (delta->real) {
                        ui_compute_bars(delta);
                        ui_show_bars();
                }

                // Done updating UI
                fflush(stdout);

                SWAP(before, after);
                SWAP(delta, prevLayout);
        }

        return 0;
}
Exemple #13
0
void
ui_layout(struct cpustats *cpus)
{
        int i;

        putp(exit_attribute_mode);
        putp(clear_screen);

        // Draw key at the top
        const struct ui_stat *si;
        for (si = ui_stats; si->name; si++) {
                putp(tiparm(set_a_background, si->color));
                printf("  ");
                putp(exit_attribute_mode);
                printf(" %s ", si->name);
        }

        // Create one pane by default
        ui_init_panes(1);
        ui_panes[0].barpos = 0;

        // Create bar info
        free(ui_bars);
        ui_num_bars = cpus->online + 1;
        ui_bars = malloc(ui_num_bars * sizeof *ui_bars);
        if (!ui_bars)
                epanic("allocating bars");

        // Create average bar
        ui_bars[0].start = 0;
        ui_bars[0].width = 3;
        ui_bars[0].cpu = -1;

        // Lay out labels
        char buf[16];
        snprintf(buf, sizeof buf, "%d", cpus->max);
        int length = strlen(buf);
        int label_len;
        int w = COLS - 4;

        if ((length + 1) * cpus->online < w) {
                // Lay out the labels horizontally
                ui_panes[0].start = 1;
                ui_bar_length = MAX(0, LINES - ui_panes[0].start - 2);
                label_len = 1;
                putp(tiparm(cursor_address, LINES, 0));
                int bar = 1;
                for (i = 0; i <= cpus->max; ++i) {
                        if (cpus->cpus[i].online) {
                                ui_bars[bar].start = 4 + (bar-1)*(length+1);
                                ui_bars[bar].width = length;
                                ui_bars[bar].cpu = i;
                                bar++;
                        }
                }
        } else {
                // Lay out the labels vertically
                int pad = 0, count = cpus->online;
                ui_panes[0].start = length;
                ui_bar_length = MAX(0, LINES - ui_panes[0].start - 2);
                label_len = length;

                if (cpus->online * 2 < w) {
                        // We have space for padding
                        pad = 1;
                } else if (cpus->online >= w && COLS >= 2) {
                        // We don't have space for all of them
                        int totalw = 4 + cpus->online;
                        ui_init_panes((totalw + COLS - 2) / (COLS - 1));
                        int plength = (LINES - 2) / ui_num_panes;
                        for (i = 0; i < ui_num_panes; ++i) {
                                ui_panes[i].start =
                                        (ui_num_panes-i-1) * plength + length;
                                ui_panes[i].barpos = i * (COLS - 1);
                                ui_panes[i].width = COLS - 1;
                        }
                        ui_bar_length = MAX(0, plength - length);
                }

                int bar = 1;
                for (i = 0; i <= cpus->max; ++i) {
                        if (cpus->cpus[i].online) {
                                ui_bars[bar].start = 4 + (bar-1)*(pad+1);
                                ui_bars[bar].width = 1;
                                ui_bars[bar].cpu = i;
                                bar++;
                        }
                }
        }

        // Allocate bar display buffers
        free(ui_display);
        free(ui_fore);
        free(ui_back);
        ui_bar_width = ui_bars[ui_num_bars-1].start + ui_bars[ui_num_bars-1].width;
        if (!(ui_display = malloc(ui_bar_length * ui_bar_width)))
                epanic("allocating display buffer");
        if (!(ui_fore = malloc(ui_bar_length * ui_bar_width)))
                epanic("allocating foreground buffer");
        if (!(ui_back = malloc(ui_bar_length * ui_bar_width)))
                epanic("allocating background buffer");

        if (ui_ascii) {
                // ui_display and ui_fore don't change in ASCII mode
                memset(ui_display, 0, ui_bar_length * ui_bar_width);
                memset(ui_fore, 0xff, ui_bar_length * ui_bar_width);
        }

        // Trim down the last pane to the right width
        ui_panes[ui_num_panes - 1].width =
                ui_bar_width - ui_panes[ui_num_panes - 1].barpos;

        // Draw labels
        char *label_buf = malloc(ui_bar_width * label_len);
        if (!label_buf)
                epanic("allocating label buffer");
        memset(label_buf, ' ', ui_bar_width * label_len);
        int bar;
        for (bar = 0; bar < ui_num_bars; ++bar) {
                char *out = &label_buf[ui_bars[bar].start];
                int len;
                if (bar == 0) {
                        strcpy(buf, "avg");
                        len = 3;
                } else
                        len = snprintf(buf, sizeof buf, "%d", ui_bars[bar].cpu);
                if (label_len == 1 || bar == 0)
                        memcpy(out, buf, len);
                else
                        for (i = 0; i < len; i++)
                                out[i * ui_bar_width] = buf[i];
        }
        for (i = 0; i < ui_num_panes; ++i) {
                putp(tiparm(cursor_address, LINES - ui_panes[i].start, 0));

                int row;
                for (row = 0; row < label_len; ++row) {
                        if (row > 0)
                                putchar('\n');
                        fwrite(&label_buf[row*ui_bar_width + ui_panes[i].barpos],
                               1, ui_panes[i].width, stdout);
                }
        }
        free(label_buf);

}
Exemple #14
0
/**
 * Run fn on each CPU in cs, passing the CPU number and the opaque
 * argument, then join with all threads.  If cs is NULL, fn is run on
 * all affinitized CPU's (all CPU's by default).  If results is
 * non-NULL, it will be pointed to an array of *nResults results.  The
 * caller must free this array.
 */
void
CPU_RunOnSet(int (*fn)(int, void*), void *opaque, CPU_Set_t *cs,
             int **results, int *nResults)
{
        int r;

        CPU_Set_t *aff = CPU_GetAffinity();
        if (!cs)
                cs = aff;

        int count = CPU_GetCount(cs);
        int cpus = cpuSetMax(cs);
        struct runOnCPU roc[cpus];
        pthread_t threads[cpus];
        pthread_barrier_t barrier;
        r = pthread_barrier_init(&barrier, NULL, count + 1);
        if (r != 0) {
                errno = r;
                epanic("failed to create pthread barrier");
        }

        if (results)
                *results = malloc(count * sizeof(*results));
        if (nResults)
                *nResults = count;

        int i = 0;
        for (int cpu = 0; cpu < cpus; ++cpu) {
                if (numa_bitmask_isbitset(cs, cpu)) {
                        roc[cpu].fn = fn;
                        roc[cpu].cpu = cpu;
                        roc[cpu].i = i++;
                        roc[cpu].results = results ? *results : NULL;
                        roc[cpu].barrier = &barrier;
                        // Make sure the stack and such are allocated
                        // on this core.
                        CPU_Bind(cpu);
                        r = pthread_create(&threads[cpu], NULL, runOnCPUThread,
                                           &roc[cpu]);
                        if (r != 0) {
                                errno = r;
                                epanic("failed to spawn thread on CPU %d", cpu);
                        }
                }
        }
        CPU_SetAffinity(aff);

        pthread_barrier_wait(&barrier);

        for (int cpu = 0; cpu < cpus; ++cpu) {
                if (numa_bitmask_isbitset(cs, cpu)) {
                        r = pthread_join(threads[cpu], NULL);
                        if (r != 0) {
                                errno = r;
                                epanic("failed to join thread on CPU %d", cpu);
                        }
                }
        }

        pthread_barrier_destroy(&barrier);
        CPU_FreeSet(aff);
}