From 864e880f6c1a3f1e356b9f7f5f71a75b73f7f9c8 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Thu, 8 Aug 2019 18:48:12 +0200 Subject: [PATCH] MINOR: trace/cli: register the "trace" CLI keyword to list the sources For now it lists the sources if one is not provided, and checks for the source's existence. It lists the events if not provided, checks for their existence if provided, and adjusts reported events/start/stop/pause events, and performs state transitions. It lists sinks and adjusts them as well. Filters, lock, and level are not implemented yet. --- include/proto/trace.h | 14 ++++ src/trace.c | 169 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) diff --git a/include/proto/trace.h b/include/proto/trace.h index a29923796..24d260155 100644 --- a/include/proto/trace.h +++ b/include/proto/trace.h @@ -34,6 +34,20 @@ extern struct list trace_sources; extern THREAD_LOCAL struct buffer trace_buf; +/* return a single char to describe a trace state */ +static inline char trace_state_char(enum trace_state st) +{ + return (st == TRACE_STATE_RUNNING) ? 'R' : + (st == TRACE_STATE_WAITING) ? 'w' : + '.'; +} + +/* return a single char to describe an event state */ +static inline char trace_event_char(uint64_t conf, uint64_t ev) +{ + return (conf & ev) ? '+' : '-'; +} + /* registers trace source . Modifies the list element! * The {start,pause,stop,report} events are not changed so the source may * preset them. diff --git a/src/trace.c b/src/trace.c index e77c3080e..dd59abe51 100644 --- a/src/trace.c +++ b/src/trace.c @@ -23,7 +23,9 @@ #include #include #include +#include #include +#include #include struct list trace_sources = LIST_HEAD_INIT(trace_sources); @@ -45,6 +47,173 @@ static void free_trace_buffers_per_thread() REGISTER_PER_THREAD_ALLOC(alloc_trace_buffers_per_thread); REGISTER_PER_THREAD_FREE(free_trace_buffers_per_thread); + +struct trace_source *trace_find_source(const char *name) +{ + struct trace_source *src; + const struct ist iname = ist(name); + + list_for_each_entry(src, &trace_sources, source_link) + if (isteq(src->name, iname)) + return src; + return NULL; +} + +const struct trace_event *trace_find_event(const struct trace_event *ev, const char *name) +{ + for (; ev && ev->mask; ev++) + if (strcmp(ev->name, name) == 0) + return ev; + return NULL; +} + +/* parse the command, returns 1 if a message is returned, otherwise zero */ +static int cli_parse_trace(char **args, char *payload, struct appctx *appctx, void *private) +{ + struct trace_source *src; + uint64_t *ev_ptr = NULL; + + if (!cli_has_level(appctx, ACCESS_LVL_OPER)) + return 1; + + if (!*args[1]) { + /* no arg => report the list of supported sources as a warning */ + chunk_printf(&trash, + "Supported trace sources and states (.=stopped, w=waiting, R=running) :\n" + " [.] 0 : not a source, will immediately stop all traces\n" + ); + + list_for_each_entry(src, &trace_sources, source_link) + chunk_appendf(&trash, " [%c] %-10s : %s\n", trace_state_char(src->state), src->name.ptr, src->desc); + + trash.area[trash.data] = 0; + return cli_msg(appctx, LOG_WARNING, trash.area); + } + + if (strcmp(args[1], "0") == 0) { + /* emergency stop of all traces */ + list_for_each_entry(src, &trace_sources, source_link) + HA_ATOMIC_STORE(&src->state, TRACE_STATE_STOPPED); + return cli_msg(appctx, LOG_NOTICE, "All traces now stopped"); + } + + src = trace_find_source(args[1]); + if (!src) + return cli_err(appctx, "No such trace source"); + + if (!*args[2]) { + return cli_msg(appctx, LOG_WARNING, + "Supported commands:\n" + " event : list/enable/disable source-specific event reporting\n" + //" filter : list/enable/disable generic filters\n" + //" level : list/set detail level\n" + //" lock : automatic lock on thread/connection/stream/...\n" + " pause : pause and automatically restart after a specific event\n" + " sink : list/set event sinks\n" + " start : start immediately or after a specific event\n" + " stop : stop immediately or after a specific event\n" + ); + } + else if ((strcmp(args[2], "event") == 0 && (ev_ptr = &src->report_events)) || + (strcmp(args[2], "pause") == 0 && (ev_ptr = &src->pause_events)) || + (strcmp(args[2], "start") == 0 && (ev_ptr = &src->start_events)) || + (strcmp(args[2], "stop") == 0 && (ev_ptr = &src->stop_events))) { + const struct trace_event *ev; + const char *name = args[3]; + int neg = 0; + int i; + + /* skip prefix '!', '-', '+' and remind negation */ + while (*name) { + if (*name == '!' || *name == '-') + neg = 1; + else if (*name == '+') + neg = 0; + else + break; + name++; + } + + if (!*name) { + chunk_printf(&trash, "Supported events for source %s (+=enabled, -=disabled):\n", src->name.ptr); + if (ev_ptr != &src->report_events) + chunk_appendf(&trash, " - now : don't wait for events, immediately change the state\n"); + chunk_appendf(&trash, " - none : disable all event types\n"); + chunk_appendf(&trash, " - any : enable all event types\n"); + for (i = 0; src->known_events && src->known_events[i].mask; i++) { + chunk_appendf(&trash, " %c %-10s : %s\n", + trace_event_char(*ev_ptr, src->known_events[i].mask), + src->known_events[i].name, src->known_events[i].desc); + } + trash.area[trash.data] = 0; + return cli_msg(appctx, LOG_WARNING, trash.area); + } + + if (strcmp(name, "now") == 0 && ev_ptr != &src->report_events) { + HA_ATOMIC_STORE(ev_ptr, 0); + if (ev_ptr == &src->pause_events) + HA_ATOMIC_STORE(&src->state, TRACE_STATE_WAITING); + else if (ev_ptr == &src->start_events) + HA_ATOMIC_STORE(&src->state, TRACE_STATE_RUNNING); + else if (ev_ptr == &src->stop_events) + HA_ATOMIC_STORE(&src->state, TRACE_STATE_STOPPED); + return 0; + } + + if (strcmp(name, "none") == 0) + HA_ATOMIC_STORE(ev_ptr, 0); + else if (strcmp(name, "any") == 0) + HA_ATOMIC_STORE(ev_ptr, ~0); + else { + ev = trace_find_event(src->known_events, name); + if (!ev) + return cli_err(appctx, "No such trace event"); + + if (!neg) + HA_ATOMIC_OR(ev_ptr, ev->mask); + else + HA_ATOMIC_AND(ev_ptr, ~ev->mask); + } + } + else if (strcmp(args[2], "sink") == 0) { + const char *name = args[3]; + struct sink *sink; + + if (!*name) { + chunk_printf(&trash, "Supported sinks for source %s (*=current):\n", src->name.ptr); + chunk_appendf(&trash, " %c none : no sink\n", src->sink ? ' ' : '*'); + list_for_each_entry(sink, &sink_list, sink_list) { + chunk_appendf(&trash, " %c %-10s : %s\n", + src->sink == sink ? '*' : ' ', + sink->name, sink->desc); + } + trash.area[trash.data] = 0; + return cli_msg(appctx, LOG_WARNING, trash.area); + } + + if (strcmp(name, "none") == 0) + sink = NULL; + else { + sink = sink_find(name); + if (!sink) + return cli_err(appctx, "No such sink"); + } + + HA_ATOMIC_STORE(&src->sink, sink); + } + else + return cli_err(appctx, "Unknown trace keyword"); + + return 0; +} + +static struct cli_kw_list cli_kws = {{ },{ + { { "trace", NULL }, "trace [cmd [args...]] : manage live tracing", cli_parse_trace, NULL, NULL }, + {{},} +}}; + +INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws); + /* * Local variables: * c-indent-level: 8