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