diff --git a/src/pmdas/linux_proc/GNUmakefile b/src/pmdas/linux_proc/GNUmakefile index c259bda..2b41e9a 100644 --- a/src/pmdas/linux_proc/GNUmakefile +++ b/src/pmdas/linux_proc/GNUmakefile @@ -25,16 +25,20 @@ PMDAINIT = proc_init PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) CONF_LINE = "proc 3 pipe binary $(PMDADIR)/$(CMDTARGET) -d 3" -CFILES = pmda.c \ - cgroups.c proc_pid.c proc_runq.c ksym.c getinfo.c contexts.c +CFILES = pmda.c cgroups.c proc_pid.c proc_runq.c \ + ksym.c getinfo.c contexts.c gram_node.c config.c error.c HFILES = clusters.h indom.h \ - cgroups.h proc_pid.h proc_runq.h ksym.h getinfo.h contexts.h + cgroups.h proc_pid.h proc_runq.h ksym.h getinfo.h contexts.h hotproc.h gram_node.h config.h + +LFILES = lex.l +YFILES = gram.y SCRIPTS = Install Remove VERSION_SCRIPT = exports HELPTARGETS = help.dir help.pag -LDIRT = $(HELPTARGETS) domain.h $(VERSION_SCRIPT) +LSRCFILES = help root root_proc linux_proc_migrate.conf $(SCRIPTS) +LDIRT = $(HELPTARGETS) domain.h $(VERSION_SCRIPT) $(YFILES:%.y=%.tab.?) LLDLIBS = $(PCP_PMDALIB) LCFLAGS = $(INVISIBILITY) @@ -46,6 +50,7 @@ LCFLAGS = $(INVISIBILITY) MAN_SECTION = 1 MAN_PAGES = pmda$(IAM).$(MAN_SECTION) MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +LLDLIBS += -lfl default: build-me @@ -59,7 +64,7 @@ build-me: domain.h $(LIBTARGET) $(CMDTARGET) $(HELPTARGETS) install: default $(INSTALL) -m 755 -d $(PMDADIR) - $(INSTALL) -m 644 domain.h help help.dir help.pag root root_proc $(PMDADIR) + $(INSTALL) -m 644 domain.h help help.dir help.pag root root_proc hotproc.conf $(PMDADIR) $(INSTALL) -m 755 $(LIBTARGET) $(CMDTARGET) $(SCRIPTS) $(PMDADIR) $(INSTALL) -m 644 root_proc $(PCP_VAR_DIR)/pmns/root_proc $(INSTALL) -m 644 linux_proc_migrate.conf $(PCP_VAR_DIR)/config/pmlogrewrite/linux_proc_migrate.conf @@ -69,6 +74,11 @@ build-me: install: endif +.NOTPARALLEL: +gram.tab.h gram.tab.c: gram.y + +config.o lex.o gram.tab.o: gram.tab.h + default_pcp : default install_pcp : install @@ -82,6 +92,8 @@ $(VERSION_SCRIPT): domain.h: ../../pmns/stdpmid $(DOMAIN_MAKERULE) +gram.tab.o: hotproc.h +lex.o: hotproc.h cgroups.o pmda.o: clusters.h cgroups.o pmda.o: cgroups.h cgroups.o pmda.o proc_pid.o proc_runq.o: proc_pid.h diff --git a/src/pmdas/linux_proc/clusters.h b/src/pmdas/linux_proc/clusters.h index e1c8c2a..969e3e7 100644 --- a/src/pmdas/linux_proc/clusters.h +++ b/src/pmdas/linux_proc/clusters.h @@ -39,10 +39,22 @@ #define CLUSTER_NET_CLS_GROUPS 47 /* network classification control groups */ #define CLUSTER_BLKIO_GROUPS 49 /* blkio control groups */ #define CLUSTER_PID_FD 51 /* /proc//fd */ + +#define CLUSTER_HOTPROC_PID_STAT 52 /* /proc//stat */ +#define CLUSTER_HOTPROC_PID_STATM 53 /* /proc//statm + /proc//maps */ +#define CLUSTER_HOTPROC_PID_CGROUP 54 /* /proc//cgroup */ +#define CLUSTER_HOTPROC_PID_LABEL 55 /* /proc//attr/current (label) */ +#define CLUSTER_HOTPROC_PID_STATUS 56 /* /proc//status */ +#define CLUSTER_HOTPROC_PID_SCHEDSTAT 57 /* /proc//schedstat */ +#define CLUSTER_HOTPROC_PID_IO 58 /* /proc//io */ +#define CLUSTER_HOTPROC_PID_FD 59 /* /proc//fd */ +#define CLUSTER_HOTPROC_GLOBAL 60 /* overall hotproc stats and controls*/ +#define CLUSTER_HOTPROC_PRED 61 /* derived hotproc metrics */ + /* Note: do not use higher than (1 << CGROUP_SPLIT)-1 as cluster ID */ #define MIN_CLUSTER 8 /* first cluster number we use here */ -#define NUM_CLUSTERS 52 /* one more than highest cluster number used */ +#define NUM_CLUSTERS 62 /* one more than highest cluster number used */ #define MAX_CLUSTER 63 /* last available - fill gaps if more needed */ #endif /* _CLUSTERS_H */ diff --git a/src/pmdas/linux_proc/config.c b/src/pmdas/linux_proc/config.c new file mode 100644 index 0000000..ffad9e2 --- /dev/null +++ b/src/pmdas/linux_proc/config.c @@ -0,0 +1,561 @@ +/* + * Copyright (c) 1995 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 +#include +#include +#include +#include +#include + +#define _REGEX_RE_COMP +#include +#include + +#include "pmapi.h" + +#include "gram_node.h" +#include "gram.tab.h" +#include "config.h" + +char *conf_buffer = NULL; /* contains config text */ +char *pred_buffer = NULL; /* contains parsed predicate */ + +static bool_node *the_tree = NULL; +static config_vars *the_vars = NULL; + +/* internal functions */ +static int eval_predicate(bool_node *); +static int eval_comparison(bool_node *); +static int eval_num_comp(N_tag, bool_node *, bool_node *); +static int eval_str_comp(N_tag, bool_node *, bool_node *); +static int eval_match_comp(N_tag, bool_node *, bool_node *); +static char* get_strvalue(bool_node *); +static double get_numvalue(bool_node *); +static void eval_error(char *); + +extern int parse_predicate(bool_node **); +extern char *pmProgname; +char *hotproc_configfile; +extern FILE *yyin; + +void +set_conf_buffer( char * buf){ + conf_buffer = strdup(buf); +} + +char * +get_conf_buffer(){ + return conf_buffer; +} + +FILE * +open_config(char configfile[]) +{ + + hotproc_configfile = strdup(configfile); + + FILE *conf; + if ((conf = fopen(hotproc_configfile, "r")) == NULL) { + (void)fprintf(stderr, "%s: Unable to open configuration file \"%s\": %s\n", + pmProgname, hotproc_configfile, osstrerror()); + exit(1); + } + return conf; +} + +int +parse_config(bool_node **tree) +{ + int sts; + FILE *file = NULL; + char tmpname[] = "/var/tmp/pcp.XXXXXX"; + int fid = -1; + struct stat stat_buf; + long size; + char *ptr; + + if ((sts = parse_predicate(tree)) != 0) { + (void)fprintf(stderr, "%s: Failed to parse configuration file\n", pmProgname); + return sts; + } + + /* --- dump to tmp file & read to buffer --- */ + if ((fid = mkstemp(tmpname)) == -1 || + (file = fdopen(fid, "w+")) == NULL) { + sts = -oserror(); + fprintf(stderr, "%s: parse_config: failed to create \"%s\": %s\n", + pmProgname, tmpname, strerror(-sts)); + goto error; + } + if (unlink(tmpname) == -1) { + sts = -oserror(); + fprintf(stderr, "%s: parse_config: failed to unlink \"%s\": %s\n", + pmProgname, tmpname, strerror(-sts)); + goto error; + } + dump_predicate(file, *tree); + fflush(file); + if (fstat(fileno(file), &stat_buf) < 0) { + sts = -oserror(); + fprintf(stderr, "%s: parse_config: failed to stat \"%s\": %s\n", + pmProgname, tmpname, strerror(-sts)); + goto error; + } + size = (long)stat_buf.st_size; + ptr = malloc(size+1); + if (ptr == NULL) { + sts = -oserror(); + fprintf(stderr, "%s: parse_config: failed to malloc: %s\n", + pmProgname, strerror(-sts)); + goto error; + } + rewind(file); + if (fread(ptr, size, 1, file) != 1) { + clearerr(file); + fprintf(stderr, "%s: parse_config: failed to fread \"%s\"\n", + pmProgname, tmpname); + sts = -1; + goto error; + } + (void)fclose(file); + + if (pred_buffer != NULL) + free(pred_buffer); + pred_buffer = ptr; + pred_buffer[size] = '\0'; + return 0; + +error: + if (file != NULL) + (void)fclose(file); + return sts; +} + +void +new_tree(bool_node *tree) +{ + free_tree(the_tree); + the_tree = tree; +} + +void +read_config(FILE *conf) +{ + struct stat stat_buf; + long size; + int sts; + size_t nread; + + /* get length of file */ + sts = fstat(fileno(conf), &stat_buf); + if (sts < 0) { + (void)fprintf(stderr, "%s: Failure to stat configuration file \"%s\": %s\n", + pmProgname, hotproc_configfile, osstrerror()); + exit(1); + } + size = (long)stat_buf.st_size; + + /* create buffer */ + conf_buffer = (char*)malloc(size+1*sizeof(char)); + if (conf_buffer == NULL) { + (void)fprintf(stderr, "%s: Failure to create buffer for configuration file \"%s\"\n", + pmProgname, hotproc_configfile); + exit(1); + } + + /* read whole file into buffer */ + nread = fread(conf_buffer, sizeof(char), size, conf); + if (nread != size) { + (void)fprintf(stderr, "%s: Failure to read configuration file \"%s\" into buffer\n", + pmProgname, hotproc_configfile); + exit(1); + } + conf_buffer[size] = '\0'; /* terminate the buffer */ + + fprintf(stderr, "Buffer: %s\n", conf_buffer); + + if (parse_config(&the_tree) != 0) + exit(1); +} + +void +dump_tree(FILE *f) +{ + dump_bool_tree(f, the_tree); +} + + +int +eval_tree(config_vars *vars) +{ + the_vars = vars; + return eval_predicate(the_tree); +} + +/* + * do predicate testing for qa + */ + +#define QA_LINE 512 + +/* + * Return convention + * EOF = finished line or file + * 0 = error in input + * 1 = successful and continue + */ + +/* + * Read test vars of form: "var=value|var=value|var=value" + */ + +static int +read_test_var(char *line, config_vars *vars) +{ + const char EQUALS = '='; + const char DIVIDER = '|'; + char var[QA_LINE]; + char value[QA_LINE]; + static char *c; + int i = 0; + + /* if line is NULL then continue where left off */ + if (line != NULL) + c = line; + + if (*c == '\n') + return EOF; + + /* --- get variable name --- */ + i = 0; + while(*c != EQUALS && *c != '\n') { + var[i++] = *c++; + } + var[i] = '\0'; + + if (*c == '\n') { + fprintf(stderr, "%s: Error reading test variable, " + "looking for \"%c\"\n", pmProgname, EQUALS); + return 0; + } + + c++; /* skip over EQUALS */ + + /* --- get value --- */ + i = 0; + while(*c != DIVIDER && *c != '\n') { + value[i++] = *c++; + } + value[i] = '\0'; + + if (*c == DIVIDER) /* skip over DIVIDER */ + c++; + + /* --- var = value --- */ + if (strcmp(var, "uid") == 0) { + vars->uid = atoi(value); + } + else if (strcmp(var, "uname") == 0) { + if ((strcpy(vars->uname, value)) == NULL) + goto failure; + } + else if (strcmp(var, "gid") == 0) { + vars->gid = atoi(value); + } + else if (strcmp(var, "gname") == 0) { + if ((strcpy(vars->gname, value)) == NULL) + goto failure; + } + else if (strcmp(var, "fname") == 0) { + (void)strcpy(vars->fname, value); + } + else if (strcmp(var, "psargs") == 0) { + (void)strcpy(vars->psargs, value); + } + else if (strcmp(var, "cpuburn") == 0) { + vars->cpuburn = atof(value); + } + /*else if (strcmp(var, "syscalls") == 0) { + vars->preds.syscalls = atof(value); + } + else if (strcmp(var, "pu_sysc") == 0) { + vars->pu_sysc = atol(value); + } + else if (strcmp(var, "ctxswitch") == 0) { + vars->preds.ctxswitch = atof(value); + } + else if (strcmp(var, "pu_vctx") == 0) { + vars->pu_vctx = atol(value); + } + else if (strcmp(var, "pu_ictx") == 0) { + vars->pu_ictx = atol(value); + }*/ + else if (strcmp(var, "virtualsize") == 0) { + vars->preds.virtualsize = atof(value); + } + //else if (strcmp(var, "pr_size") == 0) { + // vars->pr_size = atol(value); + //} + else if (strcmp(var, "residentsize") == 0) { + vars->preds.residentsize = atof(value); + } + //else if (strcmp(var, "pr_rssize") == 0) { + // vars->pr_rssize = atol(value); + //} + else if (strcmp(var, "iodemand") == 0) { + vars->preds.iodemand = atof(value); + } + //else if (strcmp(var, "pu_gbread") == 0) { + // vars->pu_gbread = atol(value); + //} + //else if (strcmp(var, "pu_bread") == 0) { + // vars->pu_bread = atol(value); + //} + //else if (strcmp(var, "pu_gbwrit") == 0) { + // vars->pu_gbwrit = atol(value); + //} + //else if (strcmp(var, "pu_bwrit") == 0) { + // vars->pu_bwrit = atol(value); + //} + //else if (strcmp(var, "iowait") == 0) { + // vars->preds.iowait = atof(value); + //} + //else if (strcmp(var, "ac_bwtime") == 0) { + // vars->ac_bwtime = atoll(value); + //} + //else if (strcmp(var, "ac_rwtime") == 0) { + // vars->ac_rwtime = atoll(value); + //} + //else if (strcmp(var, "schedwait") == 0) { + // vars->preds.schedwait = atof(value); + //} + //else if (strcmp(var, "ac_qwtime") == 0) { + // vars->ac_qwtime = atoll(value); + //} + else { + fprintf(stderr, "%s: Error unrecognised test variable: \"%s\"\n", + pmProgname, var); + return 0; + } + + return 1; + +failure: + (void)fprintf(stderr, "%s: malloc failed for read_test_var()\n", pmProgname); + exit(1); +} + + +int +read_test_values(FILE *file, config_vars *vars) +{ + static char line[QA_LINE]; + int sts; + int i; + + if (fgets(line, QA_LINE-1, file) == NULL) + return EOF; + + if (strlen(line) == QA_LINE-1) { + fprintf(stderr, "%s: line limit exceeded\n", pmProgname); + return 0; + } + + /* note that line must end in '\n' */ + + /* reset all values */ + (void)memset(vars, 0, sizeof(*vars)); + + /* read each var=value pair for a line */ + for(i=0;/*forever*/;i++) { + sts = read_test_var((i==0?line:NULL), vars); + if (sts == EOF) + return 1; + if (sts == 0) + return 0; + } +} + +static void +eval_error(char *msg) +{ + (void)fprintf(stderr, "%s: Internal error : %s\n", pmProgname, msg?msg:""); + exit(1); +} + +static int +eval_predicate(bool_node *pred) +{ + bool_node *lhs, *rhs; + + switch(pred->tag) { + case N_and: + lhs = pred->data.children.left; + rhs = pred->data.children.right; + return eval_predicate(lhs) && eval_predicate(rhs); + case N_or: + lhs = pred->data.children.left; + rhs = pred->data.children.right; + return eval_predicate(lhs) || eval_predicate(rhs); + case N_not: + lhs = pred->data.children.left; + return !eval_predicate(lhs); + case N_true: + return 1; + case N_false: + return 0; + default: + return eval_comparison(pred); + }/*switch*/ +} + +static int +eval_comparison(bool_node *comp) +{ + bool_node *lhs = comp->data.children.left; + bool_node *rhs = comp->data.children.right; + + switch(comp->tag) { + case N_lt: case N_gt: case N_ge: case N_le: + case N_eq: case N_neq: + return eval_num_comp(comp->tag, lhs, rhs); + case N_seq: case N_sneq: + return eval_str_comp(comp->tag, lhs, rhs); + case N_match: case N_nmatch: + return eval_match_comp(comp->tag, lhs, rhs); + default: + eval_error("comparison"); + // error exits but to keep compiler happy + return 0; + }/*switch*/ +} + +static int +eval_num_comp(N_tag tag, bool_node *lhs, bool_node *rhs) +{ + double x = get_numvalue(lhs); + double y = get_numvalue(rhs); + + switch(tag) { + case N_lt: return (x < y); + case N_gt: return (x > y); + case N_le: return (x <= y); + case N_ge: return (x >= y); + case N_eq: return (x == y); + case N_neq: return (x != y); + default: + eval_error("number comparison"); + // error exits but to keep compiler happy + return 0; + }/*switch*/ +} + +static double +get_numvalue(bool_node *n) +{ + switch(n->tag) { + case N_number: return n->data.num_val; + case N_cpuburn: return the_vars->cpuburn; + // case N_syscalls: return the_vars->preds.syscalls; + // case N_ctxswitch: return the_vars->preds.ctxswitch; + case N_virtualsize: return the_vars->preds.virtualsize; + case N_residentsize: return the_vars->preds.residentsize; + case N_iodemand: return the_vars->preds.iodemand; + case N_iowait: return the_vars->preds.iowait; + // case N_schedwait: return the_vars->preds.schedwait; + case N_gid: return the_vars->gid; + case N_uid: return the_vars->uid; + default: + eval_error("number value"); + // error exits but to keep compiler happy + return 0; + } +} + +static int +eval_str_comp(N_tag tag, bool_node *lhs, bool_node *rhs) +{ + char *x = get_strvalue(lhs); + char *y = get_strvalue(rhs); + + switch(tag) { + case N_seq: return (strcmp(x,y)==0?1:0); + case N_sneq: return (strcmp(x,y)==0?0:1); + default: + eval_error("string comparison"); + // error exits but to keep compiler happy + return 0; + }/*switch*/ +} + +static int +eval_match_comp(N_tag tag, bool_node *lhs, bool_node *rhs) +{ + int sts; + char *res; + char *str= get_strvalue(lhs); + char *pat = get_strvalue(rhs); + + if (rhs->tag != N_pat) { + eval_error("match"); + } + + res = re_comp(pat); + if (res != NULL) { + /* should have been checked at lex stage */ + /* => internal error */ + eval_error(res); + } + sts = re_exec(str); + if (sts < 0) { + eval_error("re_exec"); + } + + switch(tag) { + case N_match: return sts; + case N_nmatch: return !sts; + default: + eval_error("match comparison"); + // error exits but to keep compiler happy + return 0; + }/*switch*/ +} + +static char * +get_strvalue(bool_node *n) +{ + + switch(n->tag) { + case N_str: + case N_pat: + return n->data.str_val; + case N_gname: + //if (the_vars->gname != NULL) + return the_vars->gname; + //else + // return get_gname_info(the_vars->gid); + case N_uname: + //if (the_vars->uname != NULL) + return the_vars->uname; + //else + // return get_uname_info(the_vars->uid); + case N_fname: return the_vars->fname; + case N_psargs: return the_vars->psargs; + default: + eval_error("string value"); + // error exits but to keep compiler happy + return 0; + }/*switch*/ +} diff --git a/src/pmdas/linux_proc/config.h b/src/pmdas/linux_proc/config.h new file mode 100644 index 0000000..09950a7 --- /dev/null +++ b/src/pmdas/linux_proc/config.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 1995 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONFIG_H +#define CONFIG_H + +typedef struct { + //double syscalls; + double ctxswitch; + double virtualsize; + double residentsize; + double iodemand; + double iowait; + //double schedwait; +} derived_pred_t; + + +typedef struct { + uid_t uid; /* real user id */ + gid_t gid; /* real group id */ + char uname[64]; + char gname[64]; + char fname[256]; /* basename of exec()'d pathname */ + char psargs[256]; /* initial chars of arg list */ + double cpuburn; + + derived_pred_t preds; + + /* --- ioctl buffer fields for testing purposes only --- */ + + /* prpsinfo_t fields */ + //ulong_t pr_size; + //ulong_t pr_rssize; + + /* prusage_t fields */ + //ulong_t pu_sysc; + //ulong_t pu_ictx; + //ulong_t pu_vctx; + //ulong_t pu_gbread; + //ulong_t pu_bread; + //ulong_t pu_gbwrit; + //ulong_t pu_bwrit; + + /* accounting fields */ + //accum_t ac_bwtime; + //accum_t ac_rwtime; + //accum_t ac_qwtime; + +} config_vars; + +#include "gram_node.h" + +void set_conf_buffer( char * ); +char *get_conf_buffer(); +FILE *open_config(char []); +void read_config(FILE *); +int parse_config(bool_node **tree); +void new_tree(bool_node *tree); +int eval_tree(config_vars *); +void dump_tree(FILE *); +void do_pred_testing(void); +int read_test_values(FILE *, config_vars *); + +#endif diff --git a/src/pmdas/linux_proc/error.c b/src/pmdas/linux_proc/error.c new file mode 100644 index 0000000..f71c5db --- /dev/null +++ b/src/pmdas/linux_proc/error.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1995 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +void +yywarn(s) +char *s; +{ + extern int yylineno; + + (void)fprintf(stderr, "Warning [line %d]\n%s\n", yylineno, s); +} + +void +yyerror(s) +char *s; +{ + extern int yylineno; + extern char yytext[]; + + (void)fprintf(stderr, "Specification error in configuration\n"); + (void)fprintf(stderr, "[line %d] %s: %s\n", yylineno, s, yytext); +} diff --git a/src/pmdas/linux_proc/gram.y b/src/pmdas/linux_proc/gram.y new file mode 100644 index 0000000..13fc15a --- /dev/null +++ b/src/pmdas/linux_proc/gram.y @@ -0,0 +1,168 @@ +/* + * Copyright (c) 1995 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +%{ +#include +#include "./gram_node.h" + +void yyerror(char *s); +int yylex(void); +int yyparse(void); + +extern int yy_scan_string(const char *); +#define YYDEBUG 0 + +int need_psusage = 0; +int need_accounting = 0; +static bool_node *pred_tree = NULL; +extern char *conf_buffer; + +%} + +%union { + char *y_str; + double y_number; + bool_node *y_node; + } + +%token + NUMBER + +%token + STRING + PATTERN + +%term AND OR NOT + LPAREN RPAREN TRUE FALSE + EQUAL NEQUAL + LTHAN LEQUAL GTHAN GEQUAL + MATCH NMATCH + GID UID CPUBURN GNAME UNAME FNAME PSARGS + SYSCALLS CTXSWITCH VIRTUALSIZE RESIDENTSIZE + IODEMAND IOWAIT SCHEDWAIT + VERSION + +%type + predicate comparison + num_compar numvar + str_compar strvar + pattern_compar + +%type + version + +%left OR +%left AND +%left NOT + +%% + +pred_tree: predicate { pred_tree = $1;} + | version predicate { pred_tree = $2;} + ; + +version: VERSION NUMBER { + float version_num = $2; + + if (version_num != 1.0) { + (void)fprintf(stderr, "Wrong version number in configuration predicate\n"); + (void)fprintf(stderr, "Expected version %.2f, but was given version %.2f .\n", + 1.0, version_num); + YYABORT; + } + } + +predicate: + predicate AND predicate { $$ = create_tnode(N_and, $1, $3); } + | predicate OR predicate { $$ = create_tnode(N_or, $1, $3); } + | NOT predicate { $$ = create_tnode(N_not, $2, NULL); } + | LPAREN predicate RPAREN { $$ = $2; } + | comparison + | TRUE { $$ = create_tag_node(N_true); } + | FALSE { $$ = create_tag_node(N_false); } + ; + +comparison: + num_compar + | str_compar + | pattern_compar + ; + +num_compar: + numvar LTHAN numvar { $$ = create_tnode(N_lt, $1, $3); } + | numvar LEQUAL numvar { $$ = create_tnode(N_le, $1, $3); } + | numvar GTHAN numvar { $$ = create_tnode(N_gt, $1, $3); } + | numvar GEQUAL numvar { $$ = create_tnode(N_ge, $1, $3); } + | numvar EQUAL numvar { $$ = create_tnode(N_eq, $1, $3); } + | numvar NEQUAL numvar { $$ = create_tnode(N_neq, $1, $3); } + ; + +numvar: NUMBER { $$ = create_number_node($1); } + | GID { $$ = create_tag_node(N_gid); } + | UID { $$ = create_tag_node(N_uid); } + | CPUBURN { $$ = create_tag_node(N_cpuburn); } + | SYSCALLS { need_psusage = 1; $$ = create_tag_node(N_syscalls); } + | CTXSWITCH { need_psusage = 1; $$ = create_tag_node(N_ctxswitch); } + | VIRTUALSIZE { $$ = create_tag_node(N_virtualsize); } + | RESIDENTSIZE { $$ = create_tag_node(N_residentsize); } + | IODEMAND { need_psusage = 1; $$ = create_tag_node(N_iodemand); } + | IOWAIT { need_accounting = 1; $$ = create_tag_node(N_iowait); } + | SCHEDWAIT { need_accounting = 1; $$ = create_tag_node(N_schedwait); } + ; + +str_compar: + strvar EQUAL strvar { $$ = create_tnode(N_seq, $1, $3); } + | strvar NEQUAL strvar { $$ = create_tnode(N_sneq, $1, $3); } + ; + +strvar: STRING { $$ = create_str_node($1); } + | GNAME { $$ = create_tag_node(N_gname); } + | UNAME { $$ = create_tag_node(N_uname); } + | FNAME { $$ = create_tag_node(N_fname); } + | PSARGS { $$ = create_tag_node(N_psargs); } + ; + +pattern_compar: + strvar MATCH PATTERN { $$ = create_tnode(N_match, $1, create_pat_node($3)); } + | strvar NMATCH PATTERN { $$ = create_tnode(N_nmatch, $1, create_pat_node($3)); } + ; + + +%% + +int +parse_predicate(bool_node **tree) +{ + int sts; + extern int yylineno; /* defined by lex */ + + yylineno=1; + + start_tree(); + yy_scan_string( conf_buffer ); + sts = yyparse(); + + /* free any partial trees */ + if (sts != 0) { + free_tree(NULL); + return sts; + } + + *tree = pred_tree; + return 0; +} diff --git a/src/pmdas/linux_proc/gram_node.c b/src/pmdas/linux_proc/gram_node.c new file mode 100644 index 0000000..27a95e0 --- /dev/null +++ b/src/pmdas/linux_proc/gram_node.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 1995 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 +#include +#include +#include "./gram_node.h" +#include "pmapi.h" + +/* functions */ +static void dump_comparison(FILE *, bool_node *); +static void dump_var(FILE *, bool_node *); + +static bool_node *node_list = NULL; + +void start_tree(void) +{ + node_list = NULL; +} + +void free_tree(bool_node *root) +{ + bool_node *n, *next; + + if (root == NULL) + root = node_list; /* use last tree */ + + /* free all nodes in list */ + for (n = root; n != NULL; ) { + next = n->next; + if (n->tag == N_pat || n->tag == N_str) + free(n->data.str_val); + free(n); + n = next; + } + + if (root == node_list) + node_list = NULL; +} + +bool_node * +create_tag_node(N_tag tag) +{ + bool_node *new_node; + + new_node = (bool_node*)malloc(sizeof(bool_node)); + if (new_node == NULL) { + fprintf(stderr, "hotproc: malloc failed in config: %s", osstrerror()); + exit(1); + } + new_node->tag = tag; + + /* add to front of node-list */ + new_node->next = node_list; + node_list = new_node; + + return new_node; +} + +bool_node * +create_tnode(N_tag tag, bool_node *lnode, bool_node *rnode) +{ + bool_node *n = create_tag_node(tag); + n->data.children.left = lnode; + n->data.children.right = rnode; + return n; +} + +bool_node * +create_number_node(double x) +{ + bool_node *n = create_tag_node(N_number); + n->data.num_val = x; + return n; +} + + +bool_node *create_str_node(char *str) +{ + bool_node *n = create_tag_node(N_str); + n->data.str_val = str; + return n; +} + +bool_node *create_pat_node(char *str) +{ + bool_node *n = create_tag_node(N_pat); + n->data.str_val = str; + return n; +} + +void +dump_bool_tree(FILE *f, bool_node *tree) +{ + (void)fprintf(f, "--- bool tree ---\n"); + dump_predicate(f, tree); + (void)fprintf(f, "\n--- end bool tree ---\n"); +} + +void +dump_predicate(FILE *f, bool_node *pred) +{ + bool_node *lhs, *rhs; + + switch(pred->tag) { + case N_and: + lhs = pred->data.children.left; + rhs = pred->data.children.right; + (void)fprintf(f, "("); + dump_predicate(f, lhs); + (void)fprintf(f, " && "); + dump_predicate(f, rhs); + (void)fprintf(f, ")"); + break; + case N_or: + lhs = pred->data.children.left; + rhs = pred->data.children.right; + (void)fprintf(f, "("); + dump_predicate(f, lhs); + (void)fprintf(f, " || "); + dump_predicate(f, rhs); + (void)fprintf(f, ")"); + break; + case N_not: + lhs = pred->data.children.left; + (void)fprintf(f, "(! "); + dump_predicate(f, lhs); + (void)fprintf(f, ")"); + break; + case N_true: + (void)fprintf(f, "(true)"); + break; + case N_false: + (void)fprintf(f, "(false)"); + break; + default: + dump_comparison(f, pred); + }/*switch*/ +} + +static void +dump_comparison(FILE *f, bool_node *comp) +{ + bool_node *lhs = comp->data.children.left; + bool_node *rhs = comp->data.children.right; + + (void)fprintf(f, "("); + dump_var(f, lhs); + switch(comp->tag) { + case N_lt: (void)fprintf(f, " < "); break; + case N_gt: (void)fprintf(f, " > "); break; + case N_le: (void)fprintf(f, " <= "); break; + case N_ge: (void)fprintf(f, " >= "); break; + case N_eq: (void)fprintf(f, " == "); break; + case N_seq: (void)fprintf(f, " == "); break; + case N_sneq: (void)fprintf(f, " != "); break; + case N_neq: (void)fprintf(f, " != "); break; + case N_match: (void)fprintf(f, " ~ "); break; + case N_nmatch: (void)fprintf(f, " !~ "); break; + default: (void)fprintf(f, ""); break; + }/*switch*/ + dump_var(f, rhs); + (void)fprintf(f, ")"); +} + +static void +dump_var(FILE *f, bool_node *var) +{ + switch(var->tag) { + case N_str: (void)fprintf(f, "\"%s\"", var->data.str_val); break; + case N_pat: (void)fprintf(f, "\"%s\"", var->data.str_val); break; + case N_number: (void)fprintf(f, "%f", var->data.num_val); break; + case N_uid: (void)fprintf(f, "uid"); break; + case N_gid: (void)fprintf(f, "gid"); break; + case N_uname: (void)fprintf(f, "uname"); break; + case N_gname: (void)fprintf(f, "gname"); break; + case N_fname: (void)fprintf(f, "fname"); break; + case N_psargs: (void)fprintf(f, "psargs"); break; + case N_cpuburn: (void)fprintf(f, "cpuburn"); break; + case N_syscalls: (void)fprintf(f, "syscalls"); break; + case N_ctxswitch: (void)fprintf(f, "ctxswitch"); break; + case N_virtualsize: (void)fprintf(f, "virtualsize"); break; + case N_residentsize: (void)fprintf(f, "residentsize"); break; + case N_iodemand: (void)fprintf(f, "iodemand"); break; + case N_iowait: (void)fprintf(f, "iowait"); break; + case N_schedwait: (void)fprintf(f, "schedwait"); break; + default: (void)fprintf(f, ""); break; + }/*switch*/ +} diff --git a/src/pmdas/linux_proc/gram_node.h b/src/pmdas/linux_proc/gram_node.h new file mode 100644 index 0000000..996208b --- /dev/null +++ b/src/pmdas/linux_proc/gram_node.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1995 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GRAM_NODE_H +#define GRAM_NODE_H + +/* --- types --- */ +typedef enum +{ + N_and, N_or, N_not, + N_lt, N_le, N_gt, N_ge, + N_eq, N_neq, N_seq, N_sneq, + N_match, N_nmatch, + N_str, N_pat, N_number, + N_uid, N_gid, N_uname, N_gname, + N_fname, N_psargs, N_cpuburn, + N_true, N_false, + N_syscalls, N_ctxswitch, + N_virtualsize, N_residentsize, + N_iodemand, N_iowait, N_schedwait +} N_tag; + +typedef struct +{ + struct bool_node *left; + struct bool_node *right; +} bool_children; + +typedef struct bool_node +{ + N_tag tag; + struct bool_node *next; + union { + bool_children children; + char *str_val; + double num_val; + } + data; +} bool_node; + +/* --- functions --- */ + +void free_tree(bool_node *); +void start_tree(void); + +bool_node *create_tnode(N_tag, bool_node *, bool_node *); +bool_node *create_tag_node(N_tag); +bool_node *create_number_node(double); +bool_node *create_str_node(char *); +bool_node *create_pat_node(char *); +void dump_bool_tree(FILE *, bool_node *); +void dump_predicate(FILE *, bool_node *); + +#endif diff --git a/src/pmdas/linux_proc/hotproc.conf b/src/pmdas/linux_proc/hotproc.conf new file mode 100644 index 0000000..3992041 --- /dev/null +++ b/src/pmdas/linux_proc/hotproc.conf @@ -0,0 +1,4 @@ +#pmdahotproc +Version 1.0 + +uname != "root" || cpuburn > 0.05 diff --git a/src/pmdas/linux_proc/hotproc.h b/src/pmdas/linux_proc/hotproc.h new file mode 100644 index 0000000..ba7a1a3 --- /dev/null +++ b/src/pmdas/linux_proc/hotproc.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1995 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PHOTPROC_H +#define PHOTPROC_H + +#include "config.h" + +/* main process node type */ +typedef struct process_t { + pid_t pid; + + /* refreshed data */ + unsigned long r_vctx; + unsigned long r_ictx; + //ulong_t r_syscalls; + unsigned long long r_bread; + //ulong_t r_gbread; + unsigned long long r_bwrit; + //ulong_t r_gbwrit; + + float r_cpuburn; + double r_cputimestamp; + double r_cputime; + + unsigned long long r_bwtime; + //accum_t r_rwtime; + //accum_t r_qwtime; + + /* predicate values */ + derived_pred_t preds; + +} process_t; + +//process_t * lookup_curr_node(pid_t); + +#endif diff --git a/src/pmdas/linux_proc/indom.h b/src/pmdas/linux_proc/indom.h index 9c928cd..59bef7b 100644 --- a/src/pmdas/linux_proc/indom.h +++ b/src/pmdas/linux_proc/indom.h @@ -27,6 +27,7 @@ #define DEVT_INDOM 2 /* - disks (major:minor names) */ #define PROC_INDOM 9 /* - processes */ #define STRINGS_INDOM 10 /* - fake indom, string hash */ +#define HOTPROC_INDOM 11 /* - hot procs */ #define CGROUP_SUBSYS_INDOM 20 /* - control group subsystems */ #define CGROUP_MOUNTS_INDOM 21 /* - control group mounts */ diff --git a/src/pmdas/linux_proc/lex.l b/src/pmdas/linux_proc/lex.l new file mode 100644 index 0000000..7db2af2 --- /dev/null +++ b/src/pmdas/linux_proc/lex.l @@ -0,0 +1,114 @@ +/* + * Copyright (c) 1995 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 "./gram_node.h" +#include "./gram.tab.h" +#include "pmapi.h" + +#define _REGEX_RE_COMP +#include +#include + + +void yyerror(char *s); + +static char emsg[256]; /* error message */ + +%} + +%option noinput +%option nounput + +%% + +Version { return VERSION; } +schedwait { return SCHEDWAIT; } +iowait { return IOWAIT; } +iodemand { return IODEMAND; } +residentsize { return RESIDENTSIZE; } +virtualsize { return VIRTUALSIZE; } +ctxswitch { return CTXSWITCH; } +syscalls { return SYSCALLS; } +gid { return GID; } +uid { return UID; } +uname { return UNAME; } +gname { return GNAME; } +fname { return FNAME; } +psargs { return PSARGS; } +cpuburn { return CPUBURN; } +"&&" { return AND; } +"||" { return OR; } +"!" { return NOT; } +"(" { return LPAREN; } +")" { return RPAREN; } +true { return TRUE; } +false { return FALSE; } +"==" { return EQUAL; } +"!=" { return NEQUAL; } +"<" { return LTHAN; } +"<=" { return LEQUAL; } +">" { return GTHAN; } +">=" { return GEQUAL; } +"~" { return MATCH; } +"!~" { return NMATCH; } + +\/[^/\n]*[/\n] { + char *str; + yylval.y_str = (char *)malloc(yyleng-1); + if (yylval.y_str == 0) { + (void)sprintf(emsg, "malloc failed: %s", osstrerror()); + yyerror(emsg); + } + strncpy(yylval.y_str, &yytext[1], yyleng-2); + yylval.y_str[yyleng-2] = '\0'; + if ((str = re_comp(yylval.y_str)) != 0) { + yyerror(str); + } + return PATTERN; + } + +\"[^"\n]*["\n] { + yylval.y_str = (char *)malloc(yyleng-1); + if (yylval.y_str == 0) { + (void)sprintf(emsg, "malloc failed: %s", osstrerror()); + yyerror(emsg); + } + strncpy(yylval.y_str, &yytext[1], yyleng-2); + yylval.y_str[yyleng-2] = '\0'; + return STRING; + } + + +[0-9]+ | +[0-9]*"."[0-9]+ | +[0-9]+"."[0-9]* { + yylval.y_number = atof(yytext); + return NUMBER; + } + +\#.*\n { } + +[\t \r\n]+ { } + + +[a-zA-Z]+ { + yyerror("Illegal word"); + } + +. { + yyerror("Illegal character"); + } +%% + diff --git a/src/pmdas/linux_proc/pmda.c b/src/pmdas/linux_proc/pmda.c index 1efaabd..ab3710f 100644 --- a/src/pmdas/linux_proc/pmda.c +++ b/src/pmdas/linux_proc/pmda.c @@ -36,6 +36,7 @@ #include "../linux/convert.h" #include "clusters.h" #include "indom.h" +#include "hotproc.h" #include "getinfo.h" #include "proc_pid.h" @@ -46,6 +47,7 @@ /* globals */ static int _isDSO = 1; /* for local contexts */ static proc_pid_t proc_pid; +static proc_pid_t hotproc_pid; static struct utsname kernel_uname; static proc_runq_t proc_runq; static int all_access; /* =1 no access checks */ @@ -53,6 +55,9 @@ static int have_access; /* =1 recvd uid/gid */ static size_t _pm_system_pagesize; static unsigned int threads; /* control.all.threads */ static char * cgroups; /* control.all.cgroups */ +static int conf_gen = 1; /* hotproc config version */ + +extern struct timeval hotproc_update_interval; char *proc_statspath = ""; /* optional path prefix for all stats files */ @@ -63,6 +68,11 @@ char *proc_statspath = ""; /* optional path prefix for all stats files */ static pmdaIndom indomtab[NUM_INDOMS]; /* + * Real metric tab that will be used after we add in hotprocs + */ +static pmdaMetric *fullmetrictab; + +/* * all metrics supported in this PMDA - one table entry for each */ static pmdaMetric metrictab[] = { @@ -966,6 +976,64 @@ static pmdaMetric metrictab[] = { /* proc.control.perclient.cgroups */ { NULL, { PMDA_PMID(CLUSTER_CONTROL, 3), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* + * hotproc specific clusters + */ + + /* hotproc.control.refresh */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_GLOBAL,1), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)} }, + /* hotproc.control.config */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_GLOBAL,8), + PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)} }, + /* hotproc.control.config_gen */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_GLOBAL,9), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)} }, + /* hotproc.total.cpuidle */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_GLOBAL,2), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)} }, + /* hotproc.total.cpuburn */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_GLOBAL,3), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)} }, + /* hotproc.total.cpuother.transient */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_GLOBAL,4), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)} }, + /* hotproc.total.cpuother.not_cpuburn */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_GLOBAL,5), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)} }, + /* hotproc.total.cpuother.total */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_GLOBAL,6), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)} }, + /* hotproc.total.cpuother.percent */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_GLOBAL,7), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)} }, + + /* hotproc.predicate.syscalls */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_PRED,0), + PM_TYPE_FLOAT, HOTPROC_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,-1,1, 0,PM_TIME_SEC,0)} }, + /* hotproc.predicate.ctxswitch */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_PRED,1), + PM_TYPE_FLOAT, HOTPROC_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,-1,1, 0,PM_TIME_SEC,0)} }, + /* hotproc.predicate.virtualsize */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_PRED,2), + PM_TYPE_U32, HOTPROC_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0, PM_SPACE_KBYTE,0,0)} }, + /* hotproc.predicate.residentsize */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_PRED,3), + PM_TYPE_U32, HOTPROC_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0, PM_SPACE_KBYTE,0,0)} }, + /* hotproc.predicate.iodemand */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_PRED,4), + PM_TYPE_FLOAT, HOTPROC_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(1,-1,0, PM_SPACE_KBYTE,PM_TIME_SEC,0)} }, + /* hotproc.predicate.iowait */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_PRED,5), + PM_TYPE_FLOAT, HOTPROC_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0, 0,0,0)} }, + /* hotproc.predicate.schedwait */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_PRED,6), + PM_TYPE_FLOAT, HOTPROC_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0, 0,0,0)} }, + /* hotproc.predicate.cpuburn */ + { NULL, {PMDA_PMID(CLUSTER_HOTPROC_PRED,7), + PM_TYPE_FLOAT, HOTPROC_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0, 0,0,0)} }, + }; pmInDom @@ -1017,6 +1085,20 @@ proc_refresh(pmdaExt *pmda, int *need_refresh) proc_ctx_threads(pmda->e_context, threads), proc_ctx_cgroups(pmda->e_context, cgroups)); } + if (need_refresh[CLUSTER_HOTPROC_PID_STAT] || + need_refresh[CLUSTER_HOTPROC_PID_STATM] || + need_refresh[CLUSTER_HOTPROC_PID_STATUS] || + need_refresh[CLUSTER_HOTPROC_PID_IO] || + need_refresh[CLUSTER_HOTPROC_PID_LABEL] || + need_refresh[CLUSTER_HOTPROC_PID_CGROUP] || + need_refresh[CLUSTER_HOTPROC_PID_SCHEDSTAT] || + need_refresh[CLUSTER_HOTPROC_PID_FD] || + need_refresh[CLUSTER_HOTPROC_GLOBAL] || + need_refresh[CLUSTER_HOTPROC_PRED]){ + refresh_hotproc_pid(&hotproc_pid, + proc_ctx_threads(pmda->e_context, threads), + proc_ctx_cgroups(pmda->e_context, cgroups)); + } if (need_refresh[CLUSTER_PROC_RUNQ]) refresh_proc_runq(&proc_runq); @@ -1051,6 +1133,19 @@ proc_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaEx need_refresh[CLUSTER_PID_IO]++; need_refresh[CLUSTER_PID_FD]++; break; + case HOTPROC_INDOM: + need_refresh[CLUSTER_HOTPROC_PID_STAT]++; + need_refresh[CLUSTER_HOTPROC_PID_STATM]++; + need_refresh[CLUSTER_HOTPROC_PID_STATUS]++; + need_refresh[CLUSTER_HOTPROC_PID_LABEL]++; + need_refresh[CLUSTER_HOTPROC_PID_CGROUP]++; + need_refresh[CLUSTER_HOTPROC_PID_SCHEDSTAT]++; + need_refresh[CLUSTER_HOTPROC_PID_IO]++; + need_refresh[CLUSTER_HOTPROC_PID_FD]++; + need_refresh[CLUSTER_HOTPROC_GLOBAL]++; + need_refresh[CLUSTER_HOTPROC_PRED]++; + break; + case CGROUP_SUBSYS_INDOM: need_refresh[CLUSTER_CGROUP_SUBSYS]++; break; @@ -1060,7 +1155,7 @@ proc_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaEx /* no default label : pmdaInstance will pick up errors */ } - if (indomp->serial == PROC_INDOM && inst == PM_IN_NULL && name != NULL) { + if ( (indomp->serial == PROC_INDOM || indomp->serial == HOTPROC_INDOM ) && inst == PM_IN_NULL && name != NULL) { /* * For the proc indom, if the name is a pid (as a string), and it * contains only digits (i.e. it's not a full instance name) then @@ -1085,7 +1180,7 @@ proc_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaEx sts = PM_ERR_PERMISSION; have_access = proc_ctx_access(pmda->e_context) || all_access; - if (have_access || indomp->serial != PROC_INDOM) { + if (have_access || ( (indomp->serial != PROC_INDOM) && (indomp->serial != HOTPROC_INDOM) )) { proc_refresh(pmda, need_refresh); sts = pmdaInstance(indom, inst, name, result, pmda); } @@ -1112,6 +1207,14 @@ proc_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) void *fsp; static long hz = -1; char *tail; + proc_pid_t *active_proc_pid; + + int have_totals; + double ta, ti, tt, tci; + + process_t *hotnode; + + active_proc_pid = &proc_pid; if (hz == -1) hz = sysconf(_SC_CLK_TCK); @@ -1154,15 +1257,128 @@ proc_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) else switch (cluster) { + case CLUSTER_HOTPROC_GLOBAL: + + + have_totals = get_hot_totals(&ta, &ti, &tt, &tci); + + switch(idp->item) { + + case 1: /* refresh */ + atom->ul = hotproc_update_interval.tv_sec; + break; + case 8: /* config */ + atom->cp = get_conf_buffer(); + break; + case 9: /* config_gen */ + atom->ul = conf_gen; + break; + case 2: /* cpuidle */ + if (!have_totals) + atom->f = 0; + else + atom->f = tci; + break; + case 3: /* cpuburn */ + if (!have_totals) + atom->f = 0; + else + atom->f = ta; + break; + case 4: /* other transient */ + if (!have_totals) + atom->f = 0; + else + atom->f = tt; + break; + case 5: /* other not_cpuburn */ + if (!have_totals) + atom->f = 0; + else + atom->f = ti; + break; + case 6: /* other total */ + if (!have_totals) + atom->f = 0; + else + atom->f = ti + tt; + break; + case 7: /* other percent */ + { + double other = tt + ti; + double non_idle = other + ta; + + /* if non_idle = 0, very unlikely, + * then the value here is meaningless + */ + + /* Also if all the numbers are very small + * this is not accurate + */ + + if (!have_totals || non_idle == 0) + atom->f = 0; + else + atom->f = other / non_idle * 100; + } + break; + + default: + return PM_ERR_PMID; + break; + } + break; + case CLUSTER_HOTPROC_PRED: + sts = get_hotproc_node( inst, &hotnode ); + + if( sts == 0 ){ + return PM_ERR_INST; + } + + switch(idp->item) { + + case 0: /* syscalls */ + return PM_ERR_PMID; + break; + case 1: /* ctxswitch */ + atom->f = hotnode->preds.ctxswitch; + break; + case 2: /* virtualsize */ + atom->ul = hotnode->preds.virtualsize; + break; + case 3: /* residentsize */ + atom->ul = hotnode->preds.residentsize; + break; + case 4: /* iodemand */ + atom->f = hotnode->preds.iodemand; + break; + case 5: /* iowait */ + atom->f = hotnode->preds.iowait; + break; + case 6: /* schedwait */ + return PM_ERR_PMID; + break; + case 7: /* cpuburn. not in orig hotproc */ + atom->f = hotnode->r_cpuburn; + break; + + default: + return PM_ERR_PMID; + break; + } + break; + + case CLUSTER_HOTPROC_PID_STAT: + active_proc_pid = &hotproc_pid; case CLUSTER_PID_STAT: if (idp->item == 99) /* proc.nprocs */ - atom->ul = proc_pid.indom->it_numinst; + atom->ul = active_proc_pid->indom->it_numinst; else { static char ttyname[MAXPATHLEN]; if (!have_access) return PM_ERR_PERMISSION; - if ((entry = fetch_proc_pid_stat(inst, &proc_pid, &sts)) == NULL) + if ((entry = fetch_proc_pid_stat(inst, active_proc_pid, &sts)) == NULL) return sts; switch (idp->item) { @@ -1321,15 +1537,17 @@ proc_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) } break; + case CLUSTER_HOTPROC_PID_STATM: + active_proc_pid = &hotproc_pid; case CLUSTER_PID_STATM: if (!have_access) return PM_ERR_PERMISSION; if (idp->item == PROC_PID_STATM_MAPS) { /* proc.memory.maps */ - if ((entry = fetch_proc_pid_maps(inst, &proc_pid, &sts)) == NULL) + if ((entry = fetch_proc_pid_maps(inst, active_proc_pid, &sts)) == NULL) return sts; atom->cp = entry->maps_buf; } else { - if ((entry = fetch_proc_pid_statm(inst, &proc_pid, &sts)) == NULL) + if ((entry = fetch_proc_pid_statm(inst, active_proc_pid, &sts)) == NULL) return sts; if (idp->item <= PROC_PID_STATM_DIRTY) { @@ -1344,10 +1562,12 @@ proc_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) } break; + case CLUSTER_HOTPROC_PID_SCHEDSTAT: + active_proc_pid = &hotproc_pid; case CLUSTER_PID_SCHEDSTAT: if (!have_access) return PM_ERR_PERMISSION; - if ((entry = fetch_proc_pid_schedstat(inst, &proc_pid, &sts)) == NULL) + if ((entry = fetch_proc_pid_schedstat(inst, active_proc_pid, &sts)) == NULL) return sts; if (idp->item < NR_PROC_PID_SCHED) { @@ -1367,10 +1587,12 @@ proc_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) return PM_ERR_PMID; break; + case CLUSTER_HOTPROC_PID_IO: + active_proc_pid = &hotproc_pid; case CLUSTER_PID_IO: if (!have_access) return PM_ERR_PERMISSION; - if ((entry = fetch_proc_pid_io(inst, &proc_pid, &sts)) == NULL) + if ((entry = fetch_proc_pid_io(inst, active_proc_pid, &sts)) == NULL) return sts; switch (idp->item) { @@ -1426,10 +1648,12 @@ proc_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) /* * Cluster added by Mike Mason */ + case CLUSTER_HOTPROC_PID_STATUS: + active_proc_pid = &hotproc_pid; case CLUSTER_PID_STATUS: if (!have_access) return PM_ERR_PERMISSION; - if ((entry = fetch_proc_pid_status(inst, &proc_pid, &sts)) == NULL) + if ((entry = fetch_proc_pid_status(inst, active_proc_pid, &sts)) == NULL) return sts; switch (idp->item) { @@ -1625,32 +1849,38 @@ proc_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) case CLUSTER_BLKIO_GROUPS: return cgroup_group_fetch(mdesc->m_desc.pmid, inst, atom); + case CLUSTER_HOTPROC_PID_FD: + active_proc_pid = &hotproc_pid; case CLUSTER_PID_FD: if (!have_access) return PM_ERR_PERMISSION; if (idp->item > PROC_PID_FD_COUNT) return PM_ERR_PMID; - if ((entry = fetch_proc_pid_fd(inst, &proc_pid, &sts)) == NULL) + if ((entry = fetch_proc_pid_fd(inst, active_proc_pid, &sts)) == NULL) return sts; atom->ul = entry->fd_count; break; + case CLUSTER_HOTPROC_PID_CGROUP: + active_proc_pid = &hotproc_pid; case CLUSTER_PID_CGROUP: if (!have_access) return PM_ERR_PERMISSION; if (idp->item > PROC_PID_CGROUP) return PM_ERR_PMID; - if ((entry = fetch_proc_pid_cgroup(inst, &proc_pid, &sts)) == NULL) + if ((entry = fetch_proc_pid_cgroup(inst, active_proc_pid, &sts)) == NULL) return sts; atom->cp = proc_strings_lookup(entry->cgroup_id); break; + case CLUSTER_HOTPROC_PID_LABEL: + active_proc_pid = &hotproc_pid; case CLUSTER_PID_LABEL: if (!have_access) return PM_ERR_PERMISSION; if (idp->item > PROC_PID_LABEL) return PM_ERR_PMID; - if ((entry = fetch_proc_pid_label(inst, &proc_pid, &sts)) == NULL) + if ((entry = fetch_proc_pid_label(inst, active_proc_pid, &sts)) == NULL) return sts; atom->cp = proc_strings_lookup(entry->label_id); break; @@ -1702,43 +1932,85 @@ proc_store(pmResult *result, pmdaExt *pmda) int i, sts = 0; have_access = proc_ctx_access(pmda->e_context) || all_access; - + for (i = 0; i < result->numpmid; i++) { pmValueSet *vsp = result->vset[i]; __pmID_int *idp = (__pmID_int *)&(vsp->pmid); pmAtomValue av; - if (idp->cluster != CLUSTER_CONTROL) - sts = PM_ERR_PERMISSION; - else if (vsp->numval != 1) - sts = PM_ERR_INST; - else switch (idp->item) { - case 1: /* proc.control.all.threads */ - if (!have_access) - sts = PM_ERR_PERMISSION; - else if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], + switch (idp->cluster){ + + case CLUSTER_CONTROL: + if (vsp->numval != 1) + sts = PM_ERR_INST; + else switch (idp->item) { + case 1: /* proc.control.all.threads */ + if (!have_access) + sts = PM_ERR_PERMISSION; + else if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], PM_TYPE_U32, &av, PM_TYPE_U32)) >= 0) { - if (av.ul > 1) /* only zero or one allowed */ - sts = PM_ERR_CONV; - else - threads = av.ul; - } - break; - case 2: /* proc.control.perclient.threads */ - if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], + if (av.ul > 1) /* only zero or one allowed */ + sts = PM_ERR_CONV; + else + threads = av.ul; + } + break; + case 2: /* proc.control.perclient.threads */ + if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], PM_TYPE_U32, &av, PM_TYPE_U32)) >= 0) { - sts = proc_ctx_set_threads(pmda->e_context, av.ul); - } - break; - case 3: /* proc.control.perclient.cgroups */ - if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], + sts = proc_ctx_set_threads(pmda->e_context, av.ul); + } + break; + case 3: /* proc.control.perclient.cgroups */ + if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], PM_TYPE_STRING, &av, PM_TYPE_STRING)) >= 0) { - if ((sts = proc_ctx_set_cgroups(pmda->e_context, av.cp)) < 0) - free(av.cp); - } - break; + if ((sts = proc_ctx_set_cgroups(pmda->e_context, av.cp)) < 0) + free(av.cp); + } + break; + default: + sts = PM_ERR_PERMISSION; + break; + } //else switch (idp->item) + case CLUSTER_HOTPROC_GLOBAL: + switch(idp->item){ + case 1: /* Update interval */ + if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], + PM_TYPE_U32, &av, PM_TYPE_U32)) >= 0) { + hotproc_update_interval.tv_sec = av.ul; + reset_hotproc_timer(); + } + break; + case 8: /* CONFIG */ + { + bool_node *tree = NULL; + char *savebuffer; + if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], + PM_TYPE_STRING, &av, PM_TYPE_STRING)) >= 0) { + savebuffer = strdup(get_conf_buffer()); + set_conf_buffer( av.cp ); + if(parse_config(&tree) !=0 ){ + set_conf_buffer( savebuffer ); + free(savebuffer); + } + else{ + conf_gen++; + new_tree(tree); + /* let things just catch up for now */ + //restart_refresh(); + } + free(av.cp); + } + } + break; + default: + sts = PM_ERR_PERMISSION; + break; + } + break; default: - sts = PM_ERR_PERMISSION; + sts = PM_ERR_PERMISSION; + break; } if (sts < 0) break; @@ -1824,14 +2096,120 @@ proc_strings_insert(const char *buf) * Initialise the agent (both daemon and DSO). */ +int isHotprocMetric( pmdaMetric *met ){ + + __pmID_int *mID = (__pmID_int *)&(met->m_desc.pmid); + int cluster = mID->cluster; + + switch( cluster ){ + case CLUSTER_PID_STAT: + case CLUSTER_PID_STATM: + case CLUSTER_PID_CGROUP: + case CLUSTER_PID_LABEL: + case CLUSTER_PID_STATUS: + case CLUSTER_PID_SCHEDSTAT: + case CLUSTER_PID_IO: + case CLUSTER_PID_FD: + return 1; + default: + return 0; + } + +} + +int getHotCluster( int cluster ){ + + switch(cluster){ + + case CLUSTER_PID_STAT: + return CLUSTER_HOTPROC_PID_STAT; + case CLUSTER_PID_STATM: + return CLUSTER_HOTPROC_PID_STATM; + case CLUSTER_PID_CGROUP: + return CLUSTER_HOTPROC_PID_CGROUP; + case CLUSTER_PID_LABEL: + return CLUSTER_HOTPROC_PID_LABEL; + case CLUSTER_PID_STATUS: + return CLUSTER_HOTPROC_PID_STATUS; + case CLUSTER_PID_SCHEDSTAT: + return CLUSTER_HOTPROC_PID_SCHEDSTAT; + case CLUSTER_PID_IO: + return CLUSTER_HOTPROC_PID_IO; + case CLUSTER_PID_FD: + return CLUSTER_HOTPROC_PID_FD; + default: + return -1; + } +} + +void createHotprocMetric( pmdaMetric *procmetric, pmdaMetric *hotmetric){ + + __pmID_int *mID = (__pmID_int *)&(procmetric->m_desc.pmid); + int cluster = mID->cluster; + int item = mID->item; + + hotmetric->m_user = NULL; + hotmetric->m_desc = procmetric->m_desc; + + int hcluster = getHotCluster(cluster); /* If the 2 fcns above are in sync, this should not error */ + + hotmetric->m_desc.pmid = PMDA_PMID( hcluster , item ); + if( procmetric->m_desc.indom == PM_INDOM_NULL ){ + hotmetric->m_desc.indom = PM_INDOM_NULL; + } + else { + hotmetric->m_desc.indom = HOTPROC_INDOM; + } +} + +int nmetrics = 0; + +void +proc_init_hotproc(){ + + nmetrics = sizeof(metrictab)/sizeof(metrictab[0]); + + fullmetrictab = (pmdaMetric *) malloc(nmetrics * 2 * sizeof(pmdaMetric)); + + /* memcopy, then add hotproc, then set nmetrics for use below, then update all to use fullmetrictab */ + + memcpy( fullmetrictab, metrictab, sizeof(metrictab) ); + + int numhotproc = 0; + int i; + + for( i=0; i < nmetrics; i++ ){ + + pmdaMetric * procmetric = &fullmetrictab[i]; + + if( isHotprocMetric( procmetric ) ){ + pmdaMetric * hotmetric = &fullmetrictab[nmetrics+numhotproc]; + createHotprocMetric( procmetric, hotmetric); + numhotproc++; + } + + } + + /* Could do 2 pasess and just alloc once */ + fullmetrictab = realloc( fullmetrictab, (nmetrics+numhotproc) * sizeof(pmdaMetric) ); + + nmetrics += numhotproc; + +} + void __PMDA_INIT_CALL proc_init(pmdaInterface *dp) { - int nindoms = sizeof(indomtab)/sizeof(indomtab[0]); - int nmetrics = sizeof(metrictab)/sizeof(metrictab[0]); + + FILE *conf; char *envpath; + int nindoms = sizeof(indomtab)/sizeof(indomtab[0]); + int sep = __pmPathSeparator(); + + proc_init_hotproc(); + _pm_system_pagesize = getpagesize(); if ((envpath = getenv("PROC_STATSPATH")) != NULL) proc_statspath = envpath; @@ -1871,6 +2249,21 @@ proc_init(pmdaInterface *dp) indomtab[CGROUP_MOUNTS_INDOM].it_indom = CGROUP_MOUNTS_INDOM; proc_pid.indom = &indomtab[PROC_INDOM]; + + indomtab[HOTPROC_INDOM].it_indom = HOTPROC_INDOM; + hotproc_pid.indom = &indomtab[HOTPROC_INDOM]; + + char h_configfile[MAXPATHLEN]; + + snprintf(h_configfile, sizeof(h_configfile), "%s%c" "proc" "%c" "hotproc.conf", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + + //Send this all to a hotproc init config function? TODO + conf = open_config(h_configfile); + read_config(conf); + (void)fclose(conf); + + init_hotproc_pid( &indomtab[HOTPROC_INDOM] ); /* * Read System.map and /proc/ksyms. Used to translate wait channel @@ -1879,11 +2272,11 @@ proc_init(pmdaInterface *dp) */ read_ksym_sources(kernel_uname.release); - cgroup_init(metrictab, nmetrics); + cgroup_init(fullmetrictab, nmetrics); proc_ctx_init(); pmdaSetFlags(dp, PMDA_EXT_FLAG_HASHED); - pmdaInit(dp, indomtab, nindoms, metrictab, nmetrics); + pmdaInit(dp, indomtab, nindoms, fullmetrictab, nmetrics); /* string metrics use the pmdaCache API for value indexing */ pmdaCacheOp(INDOM(STRINGS_INDOM), PMDA_CACHE_STRINGS); diff --git a/src/pmdas/linux_proc/proc_pid.c b/src/pmdas/linux_proc/proc_pid.c index c0bab14..3840a23 100644 --- a/src/pmdas/linux_proc/proc_pid.c +++ b/src/pmdas/linux_proc/proc_pid.c @@ -22,10 +22,78 @@ #include #include #include +#include +#include +#include #include "proc_pid.h" #include "indom.h" -static proc_pid_list_t pids; +#include "hotproc.h" + +static proc_pid_list_t procpids; /* previous pids list that the proc pmda uses */ + +/* Hotproc variables */ + +/* PIDS that we are keeping track of as POSSIBLE "hot" candidates + * need a seperate list since it is generated by the timer update. +*/ +static proc_pid_list_t hotpids; +static proc_pid_t hotproc_poss_pid; + +#define INIT_HOTPROC_MAX 200 + +/* Actual processes that are hot based on the current configuration + * Filled in hotproc_eval_procs + */ +static pid_t *hot_active_list = NULL; + +static int hot_numactive = 0; +static int hot_maxactive = INIT_HOTPROC_MAX; + +/* array size allocated */ +static int hot_maxprocs[2] = {INIT_HOTPROC_MAX, INIT_HOTPROC_MAX}; + +/* number of procs used in list (<= hot_maxprocs) */ +static int hot_numprocs[2] = {0, 0}; + +/* Current and Previous list of processes that we are considering for "hot" inclusion + * Updated by the timer callback + * keeps stats that we will use for determination + */ +static process_t *hotproc_list[2] = {NULL, NULL}; + +/* various cpu time totals */ +static int num_cpus = 0; +static int hot_have_totals = 0; +static double hot_total_transient; +static double hot_total_cpuidle; +static double hot_total_active; +static double hot_total_inactive; + +int get_hot_totals(double * ta, double * ti, double * tt, double * tci ){ + + if( hot_have_totals ){ + (*ta) = hot_total_active; + (*ti) = hot_total_inactive; + (*tt) = hot_total_transient; + (*tci) = hot_total_cpuidle; + return 1; + } + else{ + return 0; + } + +} + + +static unsigned long hot_refresh_count = 0; + +/* index into proc_list etc.. */ +static int current = 0; +static int previous = 1; + +struct timeval hotproc_update_interval; +int hotproc_timer_id = -1; static int compare_pid(const void *pa, const void *pb) @@ -36,27 +104,27 @@ compare_pid(const void *pa, const void *pb) } static void -pidlist_append_pid(int pid) +pidlist_append_pid(int pid, proc_pid_list_t *pids) { - if (pids.count >= pids.size) { - pids.size += 64; - if (!(pids.pids = (int *)realloc(pids.pids, pids.size * sizeof(int)))) { + if (pids->count >= pids->size) { + pids->size += 64; + if (!(pids->pids = (int *)realloc(pids->pids, pids->size * sizeof(int)))) { perror("pidlist_append: out of memory"); - pids.size = pids.count = 0; + pids->size = pids->count = 0; return; /* soldier on bravely */ } } - pids.pids[pids.count++] = pid; + pids->pids[pids->count++] = pid; } static void -pidlist_append(const char *pidname) +pidlist_append(const char *pidname, proc_pid_list_t *pids) { - pidlist_append_pid(atoi(pidname)); + pidlist_append_pid(atoi(pidname), pids); } static void -tasklist_append(const char *pid) +tasklist_append(const char *pid, proc_pid_list_t *pids) { DIR *taskdirp; struct dirent *tdp; @@ -67,7 +135,7 @@ tasklist_append(const char *pid) while ((tdp = readdir(taskdirp)) != NULL) { if (!isdigit((int)tdp->d_name[0]) || strcmp(pid, tdp->d_name) == 0) continue; - pidlist_append(tdp->d_name); + pidlist_append(tdp->d_name, pids); } closedir(taskdirp); } @@ -82,7 +150,7 @@ tasklist_append(const char *pid) } static int -refresh_cgroup_pidlist(int want_threads, const char *cgroup) +refresh_cgroup_pidlist(int want_threads, const char *cgroup, proc_pid_list_t *pids) { char path[MAXPATHLEN]; FILE *fp; @@ -103,7 +171,7 @@ refresh_cgroup_pidlist(int want_threads, const char *cgroup) if ((fp = fopen(path, "r")) != NULL) { while (fscanf(fp, "%d\n", &pid) == 1) - pidlist_append_pid(pid); + pidlist_append_pid(pid, pids); fclose(fp); } #if PCP_DEBUG @@ -118,7 +186,7 @@ refresh_cgroup_pidlist(int want_threads, const char *cgroup) } static int -refresh_global_pidlist(int want_threads) +refresh_global_pidlist(int want_threads, proc_pid_list_t *pids) { DIR *dirp; struct dirent *dp; @@ -138,19 +206,618 @@ refresh_global_pidlist(int want_threads) /* note: readdir on /proc ignores threads */ while ((dp = readdir(dirp)) != NULL) { if (isdigit((int)dp->d_name[0])) { - pidlist_append(dp->d_name); + pidlist_append(dp->d_name, pids); if (want_threads) - tasklist_append(dp->d_name); + tasklist_append(dp->d_name, pids); + } + } + closedir(dirp); + + qsort(pids->pids, pids->count, sizeof(int), compare_pid); + return 0; +} + +static int +in_hot_active_list(pid_t pid) +{ + int i; + + for(i = 0; i < hot_numactive; i++) { + if (pid == hot_active_list[i]) + return 1; + } + + return 0; +} + +static int +check_if_hot( char *cpid ){ + + int mypid; + int sts = 0; + + sts = sscanf( cpid, "%d", &mypid ); + + if( sts == 0 ){ + return 0; + } + + if( in_hot_active_list(mypid) ){ + return 1; + } + else{ + return 0; + } +} + +static int +refresh_hotproc_pidlist( proc_pid_list_t *pids ) +{ + DIR *dirp; + struct dirent *dp; + + if ((dirp = opendir("/proc")) == NULL) + return -oserror(); + + /* note: readdir on /proc ignores threads */ + while ((dp = readdir(dirp)) != NULL) { + if (isdigit((int)dp->d_name[0])) { + if( check_if_hot( dp->d_name ) ){ + pidlist_append(dp->d_name, pids); + } } } closedir(dirp); - qsort(pids.pids, pids.count, sizeof(int), compare_pid); + qsort(pids->pids, pids->count, sizeof(int), compare_pid); return 0; } +static int +init_hotproc_list(void) +{ + hot_active_list = (pid_t*)malloc(INIT_HOTPROC_MAX * sizeof(pid_t)); + hotproc_list[0] = (process_t*)malloc(INIT_HOTPROC_MAX * sizeof(process_t)); + hotproc_list[1] = (process_t*)malloc(INIT_HOTPROC_MAX * sizeof(process_t)); + if (hotproc_list[0] == NULL || hotproc_list[1] == NULL || hot_active_list == NULL) + return -oserror(); + return 0; +} + + static void -refresh_proc_pidlist(proc_pid_t *proc_pid) +init_hot_active_list(void) +{ + hot_numactive = 0; +} + +/* + * * add_hot_active_list: + * * If unsuccessful in add - due to memory then return neg status. + * * If member of active list return 1 + * * If non-member of active list return 0 + * */ + +static int +add_hot_active_list(process_t *node, config_vars *vars) +{ + if (eval_tree(vars) == 0) { + return 0; + } + else{ + //fprintf(stderr, "Added hotproc %d\n", node->pid); + } + + if (hot_numactive == hot_maxactive) { + pid_t *res; + hot_maxactive = hot_numactive*2; + res = (pid_t *)realloc(hot_active_list, hot_maxactive * sizeof(pid_t)); + if (res == NULL) + return -1; + hot_active_list = res; + } + hot_active_list[hot_numactive++] = node->pid; + return 1; +} + +static int +compar_pids(const void *n1, const void *n2) +{ + return ((process_t*)n2)->pid - ((process_t*)n1)->pid; +} + + +static process_t * +lookup_node(int curr_prev, pid_t pid) +{ + process_t key; + process_t *node; + + key.pid = pid; + + if ( (hot_numprocs[curr_prev] > 0) && + ((node = bsearch(&key, hotproc_list[curr_prev], hot_numprocs[curr_prev], + sizeof(process_t), compar_pids)) != NULL) ) { + return node; + } + return NULL; +} + +process_t * +lookup_curr_node(pid_t pid) +{ + return lookup_node(current, pid); +} + + +static double +DiffCounter(double current, double previous, int pmtype) +{ + double outval = current-previous; + + if (outval < 0.0) { + switch (pmtype) { + case PM_TYPE_32: + case PM_TYPE_U32: + outval += (double)UINT_MAX+1; + break; + case PM_TYPE_64: + case PM_TYPE_U64: + outval += (double)ULONGLONG_MAX+1; + break; + } + } + + return outval; +} + + + + +int +get_hotproc_node( pid_t pid, process_t **getnode ){ + + if( in_hot_active_list(pid) ){ + (*getnode) = lookup_curr_node( pid ); + if( (*getnode) == NULL ){ + return 0; + } + else{ + return 1; + } + } + else{ + (*getnode) = NULL; + return 0; + } + +} + +/* The idea of this is copied from linux/proc_stat.c */ +static unsigned long long +get_idle_time(){ + + FILE * fp = NULL; /* kept open until exit() */ + char *linux_statspath = ""; + + char fmt[64]; + unsigned long long idle_time; + int n; + char *envpath; + char buf[MAXPATHLEN]; + + if ((envpath = getenv("LINUX_STATSPATH")) != NULL) + linux_statspath = envpath; + snprintf(buf, sizeof(buf), "%s/proc/stat", linux_statspath); + if ((fp = fopen(buf, "r")) < 0) + return -oserror(); + + strcpy(fmt, "cpu %*llu %*llu %*llu %llu %*llu %*llu %*llu %*llu %*llu"); + n = fscanf( fp, fmt, &idle_time); + + if( n != 1){ + idle_time = 0; + } + + fprintf(stderr, "Idle time: %llu\n", idle_time); + + fclose( fp ); + + return idle_time; +} + +static void +refresh_proc_pidlist(proc_pid_t *proc_pid, proc_pid_list_t *pids); + +static int +hotproc_eval_procs(){ + + /* for each pid, compute stats and store in hotpid array */ + + /* Called by the timer */ + + pid_t pid; + struct timeval ts; + int sts; + char *f; + unsigned long ul; + unsigned long long ull; + char *tail; + process_t *oldnode = NULL; + process_t *newnode = NULL; + int np = 0; + struct timeval p_timestamp; + config_vars vars; + proc_pid_entry_t *statentry; + proc_pid_entry_t *statusentry; + proc_pid_entry_t *ioentry; + __pmHashNode *node; + int i; + + /* Still need to compute some of these */ + static double refresh_time[2]; /* timestamp after refresh */ + static time_t sysidle[2]; /* sys idle from /proc/stat */ + double sysidle_delta; /* system idle delta time since last refresh */ + double actual_delta; /* actual delta time since last refresh */ + double transient_delta; /* calculated delta time of transient procs */ + double cputime_delta; /* delta cpu time for a process */ + //double syscalls_delta; /* delta num of syscalls for a process */ + double vctx_delta; /* delta num of valid ctx switches for a process */ + double ictx_delta; /* delta num of invalid ctx switches for a process */ + double bread_delta; /* delta num of bytes read */ + //double gbread_delta; /* delta num of gigabytes read */ + double bwrit_delta; /* delta num of bytes written */ + //double gbwrit_delta; /* delta num of gigabytes written */ + double bwtime_delta; /* delta num of nanosesc for waiting for blocked io */ + //double rwtime_delta; /* delta num of nanosesc for waiting for raw io */ + //double qwtime_delta; /* delta num of nanosesc waiting on run queue */ + double timestamp_delta; /* real time delta b/w refreshes for process */ + double total_cputime = 0; /* total of cputime_deltas for each process */ + double total_activetime = 0; /* total of cputime_deltas for active processes */ + double total_inactivetime = 0; /* total of cputime_deltas for inactive processes */ + + + static long hz = -1; + + if (hz == -1){ + hz = sysconf(_SC_CLK_TCK); + num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + } + + if (current == 0) { + current = 1; previous = 0; + } + else { + current = 0; previous = 1; + } + + init_hot_active_list(); + + (void)memset(&vars, 0, sizeof(config_vars)); + + hotpids.count = 0; + hotpids.threads = 0; + + /* Whats running right now */ + refresh_global_pidlist( 0, &hotpids ); + refresh_proc_pidlist( &hotproc_poss_pid, &hotpids ); + + for (i=0; i < hotpids.count; i++) { + + pid = hotpids.pids[i]; + + // Must (should?) be there due to 2 refresh calls above + node = __pmHashSearch(hotpids.pids[i], &hotproc_poss_pid.pidhash); + + if( node == NULL){ + fprintf(stderr,"hotproc : Hash search failed for Proc %d!\n", i); + continue; + } + + __pmtimevalNow(&p_timestamp); + + /* Collect all the stat/status/statm info */ + + /* (Ab)use existing functions */ + + statentry = fetch_proc_pid_stat(pid, &hotproc_poss_pid, &sts); + statusentry = fetch_proc_pid_status(pid, &hotproc_poss_pid, &sts); + ioentry = fetch_proc_pid_io(pid, &hotproc_poss_pid, &sts); + + if( statentry == NULL || statusentry == NULL || ioentry == NULL){ + /* Can happen if the process was exiting during refresh_proc_pidlist + * then the above fetch's will fail + * Would be best if they were not in the list at all + * "np" is used from now on, so hopefully can just continue + */ + continue; + } + + if (np == hot_maxprocs[current]) { + process_t *res; + hot_maxprocs[current] = np*2; + res = (process_t *)realloc(hotproc_list[current], + hot_maxprocs[current] * sizeof(process_t)); + if (res == NULL) + return -oserror(); + hotproc_list[current] = res; + } + + newnode = &hotproc_list[current][np++]; + newnode->pid = pid; + + /* Calc the stats we will need */ + + /* CPU Time is sum of U & S time */ + /* How do we deal with errors here??? */ + + if( (f = _pm_getfield(statentry->stat_buf, PROC_PID_STAT_UTIME)) == NULL ) + newnode->r_cputime = 0; + else{ + ul = (__uint32_t)strtoul(f, &tail, 0); + newnode->r_cputime = (double)ul / (double)hz; + } + + if( (f = _pm_getfield(statentry->stat_buf, PROC_PID_STAT_STIME)) == NULL){ + /* Nothing */ + } + else{ + ul = (__uint32_t)strtoul(f, &tail, 0); + newnode->r_cputime += (double)ul / (double)hz; + } + + newnode->r_cputimestamp = p_timestamp.tv_sec + p_timestamp.tv_usec / 1000000; + + /* Context Switches : vol and invol */ + + if ((f = _pm_getfield(statusentry->status_lines.vctxsw, 1)) == NULL) + newnode->r_vctx = 0; + else + newnode->r_vctx = (__uint32_t)strtoul(f, &tail, 0); + + if ((f = _pm_getfield(statusentry->status_lines.nvctxsw, 1)) == NULL) + newnode->r_ictx = 0; + else + newnode->r_ictx = (__uint32_t)strtoul(f, &tail, 0); + + /* IO demand */ + /* Read */ + + if ((f = _pm_getfield(ioentry->io_lines.readb, 1)) == NULL) + ull = 0; + else + ull = (__uint64_t)strtoull(f, &tail, 0); + + newnode->r_bread = ull; + + /* Write */ + + if ((f = _pm_getfield(ioentry->io_lines.writeb, 1)) == NULL) + ull = 0; + else + ull = (__uint64_t)strtoull(f, &tail, 0); + + newnode->r_bwrit = ull; + + /* Block IO wait (delayacct_blkio_ticks) */ + + if ((f = _pm_getfield(statentry->stat_buf, PROC_PID_STAT_DELAYACCT_BLKIO_TICKS - 3)) == NULL) /* Note the offset */ + ul = 0; + else + ul = (__uint32_t)strtoul(f, &tail, 0); + + newnode->r_bwtime = (double)ul / hz; + + /* This is not the first time through, so we can generate rate stats */ + if ((oldnode = lookup_node(previous, pid)) != NULL) { + + /* Just copied the old Diff code. Is there a new way to do this? */ + + /* CPU */ + cputime_delta = DiffCounter(newnode->r_cputime, oldnode->r_cputime, PM_TYPE_64); + timestamp_delta = DiffCounter(newnode->r_cputimestamp, oldnode->r_cputimestamp, PM_TYPE_64); + + newnode->r_cpuburn = cputime_delta / timestamp_delta; + vars.cpuburn = newnode->r_cpuburn; + + /* IO */ + bread_delta = DiffCounter((double)newnode->r_bread, + (double)oldnode->r_bread, PM_TYPE_64); + bwrit_delta = DiffCounter((double)newnode->r_bwrit, + (double)oldnode->r_bwrit, PM_TYPE_64); + vars.preds.iodemand = ( + (double)bread_delta + + (double)bwrit_delta ) / + timestamp_delta; + + /* ctx switches */ + vctx_delta = DiffCounter((double)newnode->r_vctx, + (double)oldnode->r_vctx, PM_TYPE_64); + ictx_delta = DiffCounter((double)newnode->r_ictx, + (double)oldnode->r_ictx, PM_TYPE_64); + vars.preds.ctxswitch = (vctx_delta + ictx_delta) / timestamp_delta; + + /* IO wait */ + bwtime_delta = DiffCounter((double)newnode->r_bwtime, + (double)oldnode->r_bwtime, PM_TYPE_64); + + vars.preds.iowait = bwtime_delta / timestamp_delta; + + + } + else { + newnode->r_cpuburn = 0; + bzero(&newnode->preds, sizeof(newnode->preds)); + vars.cpuburn = 0; + //vars.preds.syscalls = 0; + vars.preds.ctxswitch = 0; + vars.preds.iowait = 0; + //vars.preds.schedwait = 0; + vars.preds.iodemand = 0; + cputime_delta = 0; + } + + total_cputime += cputime_delta; + + /* Command */ + + if ((f = _pm_getfield(statentry->stat_buf, PROC_PID_STAT_CMD)) == NULL){ + strcpy(vars.fname, "Unknown"); + } + else{ + char *cmd = malloc( strlen(f+1) ); + strcpy(cmd, f+1); + cmd[strlen(f+1)-1] = '\0'; + strcpy(vars.fname, cmd); + free(cmd); + } + + /* PS Args */ + strcpy(vars.psargs, statentry->name+7); + + /* UID and GID */ + if ((f = _pm_getfield(statusentry->status_lines.uid, 1)) == NULL){ + ul = 0; + } + else{ + ul = (__uint32_t)strtoul(f, &tail, 0); + } + + vars.uid = ul; + + if ((f = _pm_getfield(statusentry->status_lines.gid, 1)) == NULL){ + ul = 0; + } + else{ + ul = (__uint32_t)strtoul(f, &tail, 0); + } + + vars.gid = ul; + + /* uname and gname */ + + struct passwd *pwe; + + if ((pwe = getpwuid((uid_t)vars.uid)) != NULL) + strcpy(vars.uname, pwe->pw_name); + else + strcpy(vars.uname, "UNKNOWN"); + + struct group *gre; + + if ((gre = getgrgid((gid_t)vars.gid)) != NULL) { + strcpy(vars.gname, gre->gr_name); + } else { + strcpy(vars.gname, "UNKNOWN"); + } + + /* VSIZE from stat */ + + if ((f = _pm_getfield(statentry->stat_buf, PROC_PID_STAT_VSIZE)) == NULL){ + ul = 0; + } + else{ + ul = (__uint32_t)strtoul(f, &tail, 0); + ul /= 1024; + } + + vars.preds.virtualsize = ul; + + /* RSS from stat */ + + if ((f = _pm_getfield(statentry->stat_buf, PROC_PID_STAT_RSS)) == NULL){ + ul = 0; + } + else{ + ul = (__uint32_t)strtoul(f, &tail, 0); + ul *= getpagesize() / 1024; + } + + vars.preds.residentsize = ul; + + // Struct copy. I think it was a bug before. Copy should be after rss and vm calcs + newnode->preds = vars.preds; + + if ((sts = add_hot_active_list(newnode, &vars)) < 0) { + return sts; + } + + if (sts == 0) + total_inactivetime += cputime_delta; + else + total_activetime += cputime_delta; + + } + + hot_numprocs[current] = np; + + __pmtimevalNow(&ts); + refresh_time[current] = ts.tv_sec + ts.tv_usec / 1000000; + + double hptime = (ts.tv_sec - p_timestamp.tv_sec) + (ts.tv_usec - p_timestamp.tv_usec)/1000000.0; + + //fprintf(stderr, "Hotproc Update took %f time\n", hptime); + + /* Idle */ + sysidle[current] = get_idle_time(); + + /* Handle rollover */ + hot_refresh_count++; + if (hot_refresh_count == 0) + hot_refresh_count = 2; + + if (hot_refresh_count > 1 ) { + sysidle_delta = DiffCounter(sysidle[current], sysidle[previous], PM_TYPE_64) / (double)HZ; + actual_delta = DiffCounter(refresh_time[current], refresh_time[previous], PM_TYPE_64); + transient_delta = num_cpus * actual_delta - (total_cputime + sysidle_delta); + if (transient_delta < 0) /* sanity check */ + transient_delta = 0; + + hot_have_totals = 1; + hot_total_transient = transient_delta / actual_delta; + hot_total_cpuidle = sysidle_delta / actual_delta; + hot_total_active = total_activetime / actual_delta; + hot_total_inactive = total_inactivetime / actual_delta; + } + + + qsort(hotproc_list[current], hot_numprocs[current], + sizeof(process_t), compar_pids); + + return 0; +} + +void +hotproc_timer(int sig, void *ptr) +{ + + hotproc_eval_procs(); +} + +void +init_hotproc_pid( pmdaIndom *indomtab ){ + + hotproc_poss_pid.indom = indomtab; + + hotproc_update_interval.tv_sec = 10; + + init_hotproc_list(); + + if ((hotproc_timer_id = __pmAFregister(&hotproc_update_interval, NULL, hotproc_timer)) < 0) { + __pmNotifyErr(LOG_ERR, "error registering hotproc timer"); + exit(1); + } +} + +void reset_hotproc_timer(){ + + __pmAFunregister(hotproc_timer_id); + hotproc_timer_id = __pmAFregister(&hotproc_update_interval, NULL, hotproc_timer); + +} + +static void +refresh_proc_pidlist(proc_pid_t *proc_pid, proc_pid_list_t *pids) { int i; int fd; @@ -160,10 +827,10 @@ refresh_proc_pidlist(proc_pid_t *proc_pid) proc_pid_entry_t *ep; pmdaIndom *indomp = proc_pid->indom; - if (indomp->it_numinst < pids.count) + if (indomp->it_numinst < pids->count) indomp->it_set = (pmdaInstid *)realloc(indomp->it_set, - pids.count * sizeof(pmdaInstid)); - indomp->it_numinst = pids.count; + pids->count * sizeof(pmdaInstid)); + indomp->it_numinst = pids->count; /* * invalidate all entries so we can harvest pids that have exited @@ -179,19 +846,19 @@ refresh_proc_pidlist(proc_pid_t *proc_pid) * walk pid list and add new pids to the hash table, * marking entries valid as we go ... */ - for (i=0; i < pids.count; i++) { - node = __pmHashSearch(pids.pids[i], &proc_pid->pidhash); + for (i=0; i < pids->count; i++) { + node = __pmHashSearch(pids->pids[i], &proc_pid->pidhash); if (node == NULL) { int k = 0; ep = (proc_pid_entry_t *)malloc(sizeof(proc_pid_entry_t)); memset(ep, 0, sizeof(proc_pid_entry_t)); - ep->id = pids.pids[i]; + ep->id = pids->pids[i]; - snprintf(buf, sizeof(buf), "%s/proc/%d/cmdline", proc_statspath, pids.pids[i]); + snprintf(buf, sizeof(buf), "%s/proc/%d/cmdline", proc_statspath, pids->pids[i]); if ((fd = open(buf, O_RDONLY)) >= 0) { - sprintf(buf, "%06d ", pids.pids[i]); + sprintf(buf, "%06d ", pids->pids[i]); if ((k = read(fd, buf+7, sizeof(buf)-8)) > 0) { p = buf + k +7; *p-- = '\0'; @@ -224,7 +891,7 @@ refresh_proc_pidlist(proc_pid_t *proc_pid) * returns an empty string so we have to get it * from /proc//status or /proc//stat */ - sprintf(buf, "%s/proc/%d/status", proc_statspath, pids.pids[i]); + sprintf(buf, "%s/proc/%d/status", proc_statspath, pids->pids[i]); if ((fd = open(buf, O_RDONLY)) >= 0) { /* We engage in a bit of a hanky-panky here: * the string should look like "123456 (name)", @@ -246,7 +913,7 @@ refresh_proc_pidlist(proc_pid_t *proc_pid) p = buf+k; p[0] = ')'; p[1] = '\0'; - bc = sprintf(buf, "%06d ", pids.pids[i]); + bc = sprintf(buf, "%06d ", pids->pids[i]); buf[bc] = '('; } close(fd); @@ -263,13 +930,13 @@ refresh_proc_pidlist(proc_pid_t *proc_pid) if (k <= 0) { /* hmm .. must be exiting */ - sprintf(buf, "%06d ", pids.pids[i]); + sprintf(buf, "%06d ", pids->pids[i]); } ep->name = strdup(buf); - __pmHashAdd(pids.pids[i], (void *)ep, &proc_pid->pidhash); - // fprintf(stderr, "## ADDED \"%s\" to hash table\n", buf); + __pmHashAdd(pids->pids[i], (void *)ep, &proc_pid->pidhash); + fprintf(stderr, "key %d : ADDED \"%s\" to hash table\n", pids->pids[i], buf); } else ep = (proc_pid_entry_t *)node->data; @@ -292,7 +959,7 @@ refresh_proc_pidlist(proc_pid_t *proc_pid) // fprintf(stderr, "CHECKING key=%d node=" PRINTF_P_PFX "%p prev=" PRINTF_P_PFX "%p next=" PRINTF_P_PFX "%p ep=" PRINTF_P_PFX "%p valid=%d\n", // ep->id, node, prev, node->next, ep, ep->valid); if (!(ep->flags & PROC_PID_FLAG_VALID)) { - // fprintf(stderr, "DELETED key=%d name=\"%s\"\n", ep->id, ep->name); + fprintf(stderr, "DELETED key=%d name=\"%s\"\n", ep->id, ep->name); if (ep->name != NULL) free(ep->name); if (ep->stat_buf != NULL) @@ -331,12 +998,12 @@ refresh_proc_pid(proc_pid_t *proc_pid, int threads, const char *cgroups) { int sts; - pids.count = 0; - pids.threads = threads; + procpids.count = 0; + procpids.threads = threads; sts = (cgroups && cgroups[0] != '\0') ? - refresh_cgroup_pidlist(threads, cgroups) : - refresh_global_pidlist(threads); + refresh_cgroup_pidlist(threads, cgroups, &procpids) : + refresh_global_pidlist(threads, &procpids); if (sts < 0) return sts; @@ -347,10 +1014,29 @@ refresh_proc_pid(proc_pid_t *proc_pid, int threads, const char *cgroups) sts, threads, cgroups ? cgroups : ""); #endif - refresh_proc_pidlist(proc_pid); + refresh_proc_pidlist(proc_pid, &procpids); return 0; } +int +refresh_hotproc_pid(proc_pid_t *proc_pid, int threads, const char *cgroups) +{ + + int sts; + + hotpids.count = 0; + hotpids.threads = threads; + + sts = refresh_hotproc_pidlist(&hotpids); + + if (sts < 0) + return sts; + + refresh_proc_pidlist(proc_pid, &hotpids); + return 0; +} + + /* * Open a proc file, taking into account that we may want thread info @@ -367,7 +1053,7 @@ proc_open(const char *base, proc_pid_entry_t *ep) int fd; char buf[128]; - if (pids.threads) { + if (procpids.threads) { sprintf(buf, "%s/proc/%d/task/%d/%s", proc_statspath, ep->id, ep->id, base); if ((fd = open(buf, O_RDONLY)) >= 0) { return fd; @@ -401,7 +1087,7 @@ proc_opendir(const char *base, proc_pid_entry_t *ep) DIR *dir; char buf[128]; - if (pids.threads) { + if (procpids.threads) { sprintf(buf, "%s/proc/%d/task/%d/%s", proc_statspath, ep->id, ep->id, base); if ((dir = opendir(buf)) != NULL) { return dir; @@ -454,6 +1140,12 @@ fetch_proc_pid_stat(int id, proc_pid_t *proc_pid, int *sts) int fd; int n; __pmHashNode *node = __pmHashSearch(id, &proc_pid->pidhash); + if( node == NULL ){ + fprintf(stderr, "Hash Search in fetch_proc_pid_stat failed\n"); + } + else{ + //fprintf(stderr, "Hash Search in fetch_proc_pid_stat success\n"); + } proc_pid_entry_t *ep; char buf[1024]; diff --git a/src/pmdas/linux_proc/proc_pid.h b/src/pmdas/linux_proc/proc_pid.h index 1dd3630..bc97701 100644 --- a/src/pmdas/linux_proc/proc_pid.h +++ b/src/pmdas/linux_proc/proc_pid.h @@ -18,6 +18,9 @@ #ifndef _PROC_PID_H #define _PROC_PID_H +#include "hotproc.h" + + /* * /proc//stat metrics */ @@ -265,6 +268,14 @@ typedef struct { /* refresh the proc indom, reset all "fetched" flags */ extern int refresh_proc_pid(proc_pid_t *, int, const char *); +extern int refresh_hotproc_pid(proc_pid_t *, int, const char *); + +extern int get_hot_totals(double * ta, double * ti, double * tt, double * tci ); + +extern int get_hotproc_node( pid_t pid, process_t **getnode ); + +extern void reset_hotproc_timer(); + /* fetch a proc//stat entry for pid */ extern proc_pid_entry_t *fetch_proc_pid_stat(int, proc_pid_t *, int *); @@ -295,4 +306,6 @@ extern proc_pid_entry_t *fetch_proc_pid_label(int, proc_pid_t *, int *); /* extract the ith space separated field from a buffer */ extern char *_pm_getfield(char *, int); +extern void init_hotproc_pid( pmdaIndom * ); + #endif /* _PROC_PID_H */ diff --git a/src/pmdas/linux_proc/root_proc b/src/pmdas/linux_proc/root_proc index f64edee..fb0c44d 100644 --- a/src/pmdas/linux_proc/root_proc +++ b/src/pmdas/linux_proc/root_proc @@ -13,6 +13,7 @@ root { cgroup proc + hotproc } cgroup { @@ -185,4 +186,158 @@ proc.control.perclient { cgroups PROC:10:3 } +hotproc { + nprocs PROC:52:99 + psinfo + memory + id + io + schedstat + fd + control + total + predicate +} + +hotproc.psinfo { + pid PROC:52:0 + cmd PROC:52:1 + sname PROC:52:2 + ppid PROC:52:3 + pgrp PROC:52:4 + session PROC:52:5 + tty PROC:52:6 + tty_pgrp PROC:52:7 + flags PROC:52:8 + minflt PROC:52:9 + cmin_flt PROC:52:10 + maj_flt PROC:52:11 + cmaj_flt PROC:52:12 + utime PROC:52:13 + stime PROC:52:14 + cutime PROC:52:15 + cstime PROC:52:16 + priority PROC:52:17 + nice PROC:52:18 + /* not valid in 2.2.1 PROC:52:19 */ + it_real_value PROC:52:20 + start_time PROC:52:21 + vsize PROC:52:22 + rss PROC:52:23 + rss_rlim PROC:52:24 + start_code PROC:52:25 + end_code PROC:52:26 + start_stack PROC:52:27 + esp PROC:52:28 + eip PROC:52:29 + signal PROC:52:30 + blocked PROC:52:31 + sigignore PROC:52:32 + sigcatch PROC:52:33 + wchan PROC:52:34 + nswap PROC:52:35 + cnswap PROC:52:36 + exit_signal PROC:52:37 + processor PROC:52:38 + ttyname PROC:52:39 + wchan_s PROC:52:40 + psargs PROC:52:41 + signal_s PROC:56:16 + blocked_s PROC:56:17 + sigignore_s PROC:56:18 + sigcatch_s PROC:56:19 + threads PROC:56:28 + cgroups PROC:54:0 + labels PROC:55:0 +} + +hotproc.id { + uid PROC:56:0 + euid PROC:56:1 + suid PROC:56:2 + fsuid PROC:56:3 + gid PROC:56:4 + egid PROC:56:5 + sgid PROC:56:6 + fsgid PROC:56:7 + uid_nm PROC:56:8 + euid_nm PROC:56:9 + suid_nm PROC:56:10 + fsuid_nm PROC:56:11 + gid_nm PROC:56:12 + egid_nm PROC:56:13 + sgid_nm PROC:56:14 + fsgid_nm PROC:56:15 +} + +hotproc.memory { + size PROC:53:0 + rss PROC:53:1 + share PROC:53:2 + textrss PROC:53:3 + librss PROC:53:4 + datrss PROC:53:5 + dirty PROC:53:6 + maps PROC:53:7 + vmsize PROC:56:20 + vmlock PROC:56:21 + vmrss PROC:56:22 + vmdata PROC:56:23 + vmstack PROC:56:24 + vmexe PROC:56:25 + vmlib PROC:56:26 + vmswap PROC:56:27 +} + +hotproc.io { + rchar PROC:58:0 + wchar PROC:58:1 + syscr PROC:58:2 + syscw PROC:58:3 + read_bytes PROC:58:4 + write_bytes PROC:58:5 + cancelled_write_bytes PROC:58:6 +} + +hotproc.schedstat { + cpu_time PROC:57:0 + run_delay PROC:57:1 + pcount PROC:57:2 +} + +hotproc.fd { + count PROC:59:0 +} + +hotproc.control { + refresh PROC:60:1 + config PROC:60:8 + config_gen PROC:60:9 +} + +hotproc.total { + cpuidle PROC:60:2 + cpuburn PROC:60:3 + cpuother +} + +hotproc.total.cpuother { + transient PROC:60:4 + not_cpuburn PROC:60:5 + total PROC:60:6 + percent PROC:60:7 +} + +hotproc.predicate { + syscalls PROC:61:0 + ctxswitch PROC:61:1 + virtualsize PROC:61:2 + residentsize PROC:61:3 + iodemand PROC:61:4 + iowait PROC:61:5 + schedwait PROC:61:6 + cpuburn PROC:61:7 +} + + #undef PROC