From: Sebastien GODARD Date: Sat, 23 Feb 2019 10:15:25 +0000 (+0100) Subject: sadf: PCP: Initial support for A_QUEUE activity X-Git-Tag: v12.1.4~64 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5518a0fe1e5957939dc7180dc3c6a3232ee46ad8;p=sysstat sadf: PCP: Initial support for A_QUEUE activity This is the initial (and experimental) support for creating PCP archives by sadf. I used the work made by Steve Kay (@stevekay) to create this patch: See issue #136. At the present time, only A_QUEUE activity is supported (i.e. the metrics displayed by "sar -q"). Signed-off-by: Sebastien GODARD --- diff --git a/activity.c b/activity.c index 5276762..d2a626a 100644 --- a/activity.c +++ b/activity.c @@ -31,6 +31,7 @@ #include "json_stats.h" #include "svg_stats.h" #include "raw_stats.h" +#include "pcp_stats.h" #endif /* @@ -453,6 +454,7 @@ struct activity queue_act = { .f_json_print = json_print_queue_stats, .f_svg_print = svg_print_queue_stats, .f_raw_print = raw_print_queue_stats, + .f_pcp_print = pcp_print_queue_stats, .f_count_new = NULL, .item_list = NULL, .desc = "Queue length and load average statistics", diff --git a/format.c b/format.c index cf609e4..94cfc0c 100644 --- a/format.c +++ b/format.c @@ -140,6 +140,19 @@ struct report_format raw_fmt = { .f_comment = print_raw_comment }; +/* + * PCP output. + */ +struct report_format pcp_fmt = { + .id = F_PCP_OUTPUT, + .options = FO_HEADER_ONLY, + .f_header = print_pcp_header, + .f_statistics = print_pcp_statistics, + .f_timestamp = NULL, + .f_restart = NULL, + .f_comment = NULL +}; + /* * Array of output formats. */ @@ -151,7 +164,8 @@ struct report_format *fmt[NR_FMT] = { &json_fmt, &conv_fmt, &svg_fmt, - &raw_fmt + &raw_fmt, + &pcp_fmt }; #endif diff --git a/pcp_stats.c b/pcp_stats.c new file mode 100644 index 0000000..ecb5ac3 --- /dev/null +++ b/pcp_stats.c @@ -0,0 +1,99 @@ +/* + * pcp_stats.c: Funtions used by sadf to create PCP archive files. + * (C) 2019 by Sebastien GODARD (sysstat orange.fr) + * + *************************************************************************** + * 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 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-1335 USA * + *************************************************************************** + */ + +#include "sa.h" +#include "pcp_stats.h" + +#ifdef USE_NLS +#include +#include +#define _(string) gettext(string) +#else +#define _(string) (string) +#endif + +extern unsigned int flags; + +#ifdef HAVE_PCP +#include +#include +#endif + +/* + *************************************************************************** + * Display PCP message error then exit. + * + * IN: + * @rc Error code. + *************************************************************************** + */ +void print_pcp_error(int rc) +{ +#ifdef HAVE_PCP + fprintf(stderr, "PCP: pmiWrite: %s\n", pmiErrStr(rc)); + exit(4); +#endif +} + +/* + *************************************************************************** + * Display queue and load statistics in PCP format + * + * IN: + * @a Activity structure with statistics. + * @curr Index in array for current sample statistics. + * @itv Interval of time in 1/100th of a second. + * @record_hdr Record header for current sample. + *************************************************************************** + */ +__print_funct_t pcp_print_queue_stats(struct activity *a, int curr, unsigned long long itv, + struct record_header *record_hdr) +{ +#ifdef HAVE_PCP + int rc; + char buf[64]; + struct stats_queue + *sqc = (struct stats_queue *) a->buf[curr]; + + snprintf(buf, sizeof(buf), "%llu", sqc->nr_running); + pmiPutValue("proc.runq.runnable", NULL, buf); + + snprintf(buf, sizeof(buf), "%llu", sqc->nr_threads); + pmiPutValue("proc.nprocs", NULL, buf); + + snprintf(buf, sizeof(buf), "%llu", sqc->procs_blocked); + pmiPutValue("proc.blocked", NULL, buf); + + snprintf(buf, sizeof(buf), "%f", (double) sqc->load_avg_1 / 100); + pmiPutValue("kernel.all.load", "1 min", buf); + + snprintf(buf, sizeof(buf), "%f", (double) sqc->load_avg_5 / 100); + pmiPutValue("kernel.all.load", "5 min", buf); + + snprintf(buf, sizeof(buf), "%f", (double) sqc->load_avg_15 / 100); + pmiPutValue("kernel.all.load", "15 min", buf); + + if ((rc = pmiWrite(record_hdr->ust_time, 0)) < 0) { + print_pcp_error(rc); + } +#endif /* HAVE_PCP */ +} + diff --git a/pcp_stats.h b/pcp_stats.h new file mode 100644 index 0000000..e5a051d --- /dev/null +++ b/pcp_stats.h @@ -0,0 +1,19 @@ +/* + * pcp_stats.h: Include file used to display system statistics in PCP format. + * (C) 2019 by Sebastien Godard (sysstat orange.fr) + */ + +#ifndef _PCP_STATS_H +#define _PCP_STATS_H + +/* + *************************************************************************** + * Prototypes for functions used to display system statistics in PCP format + *************************************************************************** + */ + +/* Functions used to display statistics in PCP format */ +__print_funct_t pcp_print_queue_stats + (struct activity *, int, unsigned long long, struct record_header *); + +#endif /* _PCP_STATS_H */ diff --git a/sa.h b/sa.h index 349e5fa..db34f7b 100644 --- a/sa.h +++ b/sa.h @@ -817,6 +817,11 @@ struct activity { * This function is used by sadf to display activity statistics in raw format. */ __print_funct_t (*f_raw_print) (struct activity *, char *, int); + /* + * This function is used by sadf to display activity statistics in PCP format. + */ + __print_funct_t (*f_pcp_print) (struct activity *, int, unsigned long long, + struct record_header *); /* * This function is used by sadf to count the number of new items in current * sample and add them to the linked list @item_list. @@ -1010,9 +1015,9 @@ struct report_format { struct activity * [], unsigned int [], struct file_activity *); /* * This function defines the statistics part of the report. - * Used only with textual (XML-like) reports. + * Used only with textual (XML-like) reports and PCP archives. */ - __printf_funct_t (*f_statistics) (int *, int); + __printf_funct_t (*f_statistics) (int *, int, struct activity * [], unsigned int []); /* * This function defines the timestamp part of the report. * Used only with textual (XML-like) reports. diff --git a/sadf.c b/sadf.c index b2db388..1308641 100644 --- a/sadf.c +++ b/sadf.c @@ -38,6 +38,11 @@ # define _(string) (string) #endif +#ifdef HAVE_PCP +#include +#include +#endif + #ifdef USE_SCCSID #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__ char *sccsid(void) { return (SCCSID); } @@ -763,6 +768,13 @@ int generic_write_stats(int curr, int use_tm_start, int use_tm_end, int reset, (*act[i]->f_raw_print)(act[i], pre, curr); } + else if (format == F_PCP_OUTPUT) { + /* PCP archive */ + if (*act[i]->f_pcp_print) { + (*act[i]->f_pcp_print)(act[i], curr, itv, &record_hdr[curr]); + } + } + else { /* Other output formats: db, ppc */ (*act[i]->f_render)(act[i], (format == F_DB_OUTPUT), pre, curr, itv); @@ -981,12 +993,13 @@ void display_curr_act_graphs(int ifd, int *curr, long *cnt, int *eosaf, *************************************************************************** * Display file contents in selected format (logic #1). * Logic #1: Grouped by record type. Sorted by timestamp. - * Formats: XML, JSON + * Formats: XML, JSON, PCP * * IN: * @ifd File descriptor of input file. * @file_actlst List of (known or unknown) activities in file. * @file System activity data file name (name of file being read). + * @pcparchive PCP archive file name. * @file_magic System activity file magic header. * @rectime Structure where timestamp (expressed in local time or in UTC * depending on whether options -T/-t have been used or not) can @@ -996,7 +1009,7 @@ void display_curr_act_graphs(int ifd, int *curr, long *cnt, int *eosaf, *************************************************************************** */ void logic1_display_loop(int ifd, struct file_activity *file_actlst, char *file, - struct file_magic *file_magic, + char *pcparchive, struct file_magic *file_magic, struct tm *rectime, struct tm *loctime) { int curr, tab = 0, rtype; @@ -1013,13 +1026,13 @@ void logic1_display_loop(int ifd, struct file_activity *file_actlst, char *file, /* Print header (eg. XML file header) */ if (*fmt[f_position]->f_header) { - (*fmt[f_position]->f_header)(&tab, F_BEGIN, file, file_magic, + (*fmt[f_position]->f_header)(&tab, F_BEGIN, pcparchive, file_magic, &file_hdr, act, id_seq, file_actlst); } /* Process activities */ if (*fmt[f_position]->f_statistics) { - (*fmt[f_position]->f_statistics)(&tab, F_BEGIN); + (*fmt[f_position]->f_statistics)(&tab, F_BEGIN, act, id_seq); } do { @@ -1051,7 +1064,7 @@ void logic1_display_loop(int ifd, struct file_activity *file_actlst, char *file, if (!eosaf && (rtype != R_COMMENT) && (rtype != R_RESTART)) { if (*fmt[f_position]->f_statistics) { - (*fmt[f_position]->f_statistics)(&tab, F_MAIN); + (*fmt[f_position]->f_statistics)(&tab, F_MAIN, act, id_seq); } /* next is set to 1 when we were close enough to desired interval */ @@ -1085,7 +1098,7 @@ void logic1_display_loop(int ifd, struct file_activity *file_actlst, char *file, while (!eosaf); if (*fmt[f_position]->f_statistics) { - (*fmt[f_position]->f_statistics)(&tab, F_END); + (*fmt[f_position]->f_statistics)(&tab, F_END, act, id_seq); } /* Rewind file */ @@ -1132,7 +1145,7 @@ void logic1_display_loop(int ifd, struct file_activity *file_actlst, char *file, /* Print header trailer */ if (*fmt[f_position]->f_header) { - (*fmt[f_position]->f_header)(&tab, F_END, file, file_magic, + (*fmt[f_position]->f_header)(&tab, F_END, pcparchive, file_magic, &file_hdr, act, id_seq, file_actlst); } } @@ -1402,9 +1415,10 @@ close_svg: * * IN: * @dfile System activity data file name. + * @pcparchive PCP archive file name. *************************************************************************** */ -void read_stats_from_file(char dfile[]) +void read_stats_from_file(char dfile[], char pcparchive[]) { struct file_magic file_magic; struct file_activity *file_actlst = NULL; @@ -1418,6 +1432,9 @@ void read_stats_from_file(char dfile[]) if (DISPLAY_HDR_ONLY(flags)) { if (*fmt[f_position]->f_header) { + if (format == F_PCP_OUTPUT) { + dfile = pcparchive; + } /* Display only data file header then exit */ (*fmt[f_position]->f_header)(&tab, F_BEGIN + F_END, dfile, &file_magic, &file_hdr, act, id_seq, file_actlst); @@ -1438,7 +1455,7 @@ void read_stats_from_file(char dfile[]) &rectime, &loctime, dfile, &file_magic); } else { - logic1_display_loop(ifd, file_actlst, dfile, + logic1_display_loop(ifd, file_actlst, dfile, pcparchive, &file_magic, &rectime, &loctime); } @@ -1831,7 +1848,7 @@ int main(int argc, char **argv) } else { /* Read stats from file */ - read_stats_from_file(dfile); + read_stats_from_file(dfile, pcparchive); } /* Free bitmaps */ diff --git a/sadf.h b/sadf.h index 98b90af..1d892e8 100644 --- a/sadf.h +++ b/sadf.h @@ -161,9 +161,11 @@ __printf_funct_t print_raw_comment * Prototypes used to display the statistics part of the report */ __printf_funct_t print_xml_statistics - (int *, int); + (int *, int, struct activity * [], unsigned int []); __printf_funct_t print_json_statistics - (int *, int); + (int *, int, struct activity * [], unsigned int []); +__printf_funct_t print_pcp_statistics + (int *, int, struct activity * [], unsigned int []); /* * Prototypes used to display the timestamp part of the report @@ -194,5 +196,8 @@ __printf_funct_t print_hdr_header __printf_funct_t print_svg_header (void *, int, char *, struct file_magic *, struct file_header *, struct activity * [], unsigned int [], struct file_activity *); +__printf_funct_t print_pcp_header + (void *, int, char *, struct file_magic *, struct file_header *, + struct activity * [], unsigned int [], struct file_activity *); #endif /* _SADF_H */ diff --git a/sadf_misc.c b/sadf_misc.c index 9242ac2..e3d9520 100644 --- a/sadf_misc.c +++ b/sadf_misc.c @@ -32,6 +32,11 @@ #define _(string) (string) #endif +#ifdef HAVE_PCP +#include +#include +#endif + extern unsigned int flags; extern char *seps[]; @@ -406,12 +411,15 @@ __printf_funct_t print_raw_comment(int *tab, int action, char *cur_date, * IN: * @tab Number of tabulations. * @action Action expected from current function. + * @act Array of activities (unused here). + * @id_seq Activity sequence (unused here). * * OUT: * @tab Number of tabulations. *************************************************************************** */ -__printf_funct_t print_xml_statistics(int *tab, int action) +__printf_funct_t print_xml_statistics(int *tab, int action, struct activity *act[], + unsigned int id_seq[]) { if (action & F_BEGIN) { xprintf((*tab)++, ""); @@ -428,12 +436,15 @@ __printf_funct_t print_xml_statistics(int *tab, int action) * IN: * @tab Number of tabulations. * @action Action expected from current function. + * @act Array of activities (unused here). + * @id_seq Activity sequence (unused here). * * OUT: * @tab Number of tabulations. *************************************************************************** */ -__printf_funct_t print_json_statistics(int *tab, int action) +__printf_funct_t print_json_statistics(int *tab, int action, struct activity *act[], + unsigned int id_seq[]) { static int sep = FALSE; @@ -457,6 +468,59 @@ __printf_funct_t print_json_statistics(int *tab, int action) } } +/* + *************************************************************************** + * Display the "statistics" part of the report (PCP format). + * + * IN: + * @tab Number of tabulations (unused here). + * @action Action expected from current function. + * @act Array of activities. + * @id_seq Activity sequence. + *************************************************************************** + */ +__printf_funct_t print_pcp_statistics(int *tab, int action, struct activity *act[], + unsigned int id_seq[]) +{ +#ifdef HAVE_PCP + int i, p; + pmInDom indom; + + if (action & F_BEGIN) { + for (i = 0; i < NR_ACT; i++) { + if (!id_seq[i]) + continue; /* Activity not in file */ + + p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND); + if (!IS_SELECTED(act[p]->options)) + continue; /* Activity not selected */ + + switch (act[p]->id) { + + case A_QUEUE: + pmiAddMetric("proc.runq.runnable", + PM_IN_NULL, PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmiUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)); + pmiAddMetric("proc.nprocs", + PM_IN_NULL, PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmiUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)); + pmiAddMetric("proc.blocked", + PM_IN_NULL, PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmiUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)); + indom = pmInDom_build(0, 0); + pmiAddMetric("kernel.all.load", + PM_IN_NULL, PM_TYPE_FLOAT, indom, PM_SEM_INSTANT, + pmiUnits(0, 0, 0, 0, 0, 0)); + pmiAddInstance(indom, "1 min", 0); + pmiAddInstance(indom, "5 min", 1); + pmiAddInstance(indom, "15 min", 2); + break; + } + } + } +#endif /* HAVE_PCP */ +} + /* *************************************************************************** * Display the "timestamp" part of the report (db and ppc format). @@ -679,7 +743,7 @@ __tm_funct_t print_raw_timestamp(void *parm, int action, char *cur_date, * IN: * @parm Specific parameter. Here: number of tabulations. * @action Action expected from current function. - * @dfile Name of system activity data file. + * @dfile Unused here (PCP archive file). * @file_magic System activity file magic header. * @file_hdr System activity file standard header. * @act Array of activities (unused here). @@ -747,7 +811,7 @@ __printf_funct_t print_xml_header(void *parm, int action, char *dfile, * IN: * @parm Specific parameter. Here: number of tabulations. * @action Action expected from current function. - * @dfile Name of system activity data file. + * @dfile Unused here (PCP archive file). * @file_magic System activity file magic header. * @file_hdr System activity file standard header. * @act Array of activities (unused here). @@ -808,7 +872,7 @@ __printf_funct_t print_json_header(void *parm, int action, char *dfile, * IN: * @parm Specific parameter (unused here). * @action Action expected from current function. - * @dfile Name of system activity data file. + * @dfile Name of system activity data file (unused here). * @file_magic System activity file magic header. * @file_hdr System activity file standard header. * @act Array of activities. @@ -1005,6 +1069,53 @@ __printf_funct_t print_svg_header(void *parm, int action, char *dfile, } } +/* + *************************************************************************** + * PCP header function. + * + * IN: + * @parm Specific parameter. + * @action Action expected from current function. + * @dfile Name of PCP archive file. + * @file_magic System activity file magic header (unused here). + * @file_hdr System activity file standard header (unused here). + * @act Array of activities (unused here). + * @id_seq Activity sequence (unused here). + * @file_actlst List of (known or unknown) activities in file (unused here). + *************************************************************************** + */ +__printf_funct_t print_pcp_header(void *parm, int action, char *dfile, + struct file_magic *file_magic, + struct file_header *file_hdr, + struct activity *act[], unsigned int id_seq[], + struct file_activity *file_actlst) +{ +#ifdef HAVE_PCP + char buf[64]; + + if (action & F_BEGIN) { + /* Create new PCP context */ + pmiStart(dfile, FALSE); + pmiSetTimezone("UTC"); + + /* Save number of CPU in PCP archive */ + pmiAddMetric("hinv.ncpu", + PM_IN_NULL, PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + pmiUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)); + snprintf(buf, sizeof(buf), "%u", + file_hdr->sa_cpu_nr > 1 ? file_hdr->sa_cpu_nr - 1 : 1); + pmiPutValue("hinv.ncpu", NULL, buf); + } + + if (action & F_END) { + if (action & F_BEGIN) { + pmiWrite(file_hdr->sa_ust_time, 0); + } + pmiEnd(); + } +#endif +} + /* *************************************************************************** * Count the number of new network interfaces in current sample. If a new