SoW-2019-0007-2: Dynamic Snapshot: Triggers send partial event payload with notifications
[lttng-ust.git] / liblttng-ust / trigger-notification.c
CommitLineData
cb11f03a
FD
1/*
2 * trigger-notification.c
3 *
4 * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
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; only
9 * version 2.1 of the License.
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#define _GNU_SOURCE
22#define _LGPL_SOURCE
23
24#include <assert.h>
25#include <byteswap.h>
26#include <errno.h>
27#include <lttng/ust-events.h>
28#include <usterr-signal-safe.h>
29
30#include "../libmsgpack/msgpack.h"
31#include "lttng-bytecode.h"
32#include "share.h"
33
34/*
35 * We want this write to be atomic AND non-blocking, meaning that we
36 * want to write either everything OR nothing.
37 * According to `pipe(7)`, writes that are less than `PIPE_BUF` bytes must be
38 * atomic, so we bound the capture buffer size to the `PIPE_BUF` minus the size
39 * of the notification struct we are sending alongside the capture buffer.
40 */
41#define CAPTURE_BUFFER_SIZE \
42 (PIPE_BUF - sizeof(struct lttng_ust_trigger_notification) - 1)
43
44struct lttng_trigger_notification {
45 int notification_fd;
46 uint64_t trigger_id;
47 uint8_t capture_buf[CAPTURE_BUFFER_SIZE];
48 struct lttng_msgpack_writer writer;
49 bool has_captures;
50};
51
52static
53void capture_enum(struct lttng_msgpack_writer *writer,
54 struct lttng_interpreter_output *output)
55{
56 lttng_msgpack_begin_map(writer, 2);
57 lttng_msgpack_write_str(writer, "type");
58 lttng_msgpack_write_str(writer, "enum");
59
60 lttng_msgpack_write_str(writer, "value");
61
62 switch (output->type) {
63 case LTTNG_INTERPRETER_TYPE_SIGNED_ENUM:
64 lttng_msgpack_write_signed_integer(writer, output->u.s);
65 break;
66 case LTTNG_INTERPRETER_TYPE_UNSIGNED_ENUM:
67 lttng_msgpack_write_signed_integer(writer, output->u.u);
68 break;
69 default:
70 abort();
71 }
72
73 lttng_msgpack_end_map(writer);
74}
75
76static
77int64_t capture_sequence_element_signed(uint8_t *ptr,
78 const struct lttng_integer_type *type)
79{
80 int64_t value;
81 unsigned int size = type->size;
82 bool byte_order_reversed = type->reverse_byte_order;
83
84 switch (size) {
85 case 8:
86 value = *ptr;
87 break;
88 case 16:
89 {
90 int16_t tmp;
91 tmp = *(int16_t *) ptr;
92 if (byte_order_reversed)
93 tmp = bswap_16(tmp);
94
95 value = tmp;
96 break;
97 }
98 case 32:
99 {
100 int32_t tmp;
101 tmp = *(int32_t *) ptr;
102 if (byte_order_reversed)
103 tmp = bswap_32(tmp);
104
105 value = tmp;
106 break;
107 }
108 case 64:
109 {
110 int64_t tmp;
111 tmp = *(int64_t *) ptr;
112 if (byte_order_reversed)
113 tmp = bswap_64(tmp);
114
115 value = tmp;
116 break;
117 }
118 default:
119 abort();
120 }
121
122 return value;
123}
124
125static
126uint64_t capture_sequence_element_unsigned(uint8_t *ptr,
127 const struct lttng_integer_type *type)
128{
129 uint64_t value;
130 unsigned int size = type->size;
131 bool byte_order_reversed = type->reverse_byte_order;
132
133 switch (size) {
134 case 8:
135 value = *ptr;
136 break;
137 case 16:
138 {
139 uint16_t tmp;
140 tmp = *(uint16_t *) ptr;
141 if (byte_order_reversed)
142 tmp = bswap_16(tmp);
143
144 value = tmp;
145 break;
146 }
147 case 32:
148 {
149 uint32_t tmp;
150 tmp = *(uint32_t *) ptr;
151 if (byte_order_reversed)
152 tmp = bswap_32(tmp);
153
154 value = tmp;
155 break;
156 }
157 case 64:
158 {
159 uint64_t tmp;
160 tmp = *(uint64_t *) ptr;
161 if (byte_order_reversed)
162 tmp = bswap_64(tmp);
163
164 value = tmp;
165 break;
166 }
167 default:
168 abort();
169 }
170
171 return value;
172}
173
174static
175void capture_sequence(struct lttng_msgpack_writer *writer,
176 struct lttng_interpreter_output *output)
177{
178 const struct lttng_integer_type *integer_type;
179 const struct lttng_type *nested_type;
180 uint8_t *ptr;
181 bool signedness;
182 int i;
183
184 lttng_msgpack_begin_array(writer, output->u.sequence.nr_elem);
185
186 ptr = (uint8_t *) output->u.sequence.ptr;
187 nested_type = output->u.sequence.nested_type;
188 switch (nested_type->atype) {
189 case atype_integer:
190 integer_type = &nested_type->u.integer;
191 break;
192 case atype_enum:
193 /* Treat enumeration as an integer. */
194 integer_type = &nested_type->u.enum_nestable.container_type->u.integer;
195 break;
196 default:
197 /* Capture of array of non-integer are not supported. */
198 abort();
199 }
200 signedness = integer_type->signedness;
201 for (i = 0; i < output->u.sequence.nr_elem; i++) {
202 if (signedness) {
203 lttng_msgpack_write_signed_integer(writer,
204 capture_sequence_element_signed(ptr, integer_type));
205 } else {
206 lttng_msgpack_write_unsigned_integer(writer,
207 capture_sequence_element_unsigned(ptr, integer_type));
208 }
209
210 /*
211 * We assume that alignment is smaller or equal to the size.
212 * This currently holds true but if it changes in the future,
213 * we will want to change the pointer arithmetics below to
214 * take into account that the next element might be further
215 * away.
216 */
217 assert(integer_type->alignment <= integer_type->size);
218
219 /* Size is in number of bits. */
220 ptr += (integer_type->size / CHAR_BIT) ;
221 }
222
223 lttng_msgpack_end_array(writer);
224}
225
226static
227void notification_init(struct lttng_trigger_notification *notif,
228 struct lttng_trigger *trigger)
229{
230 struct lttng_msgpack_writer *writer = &notif->writer;
231
232 notif->trigger_id = trigger->id;
233 notif->notification_fd = trigger->group->notification_fd;
234 notif->has_captures = false;
235
236 if (trigger->num_captures > 0) {
237 lttng_msgpack_writer_init(writer, notif->capture_buf,
238 CAPTURE_BUFFER_SIZE);
239
240 lttng_msgpack_begin_array(writer, trigger->num_captures);
241 notif->has_captures = true;
242 }
243}
244
245static
246void notification_append_capture(
247 struct lttng_trigger_notification *notif,
248 struct lttng_interpreter_output *output)
249{
250 struct lttng_msgpack_writer *writer = &notif->writer;
251
252 switch (output->type) {
253 case LTTNG_INTERPRETER_TYPE_S64:
254 lttng_msgpack_write_signed_integer(writer, output->u.s);
255 break;
256 case LTTNG_INTERPRETER_TYPE_U64:
257 lttng_msgpack_write_unsigned_integer(writer, output->u.u);
258 break;
259 case LTTNG_INTERPRETER_TYPE_DOUBLE:
260 lttng_msgpack_write_double(writer, output->u.d);
261 break;
262 case LTTNG_INTERPRETER_TYPE_STRING:
263 lttng_msgpack_write_str(writer, output->u.str.str);
264 break;
265 case LTTNG_INTERPRETER_TYPE_SEQUENCE:
266 capture_sequence(writer, output);
267 break;
268 case LTTNG_INTERPRETER_TYPE_SIGNED_ENUM:
269 case LTTNG_INTERPRETER_TYPE_UNSIGNED_ENUM:
270 capture_enum(writer, output);
271 break;
272 default:
273 abort();
274 }
275}
276
277static
278void notification_append_empty_capture(
279 struct lttng_trigger_notification *notif)
280{
281 lttng_msgpack_write_nil(&notif->writer);
282}
283
284static
285void notification_send(struct lttng_trigger_notification *notif)
286{
287 ssize_t ret;
288 size_t content_len;
289 int iovec_count = 1;
290 struct lttng_ust_trigger_notification ust_notif;
291 struct iovec iov[2];
292
293 assert(notif);
294
295 ust_notif.id = notif->trigger_id;
296
297 /*
298 * Prepare sending the notification from multiple buffers using an
299 * array of `struct iovec`. The first buffer of the vector is
300 * notification structure itself and is always present.
301 */
302 iov[0].iov_base = &ust_notif;
303 iov[0].iov_len = sizeof(ust_notif);
304
305 if (notif->has_captures) {
306 /*
307 * If captures were requested, the second buffer of the array
308 * is the capture buffer.
309 */
310 assert(notif->writer.buffer);
311 content_len = notif->writer.write_pos - notif->writer.buffer;
312
313 assert(content_len > 0 && content_len <= CAPTURE_BUFFER_SIZE);
314
315 iov[1].iov_base = notif->capture_buf;
316 iov[1].iov_len = content_len;
317
318 iovec_count++;
319 } else {
320 content_len = 0;
321 }
322
323 /*
324 * Update the capture buffer size so that receiver of the buffer will
325 * know how much to expect.
326 */
327 ust_notif.capture_buf_size = content_len;
328
329 /* Send all the buffers. */
330 ret = patient_writev(notif->notification_fd, iov, iovec_count);
331 if (ret == -1) {
332 if (errno == EAGAIN) {
333 DBG("Cannot send trigger notification without blocking: %s",
334 strerror(errno));
335 } else {
336 DBG("Error to sending trigger notification: %s",
337 strerror(errno));
338 abort();
339 }
340 }
341}
342
343void lttng_trigger_notification_send(struct lttng_trigger *trigger,
344 const char *stack_data)
345{
346 /*
347 * This function is called from the probe, we must do dynamic
348 * allocation in this context.
349 */
350 struct lttng_trigger_notification notif = {0};
351
352 notification_init(&notif, trigger);
353
354 if (caa_unlikely(!cds_list_empty(&trigger->capture_bytecode_runtime_head))) {
355 struct lttng_bytecode_runtime *capture_bc_runtime;
356
357 /*
358 * Iterate over all the capture bytecodes. If the interpreter
359 * functions returns successfully, append the value of the
360 * `output` parameter to the capture buffer. If the interpreter
361 * fails, append an empty capture to the buffer.
362 */
363 cds_list_for_each_entry(capture_bc_runtime,
364 &trigger->capture_bytecode_runtime_head, node) {
365 struct lttng_interpreter_output output;
366
367 if (capture_bc_runtime->interpreter_funcs.capture(capture_bc_runtime,
368 stack_data, &output) & LTTNG_INTERPRETER_RECORD_FLAG)
369 notification_append_capture(&notif, &output);
370 else
371 notification_append_empty_capture(&notif);
372 }
373 }
374
375 /*
376 * Send the notification (including the capture buffer) to the
377 * sessiond.
378 */
379 notification_send(&notif);
380}
This page took 0.062172 seconds and 5 git commands to generate.