blob: 8691b65f157cd7911ecc0614b2f288b4087253b4 [file] [log] [blame]
Willy Tarreau4151c752019-08-08 18:21:26 +02001/*
2 * Runtime tracing API
3 *
4 * Copyright (C) 2000-2019 Willy Tarreau - w@1wt.eu
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation, version 2.1
9 * exclusively.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include <common/buffer.h>
22#include <common/compat.h>
23#include <common/config.h>
24#include <common/ist.h>
25#include <common/mini-clist.h>
Willy Tarreau864e8802019-08-08 18:48:12 +020026#include <proto/cli.h>
Willy Tarreau4151c752019-08-08 18:21:26 +020027#include <proto/log.h>
Willy Tarreau864e8802019-08-08 18:48:12 +020028#include <proto/sink.h>
Willy Tarreau4151c752019-08-08 18:21:26 +020029#include <proto/trace.h>
30
31struct list trace_sources = LIST_HEAD_INIT(trace_sources);
Willy Tarreau88ebd402019-08-19 15:55:34 +020032THREAD_LOCAL struct buffer trace_buf = { };
33
34/* allocates the trace buffers. Returns 0 in case of failure. It is safe to
35 * call to call this function multiple times if the size changes.
36 */
37static int alloc_trace_buffers_per_thread()
38{
39 chunk_init(&trace_buf, my_realloc2(trace_buf.area, global.tune.bufsize), global.tune.bufsize);
40 return !!trash.area;
41}
42
43static void free_trace_buffers_per_thread()
44{
45 chunk_destroy(&trace_buf);
46}
Willy Tarreau4151c752019-08-08 18:21:26 +020047
Willy Tarreau88ebd402019-08-19 15:55:34 +020048REGISTER_PER_THREAD_ALLOC(alloc_trace_buffers_per_thread);
49REGISTER_PER_THREAD_FREE(free_trace_buffers_per_thread);
Willy Tarreau864e8802019-08-08 18:48:12 +020050
51struct trace_source *trace_find_source(const char *name)
52{
53 struct trace_source *src;
54 const struct ist iname = ist(name);
55
56 list_for_each_entry(src, &trace_sources, source_link)
57 if (isteq(src->name, iname))
58 return src;
59 return NULL;
60}
61
62const struct trace_event *trace_find_event(const struct trace_event *ev, const char *name)
63{
64 for (; ev && ev->mask; ev++)
65 if (strcmp(ev->name, name) == 0)
66 return ev;
67 return NULL;
68}
69
70/* parse the command, returns 1 if a message is returned, otherwise zero */
71static int cli_parse_trace(char **args, char *payload, struct appctx *appctx, void *private)
72{
73 struct trace_source *src;
74 uint64_t *ev_ptr = NULL;
75
76 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
77 return 1;
78
79 if (!*args[1]) {
80 /* no arg => report the list of supported sources as a warning */
81 chunk_printf(&trash,
82 "Supported trace sources and states (.=stopped, w=waiting, R=running) :\n"
83 " [.] 0 : not a source, will immediately stop all traces\n"
84 );
85
86 list_for_each_entry(src, &trace_sources, source_link)
87 chunk_appendf(&trash, " [%c] %-10s : %s\n", trace_state_char(src->state), src->name.ptr, src->desc);
88
89 trash.area[trash.data] = 0;
90 return cli_msg(appctx, LOG_WARNING, trash.area);
91 }
92
93 if (strcmp(args[1], "0") == 0) {
94 /* emergency stop of all traces */
95 list_for_each_entry(src, &trace_sources, source_link)
96 HA_ATOMIC_STORE(&src->state, TRACE_STATE_STOPPED);
97 return cli_msg(appctx, LOG_NOTICE, "All traces now stopped");
98 }
99
100 src = trace_find_source(args[1]);
101 if (!src)
102 return cli_err(appctx, "No such trace source");
103
104 if (!*args[2]) {
105 return cli_msg(appctx, LOG_WARNING,
106 "Supported commands:\n"
107 " event : list/enable/disable source-specific event reporting\n"
108 //" filter : list/enable/disable generic filters\n"
Willy Tarreauaaaf4112019-08-12 17:57:57 +0200109 " level : list/set detail level\n"
Willy Tarreau864e8802019-08-08 18:48:12 +0200110 //" lock : automatic lock on thread/connection/stream/...\n"
111 " pause : pause and automatically restart after a specific event\n"
112 " sink : list/set event sinks\n"
113 " start : start immediately or after a specific event\n"
114 " stop : stop immediately or after a specific event\n"
115 );
116 }
117 else if ((strcmp(args[2], "event") == 0 && (ev_ptr = &src->report_events)) ||
118 (strcmp(args[2], "pause") == 0 && (ev_ptr = &src->pause_events)) ||
119 (strcmp(args[2], "start") == 0 && (ev_ptr = &src->start_events)) ||
120 (strcmp(args[2], "stop") == 0 && (ev_ptr = &src->stop_events))) {
121 const struct trace_event *ev;
122 const char *name = args[3];
123 int neg = 0;
124 int i;
125
126 /* skip prefix '!', '-', '+' and remind negation */
127 while (*name) {
128 if (*name == '!' || *name == '-')
129 neg = 1;
130 else if (*name == '+')
131 neg = 0;
132 else
133 break;
134 name++;
135 }
136
137 if (!*name) {
138 chunk_printf(&trash, "Supported events for source %s (+=enabled, -=disabled):\n", src->name.ptr);
139 if (ev_ptr != &src->report_events)
140 chunk_appendf(&trash, " - now : don't wait for events, immediately change the state\n");
141 chunk_appendf(&trash, " - none : disable all event types\n");
142 chunk_appendf(&trash, " - any : enable all event types\n");
143 for (i = 0; src->known_events && src->known_events[i].mask; i++) {
144 chunk_appendf(&trash, " %c %-10s : %s\n",
145 trace_event_char(*ev_ptr, src->known_events[i].mask),
146 src->known_events[i].name, src->known_events[i].desc);
147 }
148 trash.area[trash.data] = 0;
149 return cli_msg(appctx, LOG_WARNING, trash.area);
150 }
151
152 if (strcmp(name, "now") == 0 && ev_ptr != &src->report_events) {
153 HA_ATOMIC_STORE(ev_ptr, 0);
154 if (ev_ptr == &src->pause_events)
155 HA_ATOMIC_STORE(&src->state, TRACE_STATE_WAITING);
156 else if (ev_ptr == &src->start_events)
157 HA_ATOMIC_STORE(&src->state, TRACE_STATE_RUNNING);
158 else if (ev_ptr == &src->stop_events)
159 HA_ATOMIC_STORE(&src->state, TRACE_STATE_STOPPED);
160 return 0;
161 }
162
163 if (strcmp(name, "none") == 0)
164 HA_ATOMIC_STORE(ev_ptr, 0);
165 else if (strcmp(name, "any") == 0)
166 HA_ATOMIC_STORE(ev_ptr, ~0);
167 else {
168 ev = trace_find_event(src->known_events, name);
169 if (!ev)
170 return cli_err(appctx, "No such trace event");
171
172 if (!neg)
173 HA_ATOMIC_OR(ev_ptr, ev->mask);
174 else
175 HA_ATOMIC_AND(ev_ptr, ~ev->mask);
176 }
177 }
178 else if (strcmp(args[2], "sink") == 0) {
179 const char *name = args[3];
180 struct sink *sink;
181
182 if (!*name) {
183 chunk_printf(&trash, "Supported sinks for source %s (*=current):\n", src->name.ptr);
184 chunk_appendf(&trash, " %c none : no sink\n", src->sink ? ' ' : '*');
185 list_for_each_entry(sink, &sink_list, sink_list) {
186 chunk_appendf(&trash, " %c %-10s : %s\n",
187 src->sink == sink ? '*' : ' ',
188 sink->name, sink->desc);
189 }
190 trash.area[trash.data] = 0;
191 return cli_msg(appctx, LOG_WARNING, trash.area);
192 }
193
194 if (strcmp(name, "none") == 0)
195 sink = NULL;
196 else {
197 sink = sink_find(name);
198 if (!sink)
199 return cli_err(appctx, "No such sink");
200 }
201
202 HA_ATOMIC_STORE(&src->sink, sink);
203 }
Willy Tarreauaaaf4112019-08-12 17:57:57 +0200204 else if (strcmp(args[2], "level") == 0) {
205 const char *name = args[3];
206
207 if (!*name) {
208 chunk_printf(&trash, "Supported detail levels for source %s:\n", src->name.ptr);
209 chunk_appendf(&trash, " %c user : information useful to the end user\n",
210 src->level == TRACE_LEVEL_USER ? '*' : ' ');
211 chunk_appendf(&trash, " %c payload : add information relevant to the payload\n",
212 src->level == TRACE_LEVEL_PAYLOAD ? '*' : ' ');
213 chunk_appendf(&trash, " %c proto : add information relevant to the protocol\n",
214 src->level == TRACE_LEVEL_PROTO ? '*' : ' ');
215 chunk_appendf(&trash, " %c state : add information relevant to the state machine\n",
216 src->level == TRACE_LEVEL_STATE ? '*' : ' ');
217 chunk_appendf(&trash, " %c developer : add information useful only to the developer\n",
218 src->level == TRACE_LEVEL_DEVELOPER ? '*' : ' ');
219 trash.area[trash.data] = 0;
220 return cli_msg(appctx, LOG_WARNING, trash.area);
221 }
222
223 if (strcmp(name, "user") == 0)
224 HA_ATOMIC_STORE(&src->level, TRACE_LEVEL_USER);
225 else if (strcmp(name, "payload") == 0)
226 HA_ATOMIC_STORE(&src->level, TRACE_LEVEL_PAYLOAD);
227 else if (strcmp(name, "proto") == 0)
228 HA_ATOMIC_STORE(&src->level, TRACE_LEVEL_PROTO);
229 else if (strcmp(name, "state") == 0)
230 HA_ATOMIC_STORE(&src->level, TRACE_LEVEL_STATE);
231 else if (strcmp(name, "developer") == 0)
232 HA_ATOMIC_STORE(&src->level, TRACE_LEVEL_DEVELOPER);
233 else
234 return cli_err(appctx, "No such trace level");
235 }
Willy Tarreau864e8802019-08-08 18:48:12 +0200236 else
237 return cli_err(appctx, "Unknown trace keyword");
238
239 return 0;
240}
241
Willy Tarreau85b15752019-08-12 16:44:33 +0200242/* parse the command, returns 1 if a message is returned, otherwise zero */
243static int cli_parse_show_trace(char **args, char *payload, struct appctx *appctx, void *private)
244{
245 struct trace_source *src;
246 const struct sink *sink;
247 int i;
248
249 args++; // make args[1] the 1st arg
250
251 if (!*args[1]) {
252 /* no arg => report the list of supported sources */
253 chunk_printf(&trash,
254 "Supported trace sources and states (.=stopped, w=waiting, R=running) :\n"
255 );
256
257 list_for_each_entry(src, &trace_sources, source_link) {
258 sink = src->sink;
259 chunk_appendf(&trash, " [%c] %-10s -> %s [drp %u] [%s]\n",
260 trace_state_char(src->state), src->name.ptr,
261 sink ? sink->name : "none",
262 sink ? sink->ctx.dropped : 0,
263 src->desc);
264 }
265
266 trash.area[trash.data] = 0;
267 return cli_msg(appctx, LOG_INFO, trash.area);
268 }
269
270 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
271 return 1;
272
273 src = trace_find_source(args[1]);
274 if (!src)
275 return cli_err(appctx, "No such trace source");
276
277 sink = src->sink;
278 chunk_printf(&trash, "Trace status for %s:\n", src->name.ptr);
279 chunk_appendf(&trash, " - sink: %s [%u dropped]\n",
280 sink ? sink->name : "none", sink ? sink->ctx.dropped : 0);
281
282 chunk_appendf(&trash, " - event name : report start stop pause\n");
283 for (i = 0; src->known_events && src->known_events[i].mask; i++) {
284 chunk_appendf(&trash, " %-10s : %c %c %c %c\n",
285 src->known_events[i].name,
286 trace_event_char(src->report_events, src->known_events[i].mask),
287 trace_event_char(src->start_events, src->known_events[i].mask),
288 trace_event_char(src->stop_events, src->known_events[i].mask),
289 trace_event_char(src->pause_events, src->known_events[i].mask));
290 }
291
292 trash.area[trash.data] = 0;
293 return cli_msg(appctx, LOG_WARNING, trash.area);
294}
295
Willy Tarreau864e8802019-08-08 18:48:12 +0200296static struct cli_kw_list cli_kws = {{ },{
297 { { "trace", NULL }, "trace <module> [cmd [args...]] : manage live tracing", cli_parse_trace, NULL, NULL },
Willy Tarreau85b15752019-08-12 16:44:33 +0200298 { { "show", "trace", NULL }, "show trace [<module>] : show live tracing state", cli_parse_show_trace, NULL, NULL },
Willy Tarreau864e8802019-08-08 18:48:12 +0200299 {{},}
300}};
301
302INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
303
Willy Tarreau4151c752019-08-08 18:21:26 +0200304/*
305 * Local variables:
306 * c-indent-level: 8
307 * c-basic-offset: 8
308 * End:
309 */