blob: dd59abe5156bb36a09647afdbc47937131625c93 [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"
109 //" level : list/set detail level\n"
110 //" 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 }
204 else
205 return cli_err(appctx, "Unknown trace keyword");
206
207 return 0;
208}
209
210static struct cli_kw_list cli_kws = {{ },{
211 { { "trace", NULL }, "trace <module> [cmd [args...]] : manage live tracing", cli_parse_trace, NULL, NULL },
212 {{},}
213}};
214
215INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
216
Willy Tarreau4151c752019-08-08 18:21:26 +0200217/*
218 * Local variables:
219 * c-indent-level: 8
220 * c-basic-offset: 8
221 * End:
222 */