diff --git a/doc/configuration.txt b/doc/configuration.txt index 7552352e0..180bfc853 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -619,6 +619,7 @@ The following keywords are supported in the "global" section : - nosplice - nogetaddrinfo - noreuseport + - profiling.tasks - spread-checks - server-state-base - server-state-file @@ -1434,6 +1435,16 @@ noreuseport Disables the use of SO_REUSEPORT - see socket(7). It is equivalent to the command line argument "-dR". +profiling.tasks { on | off } + Enables ('on') or disables ('off') per-task CPU profiling. CPU profiling per + task can be very convenient to report where the time is spent and which + requests have what effect on which other request. It is not enabled by + default as it may consume a little bit extra CPU. This requires a system + supporting the clock_gettime(2) syscall with clock identifiers + CLOCK_MONOTONIC and CLOCK_THREAD_CPUTIME_ID, otherwise the reported time will + be zero. This option may be changed at run time using "set profiling" on the + CLI. + spread-checks <0..50, in percent> Sometimes it is desirable to avoid sending agent and health checks to servers at exact intervals, for instance when many logical servers are diff --git a/doc/management.txt b/doc/management.txt index 885ff2cc9..c8f8c65a9 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -1649,6 +1649,11 @@ set maxconn global delayed until the threshold is reached. A value of zero restores the initial setting. +set profiling { tasks } { on | off } + Enables or disables CPU profiling for the indicated subsystem. This is + equivalent to setting or clearing the "profiling" settings in the "global" + section of the configuration file. Please also see "show profiling". + set rate-limit connections global Change the process-wide connection rate limit, which is set by the global 'maxconnrate' setting. A value of zero disables the limitation. This limit @@ -2045,6 +2050,10 @@ show pools as the SIGQUIT when running in foreground except that it does not flush the pools. +show profiling + Dumps the current profiling settings, one per line, as well as the command + needed to change them. + show servers state [] Dump the state of the servers found in the running configuration. A backend name or identifier may be provided to limit the output to this backend only. diff --git a/include/proto/activity.h b/include/proto/activity.h index 717d6678e..098206e18 100644 --- a/include/proto/activity.h +++ b/include/proto/activity.h @@ -28,6 +28,10 @@ #include #include +/* bit fields for "profiling" */ +#define HA_PROF_TASKS 0x00000001 /* enable per-task CPU profiling */ + +extern unsigned int profiling; extern struct activity activity[MAX_THREADS]; diff --git a/src/activity.c b/src/activity.c index c843b47fd..9460a8a7e 100644 --- a/src/activity.c +++ b/src/activity.c @@ -10,11 +10,19 @@ * */ +#include #include #include #include #include +#include +#include #include +#include + + +/* bit field of profiling options. Beware, may be modified at runtime! */ +unsigned int profiling; /* One struct per thread containing all collected measurements */ struct activity activity[MAX_THREADS] __attribute__((aligned(64))) = { }; @@ -29,3 +37,95 @@ void report_stolen_time(uint64_t stolen) update_freq_ctr(&activity[tid].cpust_1s, stolen); update_freq_ctr_period(&activity[tid].cpust_15s, 15000, stolen); } + +/* config parser for global "profiling.tasks", accepts "on" or "off" */ +static int cfg_parse_prof_tasks(char **args, int section_type, struct proxy *curpx, + struct proxy *defpx, const char *file, int line, + char **err) +{ + if (too_many_args(1, args, err, NULL)) + return -1; + + if (strcmp(args[1], "on") == 0) + profiling |= HA_PROF_TASKS; + else if (strcmp(args[1], "off") == 0) + profiling &= ~HA_PROF_TASKS; + else { + memprintf(err, "'%s' expects either 'on' or 'off' but got '%s'.", args[0], args[1]); + return -1; + } + return 0; +} + +/* parse a "set profiling" command. It always returns 1. */ +static int cli_parse_set_profiling(char **args, char *payload, struct appctx *appctx, void *private) +{ + unsigned int bit = 0; + + if (!cli_has_level(appctx, ACCESS_LVL_ADMIN)) + return 1; + + if (strcmp(args[2], "tasks") == 0) + bit = HA_PROF_TASKS; + else { + appctx->ctx.cli.severity = LOG_ERR; + appctx->ctx.cli.msg = "Expects 'tasks'.\n"; + appctx->st0 = CLI_ST_PRINT; + return 1; + } + + if (strcmp(args[3], "on") == 0) + HA_ATOMIC_OR(&profiling, bit); + else if (strcmp(args[3], "off") == 0) + HA_ATOMIC_AND(&profiling, ~bit); + else { + appctx->ctx.cli.severity = LOG_ERR; + appctx->ctx.cli.msg = "Expects either 'on' or 'off'.\n"; + appctx->st0 = CLI_ST_PRINT; + return 1; + } + return 1; +} + +/* This function dumps all profiling settings. It returns 0 if the output + * buffer is full and it needs to be called again, otherwise non-zero. + */ +static int cli_io_handler_show_profiling(struct appctx *appctx) +{ + struct stream_interface *si = appctx->owner; + + if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW))) + return 1; + + chunk_reset(&trash); + + chunk_printf(&trash, "Per-task CPU profiling : %s # set profiling tasks {on|off}\n", + (profiling & HA_PROF_TASKS) ? "on" : "off"); + + if (ci_putchk(si_ic(si), &trash) == -1) { + /* failed, try again */ + si_rx_room_blk(si); + return 0; + } + return 1; +} + +/* config keyword parsers */ +static struct cfg_kw_list cfg_kws = {ILH, { + { CFG_GLOBAL, "profiling.tasks", cfg_parse_prof_tasks }, + { 0, NULL, NULL } +}}; + +/* register cli keywords */ +static struct cli_kw_list cli_kws = {{ },{ + { { "show", "profiling", NULL }, "show profiling : show CPU profiling options", NULL, cli_io_handler_show_profiling, NULL }, + { { "set", "profiling", NULL }, "set profiling : enable/disable CPU profiling", cli_parse_set_profiling, NULL }, + {{},} +}}; + +__attribute__((constructor)) +static void __activity_init(void) +{ + cfg_register_keywords(&cfg_kws); + cli_register_kw(&cli_kws); +}