mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-11-29 14:50:59 +01:00
MINOR: debug/cli: add some debugging commands for developers
When haproxy is built with DEBUG_DEV, the following commands are added to the CLI : debug dev close <fd> : close this file descriptor debug dev delay [ms] : sleep this long debug dev exec [cmd] ... : show this command's output debug dev exit [code] : immediately exit the process debug dev hex <addr> [len]: dump a memory area debug dev log [msg] ... : send this msg to global logs debug dev loop [ms] : loop this long debug dev panic : immediately trigger a panic debug dev tkill [thr] [sig] : send signal to thread These are essentially aimed at helping developers trigger certain conditions and are expected to be complemented over time.
This commit is contained in:
parent
56131ca58e
commit
6bdf3e9b11
4
Makefile
4
Makefile
@ -209,8 +209,8 @@ SMALL_OPTS =
|
|||||||
#### Debug settings
|
#### Debug settings
|
||||||
# You can enable debugging on specific code parts by setting DEBUG=-DDEBUG_xxx.
|
# You can enable debugging on specific code parts by setting DEBUG=-DDEBUG_xxx.
|
||||||
# Currently defined DEBUG macros include DEBUG_FULL, DEBUG_MEMORY, DEBUG_FSM,
|
# Currently defined DEBUG macros include DEBUG_FULL, DEBUG_MEMORY, DEBUG_FSM,
|
||||||
# DEBUG_HASH, DEBUG_AUTH, DEBUG_SPOE, DEBUG_UAF and DEBUG_THREAD. Please check
|
# DEBUG_HASH, DEBUG_AUTH, DEBUG_SPOE, DEBUG_UAF and DEBUG_THREAD, DEBUG_STRICT,
|
||||||
# sources for exact meaning or do not use at all.
|
# DEBUG_DEV. Please check sources for exact meaning or do not use at all.
|
||||||
DEBUG =
|
DEBUG =
|
||||||
|
|
||||||
#### Trace options
|
#### Trace options
|
||||||
|
|||||||
@ -1454,6 +1454,12 @@ clear table <table> [ data.<type> <operator> <value> ] | [ key <key> ]
|
|||||||
$ echo "show table http_proxy" | socat stdio /tmp/sock1
|
$ echo "show table http_proxy" | socat stdio /tmp/sock1
|
||||||
>>> # table: http_proxy, type: ip, size:204800, used:1
|
>>> # table: http_proxy, type: ip, size:204800, used:1
|
||||||
|
|
||||||
|
debug dev <command> [args]*
|
||||||
|
Call a developer-specific command. Only supported when haproxy is built with
|
||||||
|
DEBUG_DEV defined. Supported commands are then listed in the help message.
|
||||||
|
All of these commands require admin privileges, and must never appear on a
|
||||||
|
production system as most of them are unsafe and dangerous.
|
||||||
|
|
||||||
del acl <acl> [<key>|#<ref>]
|
del acl <acl> [<key>|#<ref>]
|
||||||
Delete all the acl entries from the acl <acl> corresponding to the key <key>.
|
Delete all the acl entries from the acl <acl> corresponding to the key <key>.
|
||||||
<acl> is the #<id> or the <file> returned by "show acl". If the <ref> is used,
|
<acl> is the #<id> or the <file> returned by "show acl". If the <ref> is used,
|
||||||
|
|||||||
234
src/debug.c
234
src/debug.c
@ -13,6 +13,7 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <common/buf.h>
|
#include <common/buf.h>
|
||||||
#include <common/config.h>
|
#include <common/config.h>
|
||||||
@ -147,6 +148,228 @@ void ha_panic()
|
|||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(DEBUG_DEV)
|
||||||
|
/* parse a "debug dev exit" command. It always returns 1, though it should never return. */
|
||||||
|
static int debug_parse_cli_exit(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
|
{
|
||||||
|
int code = atoi(args[3]);
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
exit(code);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse a "debug dev close" command. It always returns 1. */
|
||||||
|
static int debug_parse_cli_close(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!*args[3]) {
|
||||||
|
appctx->ctx.cli.msg = "Missing file descriptor number.\n";
|
||||||
|
goto reterr;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = atoi(args[3]);
|
||||||
|
if (fd < 0 || fd >= global.maxsock) {
|
||||||
|
appctx->ctx.cli.msg = "File descriptor out of range.\n";
|
||||||
|
goto reterr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fdtab[fd].owner) {
|
||||||
|
appctx->ctx.cli.msg = "File descriptor was already closed.\n";
|
||||||
|
goto retinfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd_delete(fd);
|
||||||
|
return 1;
|
||||||
|
retinfo:
|
||||||
|
appctx->ctx.cli.severity = LOG_INFO;
|
||||||
|
appctx->st0 = CLI_ST_PRINT;
|
||||||
|
return 1;
|
||||||
|
reterr:
|
||||||
|
appctx->ctx.cli.severity = LOG_ERR;
|
||||||
|
appctx->st0 = CLI_ST_PRINT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse a "debug dev delay" command. It always returns 1. */
|
||||||
|
static int debug_parse_cli_delay(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
|
{
|
||||||
|
int delay = atoi(args[3]);
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
usleep((long)delay * 1000);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse a "debug dev log" command. It always returns 1. */
|
||||||
|
static int debug_parse_cli_log(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
|
{
|
||||||
|
int arg;
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
chunk_reset(&trash);
|
||||||
|
for (arg = 3; *args[arg]; arg++) {
|
||||||
|
if (arg > 3)
|
||||||
|
chunk_strcat(&trash, " ");
|
||||||
|
chunk_strcat(&trash, args[arg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
send_log(NULL, LOG_INFO, "%s\n", trash.area);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse a "debug dev loop" command. It always returns 1. */
|
||||||
|
static int debug_parse_cli_loop(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
|
{
|
||||||
|
struct timeval deadline, curr;
|
||||||
|
int loop = atoi(args[3]);
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
gettimeofday(&curr, NULL);
|
||||||
|
tv_ms_add(&deadline, &curr, loop);
|
||||||
|
|
||||||
|
while (tv_ms_cmp(&curr, &deadline) < 0)
|
||||||
|
gettimeofday(&curr, NULL);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse a "debug dev panic" command. It always returns 1, though it should never return. */
|
||||||
|
static int debug_parse_cli_panic(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
|
{
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
ha_panic();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse a "debug dev exec" command. It always returns 1. */
|
||||||
|
static int debug_parse_cli_exec(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
int arg;
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
chunk_reset(&trash);
|
||||||
|
for (arg = 3; *args[arg]; arg++) {
|
||||||
|
if (arg > 3)
|
||||||
|
chunk_strcat(&trash, " ");
|
||||||
|
chunk_strcat(&trash, args[arg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
f = popen(trash.area, "re");
|
||||||
|
if (!f) {
|
||||||
|
appctx->ctx.cli.severity = LOG_ERR;
|
||||||
|
appctx->ctx.cli.msg = "Failed to execute command.\n";
|
||||||
|
appctx->st0 = CLI_ST_PRINT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk_reset(&trash);
|
||||||
|
while (1) {
|
||||||
|
size_t ret = fread(trash.area + trash.data, 1, trash.size - 20 - trash.data, f);
|
||||||
|
if (!ret)
|
||||||
|
break;
|
||||||
|
trash.data += ret;
|
||||||
|
if (trash.data + 20 == trash.size) {
|
||||||
|
chunk_strcat(&trash, "\n[[[TRUNCATED]]]\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
trash.area[trash.data] = 0;
|
||||||
|
appctx->ctx.cli.severity = LOG_INFO;
|
||||||
|
appctx->ctx.cli.msg = trash.area;
|
||||||
|
appctx->st0 = CLI_ST_PRINT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse a "debug dev hex" command. It always returns 1. */
|
||||||
|
static int debug_parse_cli_hex(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
|
{
|
||||||
|
unsigned long start, len;
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!*args[3]) {
|
||||||
|
appctx->ctx.cli.msg = "Missing memory address to dump from.\n";
|
||||||
|
goto reterr;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = strtoul(args[3], NULL, 0);
|
||||||
|
if (!start) {
|
||||||
|
appctx->ctx.cli.msg = "Will not dump from NULL address.\n";
|
||||||
|
goto reterr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* by default, dump ~128 till next block of 16 */
|
||||||
|
len = strtoul(args[4], NULL, 0);
|
||||||
|
if (!len)
|
||||||
|
len = ((start + 128) & -16) - start;
|
||||||
|
|
||||||
|
chunk_reset(&trash);
|
||||||
|
dump_hex(&trash, " ", (const void *)start, len);
|
||||||
|
trash.area[trash.data] = 0;
|
||||||
|
appctx->ctx.cli.severity = LOG_INFO;
|
||||||
|
appctx->ctx.cli.msg = trash.area;
|
||||||
|
appctx->st0 = CLI_ST_PRINT;
|
||||||
|
return 1;
|
||||||
|
reterr:
|
||||||
|
appctx->ctx.cli.severity = LOG_ERR;
|
||||||
|
appctx->st0 = CLI_ST_PRINT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse a "debug dev tkill" command. It always returns 1. */
|
||||||
|
static int debug_parse_cli_tkill(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
|
{
|
||||||
|
int thr = 0;
|
||||||
|
int sig = SIGABRT;
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (*args[3])
|
||||||
|
thr = atoi(args[3]);
|
||||||
|
|
||||||
|
if (thr < 0 || thr > global.nbthread) {
|
||||||
|
appctx->ctx.cli.severity = LOG_ERR;
|
||||||
|
appctx->ctx.cli.msg = "Thread number out of range (use 0 for current).\n";
|
||||||
|
appctx->st0 = CLI_ST_PRINT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*args[4])
|
||||||
|
sig = atoi(args[4]);
|
||||||
|
|
||||||
|
#if defined(USE_THREAD)
|
||||||
|
if (thr)
|
||||||
|
pthread_kill(thread_info[thr-1].pthread, sig);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
raise(sig);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef USE_THREAD_DUMP
|
#ifndef USE_THREAD_DUMP
|
||||||
|
|
||||||
/* This function dumps all threads' state to the trash. This version is the
|
/* This function dumps all threads' state to the trash. This version is the
|
||||||
@ -271,6 +494,17 @@ REGISTER_PER_THREAD_INIT(init_debug_per_thread);
|
|||||||
|
|
||||||
/* register cli keywords */
|
/* register cli keywords */
|
||||||
static struct cli_kw_list cli_kws = {{ },{
|
static struct cli_kw_list cli_kws = {{ },{
|
||||||
|
#if defined(DEBUG_DEV)
|
||||||
|
{{ "debug", "dev", "close", NULL }, "debug dev close <fd> : close this file descriptor", debug_parse_cli_close, NULL },
|
||||||
|
{{ "debug", "dev", "delay", NULL }, "debug dev delay [ms] : sleep this long", debug_parse_cli_delay, NULL },
|
||||||
|
{{ "debug", "dev", "exec", NULL }, "debug dev exec [cmd] ... : show this command's output", debug_parse_cli_exec, NULL },
|
||||||
|
{{ "debug", "dev", "exit", NULL }, "debug dev exit [code] : immediately exit the process", debug_parse_cli_exit, NULL },
|
||||||
|
{{ "debug", "dev", "hex", NULL }, "debug dev hex <addr> [len]: dump a memory area", debug_parse_cli_hex, NULL },
|
||||||
|
{{ "debug", "dev", "log", NULL }, "debug dev log [msg] ... : send this msg to global logs", debug_parse_cli_log, NULL },
|
||||||
|
{{ "debug", "dev", "loop", NULL }, "debug dev loop [ms] : loop this long", debug_parse_cli_loop, NULL },
|
||||||
|
{{ "debug", "dev", "panic", NULL }, "debug dev panic : immediately trigger a panic", debug_parse_cli_panic, NULL },
|
||||||
|
{{ "debug", "dev", "tkill", NULL }, "debug dev tkill [thr] [sig] : send signal to thread", debug_parse_cli_tkill, NULL },
|
||||||
|
#endif
|
||||||
{ { "show", "threads", NULL }, "show threads : show some threads debugging information", NULL, cli_io_handler_show_threads, NULL },
|
{ { "show", "threads", NULL }, "show threads : show some threads debugging information", NULL, cli_io_handler_show_threads, NULL },
|
||||||
{{},}
|
{{},}
|
||||||
}};
|
}};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user