From 47e6e7c84f008a53061e661f31ae96629bc694ef Mon Sep 17 00:00:00 2001 From: Igor Pashev Date: Sun, 26 Oct 2014 12:33:50 +0400 Subject: Debian 3.9.10 --- src/pmtop/pmtop.c | 1008 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1008 insertions(+) create mode 100644 src/pmtop/pmtop.c (limited to 'src/pmtop/pmtop.c') diff --git a/src/pmtop/pmtop.c b/src/pmtop/pmtop.c new file mode 100644 index 0000000..dc10fc0 --- /dev/null +++ b/src/pmtop/pmtop.c @@ -0,0 +1,1008 @@ +/* + * Copyright (c) 2013-2014 Red Hat. + * Copyright (c) 1999 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" + +/* + * Outputs psinfo.psargs and top value. + * + * Want to periodically write to stdout + * with the top processes for the following categories: + * + * cpuburn - rate converted proc.psusage.utime + stime + * syscalls - rate converted proc.psusage.pu_sysc + * reads - rate converted proc.psusage.bread + gbread + * writes - rate converted proc.psusage.bwrit + gbwrit + * ctxswitch - rate converted proc.psusage.pu_vctx + proc.psusage.pu_ictx + * residentsize - proc.psusage.rss + * + */ + +#define MAX_PMID 15 /* gives enough space for all possible metrics in fetch */ +char *namelist[MAX_PMID]; +pmID pmidlist[MAX_PMID]; +int type_tab[MAX_PMID]; /* the types of the metrics */ +int valfmt_tab[MAX_PMID]; /* the types of the metrics */ +int num_pmid; +int num_proc_pmid; + +/* indexes into [MAX_PMID] arrays */ +int kernel_utime; +int kernel_stime; +int kernel_sysc; +int kernel_ctx; +int pu_utime; +int pu_stime; +int pu_sysc; +int pu_bread; +int pu_gbread; +int pu_bwrit; +int pu_gbwrit; +int pu_rss; +int pu_vctx; +int pu_ictx; + +char *hostname; +int top = 5; +long num_samples = -1; +int num_inst; +int *instances; +char **instance_names; +char *runtime; +char pid_label_fmt[8]; +char pid_fmt[8]; +char *line_fmt = "%.79s"; +int pid_len; +pmInDom proc_indom; + +/* values of interest */ +#define CPU_VAL 0 +#define SYSCALLS_VAL 1 +#define CTX_VAL 2 +#define WRITES_VAL 3 +#define READS_VAL 4 +#define RSS_VAL 5 +#define USR_VAL 6 +#define SYS_VAL 7 +#define NUM_VALUES 8 + +/* which values to calculate and print */ +#define SHOW_CPU (1<values[CPU_VAL] < c2->values[CPU_VAL]) + return -1; + if (c2->values[CPU_VAL] > c1->values[CPU_VAL]) + return 1; + return 0; +} + +static int +sys_cmp(const void* x, const void* y) +{ + rate_entry_t *c1 = (rate_entry_t *)x; + rate_entry_t *c2 = (rate_entry_t *)y; + + if (c1->values[SYS_VAL] < c2->values[SYS_VAL]) + return -1; + if (c2->values[SYS_VAL] > c1->values[SYS_VAL]) + return 1; + return 0; +} + +static int +usr_cmp(const void* x, const void* y) +{ + rate_entry_t *c1 = (rate_entry_t *)x; + rate_entry_t *c2 = (rate_entry_t *)y; + + if (c1->values[USR_VAL] < c2->values[USR_VAL]) + return -1; + if (c2->values[USR_VAL] > c1->values[USR_VAL]) + return 1; + return 0; +} + +static int +syscall_cmp(const void* x, const void* y) +{ + rate_entry_t *c1 = (rate_entry_t *)x; + rate_entry_t *c2 = (rate_entry_t *)y; + + if (c1->values[SYSCALLS_VAL] < c2->values[SYSCALLS_VAL]) + return -1; + if (c2->values[SYSCALLS_VAL] > c1->values[SYSCALLS_VAL]) + return 1; + return 0; +} + +static int +ctx_cmp(const void* x, const void* y) +{ + rate_entry_t *c1 = (rate_entry_t *)x; + rate_entry_t *c2 = (rate_entry_t *)y; + + if (c1->values[CTX_VAL] < c2->values[CTX_VAL]) + return -1; + if (c2->values[CTX_VAL] > c1->values[CTX_VAL]) + return 1; + return 0; +} + +static int +write_cmp(const void* x, const void* y) +{ + rate_entry_t *c1 = (rate_entry_t *)x; + rate_entry_t *c2 = (rate_entry_t *)y; + + if (c1->values[WRITES_VAL] < c2->values[WRITES_VAL]) + return -1; + if (c2->values[WRITES_VAL] > c1->values[WRITES_VAL]) + return 1; + return 0; +} + +static int +read_cmp(const void* x, const void* y) +{ + rate_entry_t *c1 = (rate_entry_t *)x; + rate_entry_t *c2 = (rate_entry_t *)y; + + if (c1->values[READS_VAL] < c2->values[READS_VAL]) + return -1; + if (c2->values[READS_VAL] > c1->values[READS_VAL]) + return 1; + return 0; +} + +int +rss_cmp(const void* x, const void* y) +{ + rate_entry_t *c1 = (rate_entry_t *)x; + rate_entry_t *c2 = (rate_entry_t *)y; + + if (c1->values[RSS_VAL] < c2->values[RSS_VAL]) + return -1; + if (c2->values[RSS_VAL] > c1->values[RSS_VAL]) + return 1; + return 0; +} + +/* + * sum the rate values for the top entries + */ +void +calc_sum_rates(int num_entries, int val_type) +{ + int i; + double sum = 0; + + for (i = 0; i < min(top, num_entries); i++) { + rate_entry_t *entry = &rate_tab[i]; + sum += entry->values[val_type]; + } + + sum_rates[val_type] = sum; +} + +void +print_top(rate_entry_t *rate_tab, int num_entries, int val_type) +{ + static char line[2048]; + char *ptr; + int i,j; + double x; + + /* --- section heading --- */ + if (global_rates[val_type] <= 0) { + printf("%s\n", format_tab[val_type].label); + } + else { + x = 100*sum_rates[val_type]/global_rates[val_type]; + printf("%s - top %d processes account for %.0f%% of %s\n", + format_tab[val_type].label, + top, x>100?100:x, + format_tab[val_type].fullname); + } + + /* --- fields heading --- */ + printf(pid_label_fmt, "PID"); + for(j=0; j < NUM_VALUES; j++) { + if (show_spec & (1<label_fmt, fmt->label); + } + } + printf(" CMD\n"); + + /* units */ + printf(pid_label_fmt, " "); + for(j=0; j < NUM_VALUES; j++) { + if (show_spec & (1<label_fmt, fmt->units); + } + } + printf("\n"); + + + /* --- values --- */ + for(i=0; i < min(top, num_entries); i++) { + rate_entry_t *entry = &rate_tab[i]; + + *line = '\0'; + ptr = line; + + /* pid */ + sprintf(ptr, pid_fmt, instances[entry->inst]); + ptr += strlen(ptr); + + /* other values */ + for(j=0; j < NUM_VALUES; j++) { + if (show_spec & (1<val_fmt, entry->values[j]); + ptr += strlen(ptr); + } + } + + /* cmd */ + /* skip over leading pid-name in instance name */ + sprintf(ptr, " %s", &instance_names[entry->inst][pid_len+1]); + + /* ensure line fits in certain width */ + printf(line_fmt, line); + printf("\n"); + } + + printf("\n"); +} + +void +doit(void) +{ + int i, r, m, v, sts; + static double now = 0, then = 0; + double delta_d; + static unsigned long num_times = 0; + static pmResult *result[2]; + int num_entries; + + num_times++; + + if (num_times > 2) { + pmFreeResult(result[0]); + + /* if forever, ensure no wrapping */ + if (num_samples < 0) + num_times = 3; + } + + if (num_times > 1) { + result[0] = result[1]; + } + + get_indom(); + if ((sts = pmFetch(num_pmid, pmidlist, &result[1])) < 0) { + for (i = 0; i < num_pmid; i++) + fprintf(stderr, "%s: pmFetch: %s\n", namelist[i], pmErrStr(sts)); + exit(1); + } + + + if (num_times > 1) { + then = result[0]->timestamp.tv_sec + result[0]->timestamp.tv_usec/1000000; + now = result[1]->timestamp.tv_sec + result[1]->timestamp.tv_usec/1000000; + delta_d = now - then; + + if (result[1]->numpmid != num_pmid) { + fprintf(stderr, "%s: Failed to fetch all metrics (%d out of %d)\n", + pmProgname, result[1]->numpmid, num_pmid); + exit(1); + } + + /* --- build value table --- */ + + for(i=0; ivset[m]; + if (vset->numval < 0) { + fprintf(stderr, "%s: Error when fetching metric %s: %s\n", + pmProgname, namelist[m], pmErrStr(vset->numval)); + continue; + } + if (vset->numval == 0) + continue; + valfmt_tab[m] = vset->valfmt; + + /* handle global metrics */ + if (((show_spec & (SHOW_CPU | SHOW_SYS)) && m == kernel_stime) || + ((show_spec & (SHOW_CPU | SHOW_USR)) && m == kernel_utime) || + ((show_spec & SHOW_SYSCALLS) && m == kernel_sysc) || + ((show_spec & SHOW_CTX) && m == kernel_ctx)) { + if (vset->numval != 1) { + fprintf(stderr, "%s: Wrong num of values for kernel metric: %d\n", + pmProgname, vset->numval); + exit(1); + } + global_val[m][r] = vset->vlist[0]; + continue; + } + + /* handle proc-instance metrics */ + + /* Find result inst + * Would be faster to start from where left off + * last time. Need to remember this for a metric/result. + */ + for(v=0; vnumval; v++) { + pmValue val = vset->vlist[v]; + /* go thru each possible inst */ + for(i=0; i 100 ? 100 : cpuburn_val; + global_rates[CPU_VAL] = cpuburn_val; + } + + + if (show_spec & SHOW_SYSCALLS) { + __uint64_t sysc[2]; + + sysc[0] = get_value(kernel_sysc, &global_val[kernel_sysc][0]); + sysc[1] = get_value(kernel_sysc, &global_val[kernel_sysc][1]); + + global_rates[SYSCALLS_VAL] = (sysc[1] - sysc[0]) / delta_d; + } + + if (show_spec & SHOW_CTX) { + __uint64_t ctx[2]; + + ctx[0] = get_value(kernel_ctx, &global_val[kernel_ctx][0]); + ctx[1] = get_value(kernel_ctx, &global_val[kernel_ctx][1]); + + global_rates[CTX_VAL] = (ctx[1] - ctx[0]) / delta_d; + } + + if (show_spec & SHOW_SYS) { + __uint64_t stime[2]; + + stime[0] = get_value(kernel_stime, &global_val[kernel_stime][0]); + stime[1] = get_value(kernel_stime, &global_val[kernel_stime][1]); + + global_rates[SYS_VAL] = ((stime[1] - stime[0]) / delta_d) / 10; + } + + if (show_spec & SHOW_USR) { + __uint64_t utime[2]; + + utime[0] = get_value(kernel_utime, &global_val[kernel_utime][0]); + utime[1] = get_value(kernel_utime, &global_val[kernel_utime][1]); + + global_rates[USR_VAL] = ((utime[1] - utime[0]) / delta_d) / 10; + } + + + /* handle proc-instance metrics */ + num_entries = 0; + for(i=0; inum < (num_proc_pmid*2)) { + /* couldn't get instances for all proc metrics for both(*2) results */ + continue; + } + + rt = &rate_tab[num_entries]; + rt->inst = i; + + /* create cpuburn values */ + if (show_spec & SHOW_CPU) { + __uint64_t utime[2], stime[2]; + double cpuburn_val; + + utime[0] = get_value(pu_utime, &vt->values[pu_utime][0]); + utime[1] = get_value(pu_utime, &vt->values[pu_utime][1]); + stime[0] = get_value(pu_stime, &vt->values[pu_stime][0]); + stime[1] = get_value(pu_stime, &vt->values[pu_stime][1]); + + cpuburn_val = (utime[1] - utime[0] + stime[1] - stime[0]) / delta_d; + cpuburn_val /= 10; + cpuburn_val = cpuburn_val > 100 ? 100 : cpuburn_val; + rt->values[CPU_VAL] = cpuburn_val; + } + + /* create syscalls values */ + if (show_spec & SHOW_SYSCALLS) { + __uint64_t sysc[2]; + + sysc[0] = get_value(pu_sysc, &vt->values[pu_sysc][0]); + sysc[1] = get_value(pu_sysc, &vt->values[pu_sysc][1]); + + rt->values[SYSCALLS_VAL] = (sysc[1] - sysc[0]) / delta_d; + } + + /* create ctx values */ + if (show_spec & SHOW_CTX) { + double ictx, vctx; + + vctx = get_value(pu_vctx, &vt->values[pu_vctx][1]) - + get_value(pu_vctx, &vt->values[pu_vctx][0]); + ictx = get_value(pu_ictx, &vt->values[pu_ictx][1]) - + get_value(pu_ictx, &vt->values[pu_ictx][0]); + + rt->values[CTX_VAL] = (vctx + ictx) / delta_d; + } + + /* create reads values */ + if (show_spec & SHOW_READS) { + double br, gbr; + + br = get_value(pu_bread, &vt->values[pu_bread][1]) - + get_value(pu_bread, &vt->values[pu_bread][0]); + gbr = get_value(pu_gbread, &vt->values[pu_gbread][1]) - + get_value(pu_gbread, &vt->values[pu_gbread][0]); + + rt->values[READS_VAL] = (gbr * 1024 * 1024 + br / 1024) / delta_d; + } + + /* create writes values */ + if (show_spec & SHOW_WRITES) { + double bw, gbw; + + bw = get_value(pu_bwrit, &vt->values[pu_bwrit][1]) - + get_value(pu_bwrit, &vt->values[pu_bwrit][0]); + gbw = get_value(pu_gbwrit, &vt->values[pu_gbwrit][1]) - + get_value(pu_gbwrit, &vt->values[pu_gbwrit][0]); + + rt->values[WRITES_VAL] = (gbw * 1024 * 1024 + bw / 1024) / delta_d; + } + + /* create rss values */ + if (show_spec & SHOW_RSS) { + rt->values[RSS_VAL] = get_value(pu_rss, &vt->values[pu_rss][1]); + } + + /* create sys cpu time values */ + if (show_spec & SHOW_SYS) { + __uint64_t stime[2]; + + stime[0] = get_value(pu_stime, &vt->values[pu_stime][0]); + stime[1] = get_value(pu_stime, &vt->values[pu_stime][1]); + + rt->values[SYS_VAL] = ((stime[1] - stime[0]) / delta_d) / 10; + rt->values[SYS_VAL] = rt->values[SYS_VAL] > 100 ? 100 : rt->values[SYS_VAL]; + } + + /* create user cpu time values */ + if (show_spec & SHOW_USR) { + __uint64_t utime[2]; + + utime[0] = get_value(pu_utime, &vt->values[pu_utime][0]); + utime[1] = get_value(pu_utime, &vt->values[pu_utime][1]); + + rt->values[USR_VAL] = ((utime[1] - utime[0]) / delta_d) / 10; + rt->values[USR_VAL] = rt->values[USR_VAL] > 100 ? 100 : rt->values[USR_VAL]; + } + + + num_entries++; + } + + /* --- report values --- */ + printf("HOST: %s\n", hostname); + printf("DATE: %s\n\n", get_time()); + + if (show_spec & SHOW_CPU) { + qsort(rate_tab, num_entries, sizeof(rate_entry_t), cpuburn_cmp); + calc_sum_rates(num_entries, CPU_VAL); + print_top(rate_tab, num_entries, CPU_VAL); + } + + if (show_spec & SHOW_SYSCALLS) { + qsort(rate_tab, num_entries, sizeof(rate_entry_t), syscall_cmp); + calc_sum_rates(num_entries, SYSCALLS_VAL); + print_top(rate_tab, num_entries, SYSCALLS_VAL); + } + + if (show_spec & SHOW_CTX) { + qsort(rate_tab, num_entries, sizeof(rate_entry_t), ctx_cmp); + calc_sum_rates(num_entries, CTX_VAL); + print_top(rate_tab, num_entries, CTX_VAL); + } + + if (show_spec & SHOW_WRITES) { + qsort(rate_tab, num_entries, sizeof(rate_entry_t), write_cmp); + print_top(rate_tab, num_entries, WRITES_VAL); + } + + if (show_spec & SHOW_READS) { + qsort(rate_tab, num_entries, sizeof(rate_entry_t), read_cmp); + print_top(rate_tab, num_entries, READS_VAL); + } + + if (show_spec & SHOW_RSS) { + qsort(rate_tab, num_entries, sizeof(rate_entry_t), rss_cmp); + print_top(rate_tab, num_entries, RSS_VAL); + } + + if (show_spec & SHOW_SYS) { + qsort(rate_tab, num_entries, sizeof(rate_entry_t), sys_cmp); + calc_sum_rates(num_entries, SYS_VAL); + print_top(rate_tab, num_entries, SYS_VAL); + } + + if (show_spec & SHOW_USR) { + qsort(rate_tab, num_entries, sizeof(rate_entry_t), usr_cmp); + calc_sum_rates(num_entries, USR_VAL); + print_top(rate_tab, num_entries, USR_VAL); + } + } + + if (num_times-1 == num_samples) { + exit(0); + } +} + +/* + * go thru show_spec and build up the namelist and + * index variables into the array + */ +void +create_namelist(void) +{ + int i = 0; /* for each pmid */ + int j = 0; /* for global pmids */ + + if (show_spec & SHOW_CPU || show_spec & SHOW_USR) { + namelist[i] = "proc.psusage.utime"; + pu_utime = i; + i++; + namelist[i] = "kernel.all.cpu.user"; + kernel_utime = i; + i++; j++; + } + if (show_spec & SHOW_CPU || show_spec & SHOW_SYS) { + namelist[i] = "proc.psusage.stime"; + pu_stime = i; + i++; + namelist[i] = "kernel.all.cpu.sys"; + kernel_stime = i; + i++; j++; + } + if (show_spec & SHOW_SYSCALLS) { + namelist[i] = "proc.psusage.sysc"; + pu_sysc = i; + i++; + namelist[i] = "kernel.all.syscall"; + kernel_sysc = i; + i++; j++; + } + if (show_spec & SHOW_CTX) { + namelist[i] = "proc.psusage.ictx"; + pu_ictx = i; + i++; + namelist[i] = "proc.psusage.vctx"; + pu_vctx = i; + i++; + namelist[i] = "kernel.all.pswitch"; + kernel_ctx = i; + i++; j++; + } + if (show_spec & SHOW_READS) { + namelist[i] = "proc.psusage.bread"; + pu_bread = i; + i++; + namelist[i] = "proc.psusage.gbread"; + pu_gbread = i; + i++; + } + if (show_spec & SHOW_WRITES) { + namelist[i] = "proc.psusage.bwrit"; + pu_bwrit = i; + i++; + namelist[i] = "proc.psusage.gbwrit"; + pu_gbwrit = i; + i++; + } + if (show_spec & SHOW_RSS) { + namelist[i] = "proc.psusage.rss"; + pu_rss = i; + i++; + } + + num_pmid = i; + num_proc_pmid = i - j; +} + +static void +get_indom(void) +{ + static int onetrip = 1; + + if (instances != NULL) + free(instances); + if (instance_names != NULL) + free(instance_names); + + if ((num_inst = pmGetInDom(proc_indom, &instances, &instance_names)) <= 0) { + fprintf(stderr, "%s: Failed to find any proc instances : %s\n", + pmProgname, pmErrStr(num_inst)); + exit(1); + } + + + /* process the instance names + * - find how long a pid is + * - create pid label fmt based on pid length + */ + if (onetrip) { + char *str = instance_names[0]; + + onetrip=0; + + /* get pid str up to first space */ + pid_len = 0; + while (*str != '\0' && *str != ' ') { + pid_len++; + str++; + } + if (*str == '\0') { + /* shouldn't happen */ + fprintf(stderr, "%s: Bad proc instance : %s\n", + pmProgname, instance_names[0]); + exit(1); + } + + sprintf(pid_label_fmt, "%%%ds", pid_len); + sprintf(pid_fmt, "%%%dd", pid_len); + } + + + rate_tab = realloc(rate_tab, sizeof(rate_entry_t)*num_inst); + if (rate_tab == NULL) { + fprintf(stderr, "%s: Failed to allocate rate table\n", + pmProgname); + exit(1); + } + + val_tab = realloc(val_tab, sizeof(val_entry_t)*num_inst); + if (val_tab == NULL) { + fprintf(stderr, "%s: Failed to allocate value table\n", + pmProgname); + exit(1); + } + + pmDelProfile(proc_indom, 0, NULL); + pmAddProfile(proc_indom, num_inst, instances); +} + +static int +overrides(int opt, pmOptions *opts) +{ + if (opt == 'p') + return 1; + + if (opt == 's') + num_samples = atoi(opts->optarg); /* continue processing 's' */ + return 0; +} + +int +main(int argc, char *argv[]) +{ + int c; + int sts; + char *endnum; + pmDesc desc; + int one_trip = 1; + + while ((c = pmGetOptions(argc, argv, &opts)) != EOF) { + switch (c) { + + case 'w': /* wide flag */ + line_fmt = "%.1024s"; + break; + + case 'p': /* show flag */ + if (one_trip) { + show_spec = 0; + one_trip = 0; + } + if ((sts = parse_show_spec(opts.optarg)) < 0) { + pmprintf("%s: unrecognized print flag specification (%s)\n", + pmProgname, opts.optarg); + opts.errors++; + } else { + show_spec |= sts; + } + break; + + case 'm': /* top N */ + top = (int)strtol(opts.optarg, &endnum, 10); + if (top <= 0) { + pmprintf("%s: -m requires a positive integer\n", pmProgname); + opts.errors++; + } + break; + } + } + + if (opts.optind < argc) + opts.errors++; + + if (opts.errors) { + pmUsageMessage(&opts); + exit(1); + } + + create_namelist(); + + if (opts.interval.tv_sec == 0) + opts.interval.tv_sec = 2; + + if (opts.nhosts > 0) + hostname = opts.hosts[0]; + else + hostname = "local:"; + + if ((sts = c = pmNewContext(PM_CONTEXT_HOST, hostname)) < 0) { + fprintf(stderr, "%s: Cannot connect to PMCD on host \"%s\": %s\n", + pmProgname, hostname, pmErrStr(sts)); + exit(1); + } + hostname = (char *)pmGetContextHostName(c); + + if (pmGetContextOptions(c, &opts)) { + pmflush(); + exit(1); + } + + if ((sts = pmLookupName(num_pmid, namelist, pmidlist)) < 0) { + fprintf(stderr, "%s: Failed to lookup metrics : %s\n", + pmProgname, pmErrStr(sts)); + exit(1); + } + + for (c = 0; c < num_pmid; c++) { + if ((sts = pmLookupDesc(pmidlist[c], &desc)) < 0) { + fprintf(stderr, "%s: Failed to lookup descriptor for metric \"%s\": %s\n", + pmProgname, namelist[c], pmErrStr(sts)); + exit(1); + } + type_tab[c] = desc.type; + /* ASSUMES that the first metric will always be a proc metric */ + if (c == 0) { + proc_indom = desc.indom; + } + } + + for (;;) { + doit(); + __pmtimevalSleep(opts.interval); + } + + return 0; +} -- cgit v1.2.3