MINOR: activity: allow to switch per-task lock/memory profiling at runtime

Given that we already have "set profiling task", it's easy to permit to
enable/disable the lock and/or memory profiling at run time. However, the
change will only be applied next time the task profiling will be switched
from off/auto to on.

The patch is very minor and is best viewed with git show -b because it
indents a whole block that moves in a "if" clause.

This can be backported to 3.3 along with the two previous patches.
This commit is contained in:
Willy Tarreau 2026-02-10 16:57:55 +01:00
parent e2631ee5f7
commit c724693b95
2 changed files with 63 additions and 39 deletions

View File

@ -2558,7 +2558,8 @@ set maxconn global <maxconn>
delayed until the threshold is reached. A value of zero restores the initial
setting.
set profiling { tasks | memory } { auto | on | off }
set profiling memory { on | off }
set profiling tasks { auto | on | off | lock | no-lock | memory | no-memory }
Enables or disables CPU or memory 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". Note
@ -2568,6 +2569,13 @@ set profiling { tasks | memory } { auto | on | off }
on the linux-glibc target), and requires USE_MEMORY_PROFILING to be set at
compile time.
. For tasks profiling, it is possible to enable or disable the collection of
per-task lock and memory timings at runtime, but the change is only taken
into account next time the profiler switches from off/auto to on (either
automatically or manually). Thus when using "no-lock" to disable per-task
lock profiling and save CPU cycles, it is recommended to flip the task
profiling off then on to commit the change.
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

View File

@ -747,6 +747,8 @@ static int cfg_parse_prof_tasks(char **args, int section_type, struct proxy *cur
/* parse a "set profiling" command. It always returns 1. */
static int cli_parse_set_profiling(char **args, char *payload, struct appctx *appctx, void *private)
{
int arg;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
@ -792,52 +794,66 @@ static int cli_parse_set_profiling(char **args, char *payload, struct appctx *ap
if (strcmp(args[2], "tasks") != 0)
return cli_err(appctx, "Expects either 'tasks' or 'memory'.\n");
if (strcmp(args[3], "on") == 0) {
unsigned int old = profiling;
int i;
for (arg = 3; *args[arg]; arg++) {
if (strcmp(args[arg], "on") == 0) {
unsigned int old = profiling;
int i;
while (!_HA_ATOMIC_CAS(&profiling, &old, (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_ON))
;
while (!_HA_ATOMIC_CAS(&profiling, &old, (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_ON))
;
HA_ATOMIC_STORE(&prof_task_start_ns, now_ns);
HA_ATOMIC_STORE(&prof_task_stop_ns, 0);
HA_ATOMIC_STORE(&prof_task_start_ns, now_ns);
HA_ATOMIC_STORE(&prof_task_stop_ns, 0);
/* also flush current profiling stats */
for (i = 0; i < SCHED_ACT_HASH_BUCKETS; i++) {
HA_ATOMIC_STORE(&sched_activity[i].calls, 0);
HA_ATOMIC_STORE(&sched_activity[i].cpu_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].lat_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].lkw_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].lkd_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].mem_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].func, NULL);
HA_ATOMIC_STORE(&sched_activity[i].caller, NULL);
/* also flush current profiling stats */
for (i = 0; i < SCHED_ACT_HASH_BUCKETS; i++) {
HA_ATOMIC_STORE(&sched_activity[i].calls, 0);
HA_ATOMIC_STORE(&sched_activity[i].cpu_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].lat_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].lkw_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].lkd_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].mem_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].func, NULL);
HA_ATOMIC_STORE(&sched_activity[i].caller, NULL);
}
}
}
else if (strcmp(args[3], "auto") == 0) {
unsigned int old = profiling;
unsigned int new;
else if (strcmp(args[arg], "auto") == 0) {
unsigned int old = profiling;
unsigned int new;
do {
if ((old & HA_PROF_TASKS_MASK) >= HA_PROF_TASKS_AON)
new = (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_AON;
else
new = (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_AOFF;
} while (!_HA_ATOMIC_CAS(&profiling, &old, new));
do {
if ((old & HA_PROF_TASKS_MASK) >= HA_PROF_TASKS_AON)
new = (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_AON;
else
new = (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_AOFF;
} while (!_HA_ATOMIC_CAS(&profiling, &old, new));
HA_ATOMIC_STORE(&prof_task_start_ns, now_ns);
HA_ATOMIC_STORE(&prof_task_stop_ns, 0);
}
else if (strcmp(args[3], "off") == 0) {
unsigned int old = profiling;
while (!_HA_ATOMIC_CAS(&profiling, &old, (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_OFF))
;
HA_ATOMIC_STORE(&prof_task_start_ns, now_ns);
HA_ATOMIC_STORE(&prof_task_stop_ns, 0);
}
else if (strcmp(args[arg], "off") == 0) {
unsigned int old = profiling;
while (!_HA_ATOMIC_CAS(&profiling, &old, (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_OFF))
;
if (HA_ATOMIC_LOAD(&prof_task_start_ns))
HA_ATOMIC_STORE(&prof_task_stop_ns, now_ns);
if (HA_ATOMIC_LOAD(&prof_task_start_ns))
HA_ATOMIC_STORE(&prof_task_stop_ns, now_ns);
}
else if (strcmp(args[arg], "lock") == 0)
HA_ATOMIC_OR(&profiling, HA_PROF_TASKS_LOCK);
else if (strcmp(args[arg], "no-lock") == 0)
HA_ATOMIC_AND(&profiling, ~HA_PROF_TASKS_LOCK);
else if (strcmp(args[arg], "memory") == 0)
HA_ATOMIC_OR(&profiling, HA_PROF_TASKS_MEM);
else if (strcmp(args[arg], "no-memory") == 0)
HA_ATOMIC_AND(&profiling, ~HA_PROF_TASKS_MEM);
else
break; // unknown arg
}
else
return cli_err(appctx, "Expects 'on', 'auto', or 'off'.\n");
/* either no arg or invalid one */
if (arg == 3 || *args[arg])
return cli_err(appctx, "Expects a combination of either 'on', 'auto', 'off', 'lock', 'no-lock', 'memory' or 'no-memory'.\n");
return 1;
}