From: Michael Tremer Date: Sat, 4 Oct 2025 11:55:50 +0000 (+0000) Subject: Add a processor graph X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=abfa42f5924151ca1a74769a7716213527a740c8;p=collecty.git Add a processor graph Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index 68e7f2c..17da441 100644 --- a/Makefile.am +++ b/Makefile.am @@ -112,6 +112,8 @@ dist_collectyd_SOURCES = \ src/daemon/graphs/graph.h \ src/daemon/graphs/loadavg.c \ src/daemon/graphs/loadavg.h \ + src/daemon/graphs/processor.c \ + src/daemon/graphs/processor.h \ src/daemon/i18n.h \ src/daemon/logging.c \ src/daemon/logging.h \ diff --git a/src/daemon/colors.h b/src/daemon/colors.h index a6eda8a..083011e 100644 --- a/src/daemon/colors.h +++ b/src/daemon/colors.h @@ -32,6 +32,8 @@ #define LIGHT_GREEN "#8bc34a" #define BLUE "#2196f3" #define LIGHT_BLUE "#03a9f4" +#define PINK "#e91e63" +#define DEEP_PURPLE "#673ab7" /* Define some colours with a special meaning... diff --git a/src/daemon/graphs.c b/src/daemon/graphs.c index 6edea48..d2dda70 100644 --- a/src/daemon/graphs.c +++ b/src/daemon/graphs.c @@ -30,12 +30,14 @@ #include "graphs/conntrack.h" #include "graphs/contextswitches.h" #include "graphs/loadavg.h" +#include "graphs/processor.h" // Register all graphs static const collecty_graph_impl* graph_impls[] = { &conntrack_graph, &contextswitches_graph, &loadavg_graph, + &processor_graph, NULL, }; diff --git a/src/daemon/graphs/graph.h b/src/daemon/graphs/graph.h index 97181d3..2e6c6f7 100644 --- a/src/daemon/graphs/graph.h +++ b/src/daemon/graphs/graph.h @@ -49,6 +49,7 @@ // A column is exactly 16 characters wide #define COLUMN "%16s" +#define PERCENTAGE "%%13.2lf%%%%" #define INTEGER "%%16.0lf" #define LARGE_INTEGER "%%14.0lf %%s" #define FLOAT "%%14.2lf" @@ -72,22 +73,35 @@ #define DRAW_LINE3(args, field, color, ...) DRAW(args, "LINE3", field, color, __VA_ARGS__) #define DRAW_LINE3_WITH_LABEL(args, field, color, label, ...) DRAW_WITH_LABEL(args, "LINE3", field, color, label, __VA_ARGS__) +// Macros to draw the background of an area +#define DRAW_AREA_BACKGROUND(args, field, color, ...) \ + DRAW(args, "AREA", field, COLOR_WITH_ALPHA(color, OPACITY_AREA), __VA_ARGS__) + +// Macros to draw the area's outline +#define DRAW_AREA_OUTLINE_WITH_LABEL DRAW_LINE1_WITH_LABEL + // Areas are being drawn with an outline #define DRAW_AREA_WITH_LABEL(args, field, color, label, ...) \ do { \ - DRAW(args, "AREA", field, COLOR_WITH_ALPHA(color, OPACITY_AREA)); \ - DRAW_LINE1_WITH_LABEL(args, field, color, label, __VA_ARGS__); \ + DRAW_AREA_BACKGROUND(args, field, color); \ + DRAW_AREA_OUTLINE_WITH_LABEL(args, field, color, label, __VA_ARGS__); \ } while(0) +// Modifiers +#define STACK ":STACK" + // Modifiers for lines #define DASHES ":dashes" #define SKIPSCALE ":skipscale" // Add something to the legend of the graph #define PRINT(args, field, ...) SCRIPT(args, "GPRINT:" field ":" __VA_ARGS__) +#define PRINT_EMPTY_LINE(args) SCRIPT(args, "COMMENT: \\n") #define PRINT_HEADER(args, label, ...) SCRIPT(args, "COMMENT:" COLUMN __VA_ARGS__, label) +#define PRINT_LABEL(args, label, ...) SCRIPT(args, "COMMENT: %-31s" __VA_ARGS__, label) #define PRINT_EMPTY_LABEL(args, ...) SCRIPT(args, "COMMENT: " __VA_ARGS__) #define PRINT_NOTHING(args, ...) SCRIPT(args, "COMMENT: " __VA_ARGS__) +#define PRINT_PERCENTAGE(args, field, ...) PRINT(args, field, PERCENTAGE __VA_ARGS__) #define PRINT_INTEGER(args, field, ...) PRINT(args, field, INTEGER __VA_ARGS__) #define PRINT_LARGE_INTEGER(args, field, ...) PRINT(args, field, LARGE_INTEGER __VA_ARGS__) #define PRINT_FLOAT(args, field, ...) PRINT(args, field, FLOAT __VA_ARGS__) @@ -102,4 +116,38 @@ PRINT_HEADER(args, header4, EOL); \ } while (0) +// Handles for fields +#define FIELD_CURRENT(field) field "_cur" +#define FIELD_AVERAGE(field) field "_avg" +#define FIELD_MINIMUM(field) field "_min" +#define FIELD_MAXIMUM(field) field "_max" +#define FIELD_PERCENT(field) field "_p" + +#define VALUE_CURRENT(args, field) SCRIPT(args, "VDEF:" FIELD_CURRENT(field) "=" field ",LAST") +#define VALUE_AVERAGE(args, field) SCRIPT(args, "VDEF:" FIELD_AVERAGE(field) "=" field ",AVERAGE") +#define VALUE_MINIMUM(args, field) SCRIPT(args, "VDEF:" FIELD_MINIMUM(field) "=" field ",MINIMUM") +#define VALUE_MAXIMUM(args, field) SCRIPT(args, "VDEF:" FIELD_MAXIMUM(field) "=" field ",MAXIMUM") + +// Computes all values +#define VALUE_ALL(args, field) \ + do { \ + VALUE_CURRENT(args, field); \ + VALUE_AVERAGE(args, field); \ + VALUE_MINIMUM(args, field); \ + VALUE_MAXIMUM(args, field); \ + } while (0) + +#define COMPUTE_SUM(args, sum, summand1, summand2) \ + do { \ + SCRIPT(args, "CDEF:" sum "=" summand1 "," summand2 ",+"); \ + VALUE_ALL(args, sum); \ + } while (0) + +#define COMPUTE_PERCENTAGE(args, field, total) \ + do { \ + SCRIPT(args, "CDEF:" FIELD_PERCENT(field) "=100," field ",*," total ",/"); \ + VALUE_ALL(args, FIELD_PERCENT(field)); \ + } while (0) + + #endif /* COLLECTY_GRAPHS_GRAPH_H */ diff --git a/src/daemon/graphs/processor.c b/src/daemon/graphs/processor.c new file mode 100644 index 0000000..b005808 --- /dev/null +++ b/src/daemon/graphs/processor.c @@ -0,0 +1,176 @@ +/*############################################################################# +# # +# collecty - A system statistics collection daemon for IPFire # +# Copyright (C) 2025 IPFire Development Team # +# # +# 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 3 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, see . # +# # +#############################################################################*/ + +#include "graph.h" +#include "processor.h" + +// Set some colors +#define COLOR_USER LIGHT_GREEN +#define COLOR_NICE BLUE +#define COLOR_SYS RED +#define COLOR_WAIT DEEP_PURPLE +#define COLOR_IRQ ORANGE +#define COLOR_SOFTIRQ YELLOW +#define COLOR_STEAL LIGHT_BLUE +#define COLOR_GUEST PINK +#define COLOR_GUEST_NICE PINK +#define COLOR_IDLE LIGHT_GREY + +static int processor_title(collecty_ctx* ctx, collecty_graph* graph, + const char* object, char** title) { + return collecty_format_title(title, "%s", _("Processor Usage")); +} + +static int processor_vlabel(collecty_ctx* ctx, collecty_graph* graph, + const char* object, char** title) { + return collecty_format_title(title, "%s", _("Percent")); +} + + +static int processor_render(collecty_ctx* ctx, + collecty_graph* graph, collecty_args* args, const char* object) { + int r; + + // This requires the loadavg source + r = collecty_graph_require_source(graph, args, "processor", object); + if (r < 0) + return r; + + // Add up all used cycles + r = collecty_args_push(args, + "CDEF:usage=user,nice,+,sys,+,wait,+,irq,+,softirq,+,steal,+,guest,+,guest_nice,+"); + if (r < 0) + return r; + + // Add idle to get the total number of cycles + COMPUTE_SUM(args, "total", "usage", "idle"); + + // Compute usage in percent + COMPUTE_PERCENTAGE(args, "usage", "total"); + COMPUTE_PERCENTAGE(args, "user", "total"); + COMPUTE_PERCENTAGE(args, "nice", "total"); + COMPUTE_PERCENTAGE(args, "sys", "total"); + COMPUTE_PERCENTAGE(args, "wait", "total"); + COMPUTE_PERCENTAGE(args, "irq", "total"); + COMPUTE_PERCENTAGE(args, "softirq", "total"); + COMPUTE_PERCENTAGE(args, "steal", "total"); + COMPUTE_PERCENTAGE(args, "guest", "total"); + COMPUTE_PERCENTAGE(args, "guest_nice", "total"); + + // Header + PRINT_HEADER4(args, _("Current"), _("Average"), _("Minimum"), _("Maximum")); + + // Show the total usage + PRINT_LABEL(args, _("Total")); + PRINT_PERCENTAGE(args, FIELD_CURRENT(FIELD_PERCENT("usage"))); + PRINT_PERCENTAGE(args, FIELD_AVERAGE(FIELD_PERCENT("usage"))); + PRINT_PERCENTAGE(args, FIELD_MINIMUM(FIELD_PERCENT("usage"))); + PRINT_PERCENTAGE(args, FIELD_MAXIMUM(FIELD_PERCENT("usage")), EOL); + + PRINT_EMPTY_LINE(args); + + // Draw the stacked background first + DRAW_AREA_BACKGROUND(args, FIELD_PERCENT("user"), COLOR_USER); + DRAW_AREA_BACKGROUND(args, FIELD_PERCENT("nice"), COLOR_NICE, STACK); + DRAW_AREA_BACKGROUND(args, FIELD_PERCENT("sys"), COLOR_SYS, STACK); + DRAW_AREA_BACKGROUND(args, FIELD_PERCENT("wait"), COLOR_WAIT, STACK); + DRAW_AREA_BACKGROUND(args, FIELD_PERCENT("irq"), COLOR_IRQ, STACK); + DRAW_AREA_BACKGROUND(args, FIELD_PERCENT("softirq"), COLOR_SOFTIRQ, STACK); + DRAW_AREA_BACKGROUND(args, FIELD_PERCENT("steal"), COLOR_STEAL, STACK); + DRAW_AREA_BACKGROUND(args, FIELD_PERCENT("guest"), COLOR_GUEST, STACK); + DRAW_AREA_BACKGROUND(args, FIELD_PERCENT("guest_nice"), COLOR_GUEST_NICE, STACK); + + // Draw the area outlines afterwards + DRAW_AREA_OUTLINE_WITH_LABEL(args, + FIELD_PERCENT("user"), COLOR_USER, _("User")); + PRINT_PERCENTAGE(args, FIELD_CURRENT(FIELD_PERCENT("user"))); + PRINT_PERCENTAGE(args, FIELD_AVERAGE(FIELD_PERCENT("user"))); + PRINT_PERCENTAGE(args, FIELD_MINIMUM(FIELD_PERCENT("user"))); + PRINT_PERCENTAGE(args, FIELD_MAXIMUM(FIELD_PERCENT("user")), EOL); + + DRAW_AREA_OUTLINE_WITH_LABEL(args, + FIELD_PERCENT("nice"), COLOR_NICE, _("Nice"), STACK); + PRINT_PERCENTAGE(args, FIELD_CURRENT(FIELD_PERCENT("nice"))); + PRINT_PERCENTAGE(args, FIELD_AVERAGE(FIELD_PERCENT("nice"))); + PRINT_PERCENTAGE(args, FIELD_MINIMUM(FIELD_PERCENT("nice"))); + PRINT_PERCENTAGE(args, FIELD_MAXIMUM(FIELD_PERCENT("nice")), EOL); + + DRAW_AREA_OUTLINE_WITH_LABEL(args, + FIELD_PERCENT("sys"), COLOR_SYS, _("Sys"), STACK); + PRINT_PERCENTAGE(args, FIELD_CURRENT(FIELD_PERCENT("sys"))); + PRINT_PERCENTAGE(args, FIELD_AVERAGE(FIELD_PERCENT("sys"))); + PRINT_PERCENTAGE(args, FIELD_MINIMUM(FIELD_PERCENT("sys"))); + PRINT_PERCENTAGE(args, FIELD_MAXIMUM(FIELD_PERCENT("sys")), EOL); + + DRAW_AREA_OUTLINE_WITH_LABEL(args, + FIELD_PERCENT("wait"), COLOR_WAIT, _("Wait"), STACK); + PRINT_PERCENTAGE(args, FIELD_CURRENT(FIELD_PERCENT("wait"))); + PRINT_PERCENTAGE(args, FIELD_AVERAGE(FIELD_PERCENT("wait"))); + PRINT_PERCENTAGE(args, FIELD_MINIMUM(FIELD_PERCENT("wait"))); + PRINT_PERCENTAGE(args, FIELD_MAXIMUM(FIELD_PERCENT("wait")), EOL); + + DRAW_AREA_OUTLINE_WITH_LABEL(args, + FIELD_PERCENT("irq"), COLOR_IRQ, _("Interrupt"), STACK); + PRINT_PERCENTAGE(args, FIELD_CURRENT(FIELD_PERCENT("irq"))); + PRINT_PERCENTAGE(args, FIELD_AVERAGE(FIELD_PERCENT("irq"))); + PRINT_PERCENTAGE(args, FIELD_MINIMUM(FIELD_PERCENT("irq"))); + PRINT_PERCENTAGE(args, FIELD_MAXIMUM(FIELD_PERCENT("irq")), EOL); + + DRAW_AREA_OUTLINE_WITH_LABEL(args, + FIELD_PERCENT("softirq"), COLOR_SOFTIRQ, _("Soft Interrupt"), STACK); + PRINT_PERCENTAGE(args, FIELD_CURRENT(FIELD_PERCENT("softirq"))); + PRINT_PERCENTAGE(args, FIELD_AVERAGE(FIELD_PERCENT("softirq"))); + PRINT_PERCENTAGE(args, FIELD_MINIMUM(FIELD_PERCENT("softirq"))); + PRINT_PERCENTAGE(args, FIELD_MAXIMUM(FIELD_PERCENT("softirq")), EOL); + + DRAW_AREA_OUTLINE_WITH_LABEL(args, + FIELD_PERCENT("steal"), COLOR_STEAL, _("Steal"), STACK); + PRINT_PERCENTAGE(args, FIELD_CURRENT(FIELD_PERCENT("steal"))); + PRINT_PERCENTAGE(args, FIELD_AVERAGE(FIELD_PERCENT("steal"))); + PRINT_PERCENTAGE(args, FIELD_MINIMUM(FIELD_PERCENT("steal"))); + PRINT_PERCENTAGE(args, FIELD_MAXIMUM(FIELD_PERCENT("steal")), EOL); + + DRAW_AREA_OUTLINE_WITH_LABEL(args, + FIELD_PERCENT("guest"), COLOR_GUEST, _("Guest"), STACK); + PRINT_PERCENTAGE(args, FIELD_CURRENT(FIELD_PERCENT("guest"))); + PRINT_PERCENTAGE(args, FIELD_AVERAGE(FIELD_PERCENT("guest"))); + PRINT_PERCENTAGE(args, FIELD_MINIMUM(FIELD_PERCENT("guest"))); + PRINT_PERCENTAGE(args, FIELD_MAXIMUM(FIELD_PERCENT("guest")), EOL); + + DRAW_AREA_OUTLINE_WITH_LABEL(args, + FIELD_PERCENT("guest_nice"), COLOR_GUEST_NICE, _("Guest Nice"), STACK); + PRINT_PERCENTAGE(args, FIELD_CURRENT(FIELD_PERCENT("guest_nice"))); + PRINT_PERCENTAGE(args, FIELD_AVERAGE(FIELD_PERCENT("guest_nice"))); + PRINT_PERCENTAGE(args, FIELD_MINIMUM(FIELD_PERCENT("guest_nice"))); + PRINT_PERCENTAGE(args, FIELD_MAXIMUM(FIELD_PERCENT("guest_nice")), EOL); + + return 0; +} + +const collecty_graph_impl processor_graph = { + .name = "Processor", + .render = processor_render, + .title = processor_title, + .vlabel = processor_vlabel, + + // Limits + .lower_limit = 0, + .upper_limit = 100, +}; diff --git a/src/daemon/graphs/processor.h b/src/daemon/graphs/processor.h new file mode 100644 index 0000000..6df691d --- /dev/null +++ b/src/daemon/graphs/processor.h @@ -0,0 +1,28 @@ +/*############################################################################# +# # +# collecty - A system statistics collection daemon for IPFire # +# Copyright (C) 2025 IPFire Development Team # +# # +# 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 3 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, see . # +# # +#############################################################################*/ + +#ifndef COLLECTY_GRAPH_PROCESSOR_H +#define COLLECTY_GRAPH_PROCESSOR_H + +#include "../graph.h" + +extern const collecty_graph_impl processor_graph; + +#endif /* COLLECTY_GRAPH_PROCESSOR_H */