]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: activity: add configuration and CLI support for "profiling.tasks"
authorWilly Tarreau <w@1wt.eu>
Thu, 22 Nov 2018 10:02:09 +0000 (11:02 +0100)
committerWilly Tarreau <w@1wt.eu>
Thu, 22 Nov 2018 10:48:51 +0000 (11:48 +0100)
This is a new global setting which enables or disables CPU profiling
per task. For now it only sets/resets the variable based on the global
option "profiling.tasks" and supports showing it as well as setting it
from the CLI using "show profiling" and "set profiling". The option will
be used by a future commit. It was done in a way which should ease future
addition of profiling options.

doc/configuration.txt
doc/management.txt
include/proto/activity.h
src/activity.c

index 7552352e00b5fb86e6012fced8f7c192034cd02f..180bfc853b5373029597da9a1cc540cb157e74f5 100644 (file)
@@ -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
index 885ff2cc937506d00099faab789b664bcf6afc44..c8f8c65a92622d4febbad6d94125b77f61641972 100644 (file)
@@ -1649,6 +1649,11 @@ set maxconn global <maxconn>
   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 <value>
   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 [<backend>]
   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.
index 717d6678e64ec11b62d2b8962ff0a1ac461f9f9f..098206e182b149e2fdd195360890cda47f44e85f 100644 (file)
 #include <types/activity.h>
 #include <proto/freq_ctr.h>
 
+/* bit fields for "profiling" */
+#define HA_PROF_TASKS       0x00000001     /* enable per-task CPU profiling */
+
+extern unsigned int profiling;
 extern struct activity activity[MAX_THREADS];
 
 
index c843b47fd11a1dc9dca1332bbc870271d79dd890..9460a8a7e95a9071ea8c994c976154a24a7935a3 100644 (file)
  *
  */
 
+#include <common/cfgparse.h>
 #include <common/config.h>
 #include <common/standard.h>
 #include <common/hathreads.h>
 #include <types/activity.h>
+#include <proto/channel.h>
+#include <proto/cli.h>
 #include <proto/freq_ctr.h>
+#include <proto/stream_interface.h>
+
+
+/* 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);
+}