Force usage of assert() condition when NDEBUG is defined
[lttng-tools.git] / src / common / actions / firing-policy.c
1 /*
2 * Copyright (C) 2021 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only
5 *
6 */
7
8 #include <common/buffer-view.h>
9 #include <common/dynamic-buffer.h>
10 #include <common/error.h>
11 #include <common/macros.h>
12 #include <common/payload-view.h>
13 #include <common/payload.h>
14 #include <limits.h>
15 #include <lttng/action/firing-policy-internal.h>
16 #include <lttng/action/firing-policy.h>
17 #include <stdbool.h>
18 #include <sys/types.h>
19
20 #define IS_EVERY_N_FIRING_POLICY(policy) \
21 (lttng_firing_policy_get_type(policy) == \
22 LTTNG_FIRING_POLICY_TYPE_EVERY_N)
23
24 #define IS_ONCE_AFTER_N_FIRING_POLICY(policy) \
25 (lttng_firing_policy_get_type(policy) == \
26 LTTNG_FIRING_POLICY_TYPE_ONCE_AFTER_N)
27
28 typedef void (*firing_policy_destroy_cb)(
29 struct lttng_firing_policy *firing_policy);
30 typedef int (*firing_policy_serialize_cb)(
31 struct lttng_firing_policy *firing_policy,
32 struct lttng_payload *payload);
33 typedef bool (*firing_policy_equal_cb)(const struct lttng_firing_policy *a,
34 const struct lttng_firing_policy *b);
35 typedef ssize_t (*firing_policy_create_from_payload_cb)(
36 struct lttng_payload_view *view,
37 struct lttng_firing_policy **firing_policy);
38 typedef struct lttng_firing_policy *(*firing_policy_copy_cb)(
39 const struct lttng_firing_policy *source);
40
41 struct lttng_firing_policy {
42 enum lttng_firing_policy_type type;
43 firing_policy_serialize_cb serialize;
44 firing_policy_equal_cb equal;
45 firing_policy_destroy_cb destroy;
46 firing_policy_copy_cb copy;
47 };
48
49 struct lttng_firing_policy_every_n {
50 struct lttng_firing_policy parent;
51 uint64_t interval;
52 };
53
54 struct lttng_firing_policy_once_after_n {
55 struct lttng_firing_policy parent;
56 uint64_t threshold;
57 };
58
59 struct lttng_firing_policy_comm {
60 /* enum lttng_firing_policy_type */
61 int8_t firing_policy_type;
62 } LTTNG_PACKED;
63
64 struct lttng_firing_policy_once_after_n_comm {
65 uint64_t threshold;
66 } LTTNG_PACKED;
67
68 struct lttng_firing_policy_every_n_comm {
69 uint64_t interval;
70 } LTTNG_PACKED;
71
72 /* Forward declaration. */
73 static void lttng_firing_policy_init(struct lttng_firing_policy *firing_policy,
74 enum lttng_firing_policy_type type,
75 firing_policy_serialize_cb serialize,
76 firing_policy_equal_cb equal,
77 firing_policy_destroy_cb destroy,
78 firing_policy_copy_cb copy);
79
80 /* Forward declaration. Every n */
81 static bool lttng_firing_policy_every_n_should_execute(
82 const struct lttng_firing_policy *policy, uint64_t counter);
83
84 /* Forward declaration. Once after N */
85 static bool lttng_firing_policy_once_after_n_should_execute(
86 const struct lttng_firing_policy *policy, uint64_t counter);
87
88 LTTNG_HIDDEN
89 const char *lttng_firing_policy_type_string(
90 enum lttng_firing_policy_type firing_policy_type)
91 {
92 switch (firing_policy_type) {
93 case LTTNG_FIRING_POLICY_TYPE_EVERY_N:
94 return "EVERY-N";
95 case LTTNG_FIRING_POLICY_TYPE_ONCE_AFTER_N:
96 return "ONCE-AFTER-N";
97 default:
98 return "???";
99 }
100 }
101
102 enum lttng_firing_policy_type lttng_firing_policy_get_type(
103 const struct lttng_firing_policy *policy)
104 {
105 return policy ? policy->type : LTTNG_FIRING_POLICY_TYPE_UNKNOWN;
106 }
107
108 LTTNG_HIDDEN
109 void lttng_firing_policy_init(struct lttng_firing_policy *firing_policy,
110 enum lttng_firing_policy_type type,
111 firing_policy_serialize_cb serialize,
112 firing_policy_equal_cb equal,
113 firing_policy_destroy_cb destroy,
114 firing_policy_copy_cb copy)
115 {
116 firing_policy->type = type;
117 firing_policy->serialize = serialize;
118 firing_policy->equal = equal;
119 firing_policy->destroy = destroy;
120 firing_policy->copy = copy;
121 }
122
123 void lttng_firing_policy_destroy(struct lttng_firing_policy *firing_policy)
124 {
125 if (!firing_policy) {
126 return;
127 }
128
129 firing_policy->destroy(firing_policy);
130 }
131
132 LTTNG_HIDDEN
133 int lttng_firing_policy_serialize(struct lttng_firing_policy *firing_policy,
134 struct lttng_payload *payload)
135 {
136 int ret;
137 const struct lttng_firing_policy_comm firing_policy_comm = {
138 .firing_policy_type = (int8_t) firing_policy->type,
139 };
140
141 ret = lttng_dynamic_buffer_append(&payload->buffer, &firing_policy_comm,
142 sizeof(firing_policy_comm));
143 if (ret) {
144 goto end;
145 }
146
147 ret = firing_policy->serialize(firing_policy, payload);
148 if (ret) {
149 goto end;
150 }
151 end:
152 return ret;
153 }
154
155 static ssize_t lttng_firing_policy_once_after_n_create_from_payload(
156 struct lttng_payload_view *view,
157 struct lttng_firing_policy **firing_policy)
158 {
159 ssize_t consumed_len = -1;
160 struct lttng_firing_policy *policy = NULL;
161 const struct lttng_firing_policy_once_after_n_comm *comm;
162 const struct lttng_payload_view comm_view =
163 lttng_payload_view_from_view(view, 0, sizeof(*comm));
164
165 if (!view || !firing_policy) {
166 consumed_len = -1;
167 goto end;
168 }
169
170 if (!lttng_payload_view_is_valid(&comm_view)) {
171 /* Payload not large enough to contain the header. */
172 consumed_len = -1;
173 goto end;
174 }
175
176 comm = (const struct lttng_firing_policy_once_after_n_comm *)
177 comm_view.buffer.data;
178
179 policy = lttng_firing_policy_once_after_n_create(comm->threshold);
180 if (policy == NULL) {
181 consumed_len = -1;
182 goto end;
183 }
184
185 *firing_policy = policy;
186 consumed_len = sizeof(*comm);
187
188 end:
189 return consumed_len;
190 }
191
192 static ssize_t lttng_firing_policy_every_n_create_from_payload(
193 struct lttng_payload_view *view,
194 struct lttng_firing_policy **firing_policy)
195 {
196 ssize_t consumed_len = -1;
197 struct lttng_firing_policy *policy = NULL;
198 const struct lttng_firing_policy_every_n_comm *comm;
199 const struct lttng_payload_view comm_view =
200 lttng_payload_view_from_view(view, 0, sizeof(*comm));
201
202 if (!view || !firing_policy) {
203 consumed_len = -1;
204 goto end;
205 }
206
207 if (!lttng_payload_view_is_valid(&comm_view)) {
208 /* Payload not large enough to contain the header. */
209 consumed_len = -1;
210 goto end;
211 }
212
213 comm = (const struct lttng_firing_policy_every_n_comm *)
214 comm_view.buffer.data;
215
216 policy = lttng_firing_policy_every_n_create(comm->interval);
217 if (policy == NULL) {
218 consumed_len = -1;
219 goto end;
220 }
221
222 *firing_policy = policy;
223 consumed_len = sizeof(*comm);
224
225 end:
226 return consumed_len;
227 }
228
229 LTTNG_HIDDEN
230 ssize_t lttng_firing_policy_create_from_payload(struct lttng_payload_view *view,
231 struct lttng_firing_policy **firing_policy)
232 {
233 ssize_t consumed_len, specific_firing_policy_consumed_len;
234 firing_policy_create_from_payload_cb create_from_payload_cb;
235 const struct lttng_firing_policy_comm *firing_policy_comm;
236 const struct lttng_payload_view firing_policy_comm_view =
237 lttng_payload_view_from_view(
238 view, 0, sizeof(*firing_policy_comm));
239
240 if (!view || !firing_policy) {
241 consumed_len = -1;
242 goto end;
243 }
244
245 if (!lttng_payload_view_is_valid(&firing_policy_comm_view)) {
246 /* Payload not large enough to contain the header. */
247 consumed_len = -1;
248 goto end;
249 }
250
251 firing_policy_comm =
252 (const struct lttng_firing_policy_comm *)
253 firing_policy_comm_view.buffer.data;
254
255 DBG("Create firing_policy from payload: firing-policy-type=%s",
256 lttng_firing_policy_type_string(
257 firing_policy_comm->firing_policy_type));
258
259 switch (firing_policy_comm->firing_policy_type) {
260 case LTTNG_FIRING_POLICY_TYPE_EVERY_N:
261 create_from_payload_cb =
262 lttng_firing_policy_every_n_create_from_payload;
263 break;
264 case LTTNG_FIRING_POLICY_TYPE_ONCE_AFTER_N:
265 create_from_payload_cb =
266 lttng_firing_policy_once_after_n_create_from_payload;
267 break;
268 default:
269 ERR("Failed to create firing-policy from payload, unhandled firing-policy type: firing-policy-type=%u (%s)",
270 firing_policy_comm->firing_policy_type,
271 lttng_firing_policy_type_string(firing_policy_comm->firing_policy_type));
272 consumed_len = -1;
273 goto end;
274 }
275
276 {
277 /*
278 * Create buffer view for the firing_policy-type-specific data.
279 */
280 struct lttng_payload_view specific_firing_policy_view =
281 lttng_payload_view_from_view(view,
282 sizeof(struct lttng_firing_policy_comm),
283 -1);
284
285 specific_firing_policy_consumed_len = create_from_payload_cb(
286 &specific_firing_policy_view, firing_policy);
287 }
288
289 if (specific_firing_policy_consumed_len < 0) {
290 ERR("Failed to create specific firing_policy from buffer");
291 consumed_len = -1;
292 goto end;
293 }
294
295 LTTNG_ASSERT(*firing_policy);
296
297 consumed_len = sizeof(struct lttng_firing_policy_comm) +
298 specific_firing_policy_consumed_len;
299
300 end:
301 return consumed_len;
302 }
303
304 LTTNG_HIDDEN
305 bool lttng_firing_policy_is_equal(const struct lttng_firing_policy *a,
306 const struct lttng_firing_policy *b)
307 {
308 bool is_equal = false;
309
310 if (!a || !b) {
311 goto end;
312 }
313
314 if (a->type != b->type) {
315 goto end;
316 }
317
318 if (a == b) {
319 is_equal = true;
320 goto end;
321 }
322
323 LTTNG_ASSERT(a->equal);
324 is_equal = a->equal(a, b);
325 end:
326 return is_equal;
327 }
328
329 LTTNG_HIDDEN
330 bool lttng_firing_policy_should_execute(
331 const struct lttng_firing_policy *policy, uint64_t counter)
332 {
333 switch (policy->type) {
334 case LTTNG_FIRING_POLICY_TYPE_EVERY_N:
335 return lttng_firing_policy_every_n_should_execute(
336 policy, counter);
337 case LTTNG_FIRING_POLICY_TYPE_ONCE_AFTER_N:
338 return lttng_firing_policy_once_after_n_should_execute(
339 policy, counter);
340 default:
341 abort();
342 break;
343 }
344 }
345
346 /* Every N */
347 static const struct lttng_firing_policy_every_n *
348 firing_policy_every_n_from_firing_policy_const(
349 const struct lttng_firing_policy *policy)
350 {
351 LTTNG_ASSERT(policy);
352
353 return container_of(policy, const struct lttng_firing_policy_every_n,
354 parent);
355 }
356
357 static int lttng_firing_policy_every_n_serialize(
358 struct lttng_firing_policy *policy,
359 struct lttng_payload *payload)
360 {
361 int ret;
362 const struct lttng_firing_policy_every_n *every_n_policy;
363 struct lttng_firing_policy_every_n_comm comm = {};
364
365 LTTNG_ASSERT(policy);
366 LTTNG_ASSERT(payload);
367
368 every_n_policy = firing_policy_every_n_from_firing_policy_const(policy);
369 comm.interval = every_n_policy->interval;
370
371 ret = lttng_dynamic_buffer_append(
372 &payload->buffer, &comm, sizeof(comm));
373 return ret;
374 }
375
376 static bool lttng_firing_policy_every_n_is_equal(
377 const struct lttng_firing_policy *_a,
378 const struct lttng_firing_policy *_b)
379 {
380 bool is_equal = false;
381 const struct lttng_firing_policy_every_n *a, *b;
382
383 a = firing_policy_every_n_from_firing_policy_const(_a);
384 b = firing_policy_every_n_from_firing_policy_const(_b);
385
386 if (a->interval != b->interval) {
387 goto end;
388 }
389
390 is_equal = true;
391
392 end:
393 return is_equal;
394 }
395
396 static void lttng_firing_policy_every_n_destroy(
397 struct lttng_firing_policy *policy)
398 {
399 /* Nothing type-specific to release. */
400 free(policy);
401 }
402
403 static struct lttng_firing_policy *lttng_firing_policy_every_n_copy(
404 const struct lttng_firing_policy *source)
405 {
406 struct lttng_firing_policy *copy = NULL;
407 const struct lttng_firing_policy_every_n *every_n_policy;
408
409 if (!source) {
410 goto end;
411 }
412
413 every_n_policy = firing_policy_every_n_from_firing_policy_const(source);
414 copy = lttng_firing_policy_every_n_create(
415 every_n_policy->interval);
416
417 end:
418 return copy;
419 }
420
421 LTTNG_HIDDEN
422 struct lttng_firing_policy *lttng_firing_policy_every_n_create(
423 uint64_t interval)
424 {
425 struct lttng_firing_policy_every_n *policy = NULL;
426
427 if (interval == 0) {
428 /*
429 * An interval of 0 is invalid since it would never be fired.
430 */
431 goto end;
432 }
433
434 policy = zmalloc(sizeof(struct lttng_firing_policy_every_n));
435 if (!policy) {
436 goto end;
437 }
438
439 lttng_firing_policy_init(&policy->parent,
440 LTTNG_FIRING_POLICY_TYPE_EVERY_N,
441 lttng_firing_policy_every_n_serialize,
442 lttng_firing_policy_every_n_is_equal,
443 lttng_firing_policy_every_n_destroy,
444 lttng_firing_policy_every_n_copy);
445
446 policy->interval = interval;
447
448 end:
449 return policy ? &policy->parent : NULL;
450 }
451
452 LTTNG_HIDDEN
453 enum lttng_firing_policy_status lttng_firing_policy_every_n_get_interval(
454 const struct lttng_firing_policy *policy, uint64_t *interval)
455 {
456 const struct lttng_firing_policy_every_n *every_n_policy;
457 enum lttng_firing_policy_status status;
458
459 if (!policy || !IS_EVERY_N_FIRING_POLICY(policy) || !interval) {
460 status = LTTNG_FIRING_POLICY_STATUS_INVALID;
461 goto end;
462 }
463
464 every_n_policy = firing_policy_every_n_from_firing_policy_const(policy);
465 *interval = every_n_policy->interval;
466 status = LTTNG_FIRING_POLICY_STATUS_OK;
467 end:
468
469 return status;
470 }
471
472 static bool lttng_firing_policy_every_n_should_execute(
473 const struct lttng_firing_policy *policy, uint64_t counter)
474 {
475 const struct lttng_firing_policy_every_n *every_n_policy;
476 LTTNG_ASSERT(policy);
477 bool execute = false;
478
479 every_n_policy = firing_policy_every_n_from_firing_policy_const(policy);
480
481 if (every_n_policy->interval == 0) {
482 abort();
483 }
484
485 execute = (counter % every_n_policy->interval) == 0;
486
487 DBG("Policy every N = %" PRIu64
488 ": execution %s. Execution count: %" PRIu64,
489 every_n_policy->interval,
490 execute ? "accepted" : "denied", counter);
491
492 return execute;
493 }
494
495 /* Once after N */
496
497 static const struct lttng_firing_policy_once_after_n *
498 firing_policy_once_after_n_from_firing_policy_const(
499 const struct lttng_firing_policy *policy)
500 {
501 LTTNG_ASSERT(policy);
502
503 return container_of(policy, struct lttng_firing_policy_once_after_n,
504 parent);
505 }
506
507 static int lttng_firing_policy_once_after_n_serialize(
508 struct lttng_firing_policy *policy,
509 struct lttng_payload *payload)
510 {
511 int ret;
512 const struct lttng_firing_policy_once_after_n *once_after_n_policy;
513 struct lttng_firing_policy_once_after_n_comm comm = {};
514
515 LTTNG_ASSERT(policy);
516 LTTNG_ASSERT(payload);
517
518 once_after_n_policy =
519 firing_policy_once_after_n_from_firing_policy_const(
520 policy);
521 comm.threshold = once_after_n_policy->threshold;
522
523 ret = lttng_dynamic_buffer_append(
524 &payload->buffer, &comm, sizeof(comm));
525 return ret;
526 }
527
528 static bool lttng_firing_policy_once_after_n_is_equal(
529 const struct lttng_firing_policy *_a,
530 const struct lttng_firing_policy *_b)
531 {
532 bool is_equal = false;
533 const struct lttng_firing_policy_once_after_n *a, *b;
534
535 a = firing_policy_once_after_n_from_firing_policy_const(_a);
536 b = firing_policy_once_after_n_from_firing_policy_const(_b);
537
538 if (a->threshold != b->threshold) {
539 goto end;
540 }
541
542 is_equal = true;
543
544 end:
545 return is_equal;
546 }
547
548 static void lttng_firing_policy_once_after_n_destroy(
549 struct lttng_firing_policy *policy)
550 {
551 /* Nothing type specific to release. */
552 free(policy);
553 }
554
555 static struct lttng_firing_policy *lttng_firing_policy_once_after_n_copy(
556 const struct lttng_firing_policy *source)
557 {
558 struct lttng_firing_policy *copy = NULL;
559 const struct lttng_firing_policy_once_after_n *once_after_n_policy;
560
561 if (!source) {
562 goto end;
563 }
564
565 once_after_n_policy =
566 firing_policy_once_after_n_from_firing_policy_const(
567 source);
568 copy = lttng_firing_policy_once_after_n_create(
569 once_after_n_policy->threshold);
570
571 end:
572 return copy;
573 }
574
575 LTTNG_HIDDEN
576 struct lttng_firing_policy *lttng_firing_policy_once_after_n_create(
577 uint64_t threshold)
578 {
579 struct lttng_firing_policy_once_after_n *policy = NULL;
580
581 if (threshold == 0) {
582 /* threshold is expected to be > 0 */
583 goto end;
584 }
585
586 policy = zmalloc(sizeof(struct lttng_firing_policy_once_after_n));
587 if (!policy) {
588 goto end;
589 }
590
591 lttng_firing_policy_init(&policy->parent,
592 LTTNG_FIRING_POLICY_TYPE_ONCE_AFTER_N,
593 lttng_firing_policy_once_after_n_serialize,
594 lttng_firing_policy_once_after_n_is_equal,
595 lttng_firing_policy_once_after_n_destroy,
596 lttng_firing_policy_once_after_n_copy);
597
598 policy->threshold = threshold;
599
600 end:
601 return policy ? &policy->parent : NULL;
602 }
603
604 LTTNG_HIDDEN
605 enum lttng_firing_policy_status lttng_firing_policy_once_after_n_get_threshold(
606 const struct lttng_firing_policy *policy, uint64_t *threshold)
607 {
608 const struct lttng_firing_policy_once_after_n *once_after_n_policy;
609 enum lttng_firing_policy_status status;
610
611 if (!policy || !IS_ONCE_AFTER_N_FIRING_POLICY(policy) || !threshold) {
612 status = LTTNG_FIRING_POLICY_STATUS_INVALID;
613 goto end;
614 }
615
616 once_after_n_policy =
617 firing_policy_once_after_n_from_firing_policy_const(
618 policy);
619 *threshold = once_after_n_policy->threshold;
620 status = LTTNG_FIRING_POLICY_STATUS_OK;
621
622 end:
623 return status;
624 }
625
626 LTTNG_HIDDEN
627 struct lttng_firing_policy *lttng_firing_policy_copy(
628 const struct lttng_firing_policy *source)
629 {
630 LTTNG_ASSERT(source->copy);
631 return source->copy(source);
632 }
633
634 static bool lttng_firing_policy_once_after_n_should_execute(
635 const struct lttng_firing_policy *policy, uint64_t counter)
636 {
637 const struct lttng_firing_policy_once_after_n *once_after_n_policy;
638 bool execute = false;
639 LTTNG_ASSERT(policy);
640
641 once_after_n_policy =
642 firing_policy_once_after_n_from_firing_policy_const(
643 policy);
644
645 execute = counter == once_after_n_policy->threshold;
646
647 DBG("Policy once after N = %" PRIu64
648 ": execution %s. Execution count: %" PRIu64,
649 once_after_n_policy->threshold,
650 execute ? "accepted" : "denied", counter);
651
652 return counter == once_after_n_policy->threshold;
653 }
This page took 0.041766 seconds and 4 git commands to generate.