Commit | Line | Data |
---|---|---|
5024c2ac JR |
1 | /* |
2 | * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
3 | * | |
4 | * SPDX-License-Identifier: MIT | |
5 | * | |
6 | */ | |
7 | ||
8 | #include <lttng/condition/event-rule.h> | |
9 | #include <lttng/lttng.h> | |
10 | ||
11 | #include <assert.h> | |
12 | #include <inttypes.h> | |
13 | #include <stdbool.h> | |
14 | #include <stddef.h> | |
15 | #include <stdio.h> | |
16 | #include <stdlib.h> | |
17 | #include <string.h> | |
18 | #include <sys/time.h> | |
19 | #include <time.h> | |
20 | ||
21 | static int print_capture(const struct lttng_event_field_value *capture, | |
22 | unsigned int indent_level); | |
23 | static int print_array(const struct lttng_event_field_value *array, | |
24 | unsigned int indent_level); | |
25 | ||
26 | static void indent(unsigned int indentation_level) | |
27 | { | |
28 | unsigned int i; | |
29 | for (i = 0; i < indentation_level; i++) { | |
30 | printf(" "); | |
31 | } | |
32 | } | |
33 | ||
34 | static bool action_group_contains_notify( | |
35 | const struct lttng_action *action_group) | |
36 | { | |
37 | unsigned int i, count; | |
38 | enum lttng_action_status status = | |
39 | lttng_action_group_get_count(action_group, &count); | |
40 | ||
41 | if (status != LTTNG_ACTION_STATUS_OK) { | |
42 | printf("Failed to get action count from action group\n"); | |
43 | exit(1); | |
44 | } | |
45 | ||
46 | for (i = 0; i < count; i++) { | |
47 | const struct lttng_action *action = | |
48 | lttng_action_group_get_at_index_const( | |
49 | action_group, i); | |
50 | const enum lttng_action_type action_type = | |
51 | lttng_action_get_type(action); | |
52 | ||
53 | if (action_type == LTTNG_ACTION_TYPE_NOTIFY) { | |
54 | return true; | |
55 | } | |
56 | } | |
57 | return false; | |
58 | } | |
59 | ||
60 | static int print_capture(const struct lttng_event_field_value *capture, | |
61 | unsigned int indent_level) | |
62 | { | |
63 | int ret = 0; | |
64 | enum lttng_event_field_value_status event_field_status; | |
65 | enum lttng_event_field_value_type type; | |
66 | uint64_t u_val; | |
67 | int64_t s_val; | |
68 | double d_val; | |
69 | const char *string_val = NULL; | |
70 | ||
71 | indent(indent_level); | |
72 | ||
73 | switch (lttng_event_field_value_get_type(capture)) { | |
74 | case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT: | |
75 | { | |
76 | event_field_status = | |
77 | lttng_event_field_value_unsigned_int_get_value( | |
78 | capture, &u_val); | |
79 | if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { | |
80 | ret = 1; | |
81 | goto end; | |
82 | } | |
83 | ||
84 | printf("Unsigned int: %" PRIu64, u_val); | |
85 | break; | |
86 | } | |
87 | case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT: | |
88 | { | |
89 | event_field_status = | |
90 | lttng_event_field_value_signed_int_get_value( | |
91 | capture, &s_val); | |
92 | if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { | |
93 | ret = 1; | |
94 | goto end; | |
95 | } | |
96 | ||
97 | printf("Signed int: %" PRId64, s_val); | |
98 | break; | |
99 | } | |
100 | case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: | |
101 | { | |
102 | event_field_status = | |
103 | lttng_event_field_value_unsigned_int_get_value( | |
104 | capture, &u_val); | |
105 | if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { | |
106 | ret = 1; | |
107 | goto end; | |
108 | } | |
109 | ||
110 | printf("Unsigned enum: %" PRIu64, u_val); | |
111 | break; | |
112 | } | |
113 | case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: | |
114 | { | |
115 | event_field_status = | |
116 | lttng_event_field_value_signed_int_get_value( | |
117 | capture, &s_val); | |
118 | if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { | |
119 | ret = 1; | |
120 | goto end; | |
121 | } | |
122 | ||
123 | printf("Signed enum: %" PRId64, s_val); | |
124 | break; | |
125 | } | |
126 | case LTTNG_EVENT_FIELD_VALUE_TYPE_REAL: | |
127 | { | |
128 | event_field_status = lttng_event_field_value_real_get_value( | |
129 | capture, &d_val); | |
130 | if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { | |
131 | ret = 1; | |
132 | goto end; | |
133 | } | |
134 | ||
135 | printf("Real: %lf", d_val); | |
136 | break; | |
137 | } | |
138 | case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING: | |
139 | { | |
140 | string_val = lttng_event_field_value_string_get_value(capture); | |
141 | if (string_val == NULL) { | |
142 | ret = 1; | |
143 | goto end; | |
144 | } | |
145 | ||
146 | printf("String: %s", string_val); | |
147 | break; | |
148 | } | |
149 | case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY: | |
150 | printf("Array: [\n"); | |
151 | print_array(capture, indent_level); | |
152 | indent(indent_level); | |
153 | printf("]\n"); | |
154 | break; | |
155 | case LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN: | |
156 | case LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID: | |
157 | default: | |
158 | ret = 1; | |
159 | break; | |
160 | } | |
161 | ||
162 | end: | |
163 | return ret; | |
164 | } | |
165 | ||
166 | static void print_unavailabe(unsigned int indent_level) | |
167 | { | |
168 | indent(indent_level); | |
169 | printf("CAPTURE UNAVAILABE"); | |
170 | } | |
171 | ||
172 | static int print_array(const struct lttng_event_field_value *array, | |
173 | unsigned int indent_level) | |
174 | { | |
175 | int ret = 0; | |
176 | enum lttng_event_field_value_status event_field_status; | |
177 | unsigned int captured_field_count; | |
178 | ||
179 | event_field_status = lttng_event_field_value_array_get_length( | |
180 | array, &captured_field_count); | |
181 | if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { | |
182 | ret = 1; | |
183 | goto end; | |
184 | } | |
185 | ||
186 | for (unsigned int i = 0; i < captured_field_count; i++) { | |
187 | const struct lttng_event_field_value *captured_field = NULL; | |
188 | event_field_status = | |
189 | lttng_event_field_value_array_get_element_at_index( | |
190 | array, i, &captured_field); | |
191 | if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { | |
192 | if (event_field_status == | |
193 | LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE) { | |
194 | print_unavailabe(indent_level + 1); | |
195 | } else { | |
196 | ret = 1; | |
197 | goto end; | |
198 | } | |
199 | } | |
200 | print_capture(captured_field, indent_level + 1); | |
201 | ||
202 | if (i + 1 < captured_field_count) { | |
203 | printf(","); | |
204 | } | |
205 | printf("\n"); | |
206 | } | |
207 | ||
208 | end: | |
209 | return ret; | |
210 | } | |
211 | ||
212 | static int print_captures(struct lttng_notification *notification) | |
213 | { | |
214 | int ret = 0; | |
215 | const struct lttng_evaluation *evaluation = | |
216 | lttng_notification_get_evaluation(notification); | |
217 | const struct lttng_condition *condition = | |
218 | lttng_notification_get_condition(notification); | |
219 | ||
220 | /* Status */ | |
221 | enum lttng_condition_status condition_status; | |
222 | enum lttng_evaluation_status evaluation_status; | |
223 | enum lttng_event_field_value_status event_field_status; | |
224 | ||
225 | const struct lttng_event_field_value *captured_field_array = NULL; | |
226 | unsigned int expected_capture_field_count; | |
227 | unsigned int captured_field_count; | |
228 | ||
229 | assert(lttng_evaluation_get_type(evaluation) == | |
230 | LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); | |
231 | ||
232 | condition_status = | |
233 | lttng_condition_event_rule_get_capture_descriptor_count( | |
234 | condition, | |
235 | &expected_capture_field_count); | |
236 | if (condition_status != LTTNG_CONDITION_STATUS_OK) { | |
237 | ret = 1; | |
238 | goto end; | |
239 | } | |
240 | ||
241 | if (expected_capture_field_count == 0) { | |
242 | ret = 0; | |
243 | goto end; | |
244 | } | |
245 | ||
246 | evaluation_status = lttng_evaluation_get_captured_values( | |
247 | evaluation, &captured_field_array); | |
248 | if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) { | |
249 | ret = 1; | |
250 | goto end; | |
251 | } | |
252 | ||
253 | printf("Captured field values:\n"); | |
254 | print_array(captured_field_array, 1); | |
255 | end: | |
256 | return ret; | |
257 | } | |
258 | ||
259 | static int print_notification(struct lttng_notification *notification) | |
260 | { | |
261 | int ret = 0; | |
262 | const struct lttng_evaluation *evaluation = | |
263 | lttng_notification_get_evaluation(notification); | |
264 | const enum lttng_condition_type type = | |
265 | lttng_evaluation_get_type(evaluation); | |
266 | ||
267 | switch (type) { | |
268 | case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: | |
269 | printf("Received consumed size notification\n"); | |
270 | break; | |
271 | case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: | |
272 | case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: | |
273 | printf("Received buffer usage notification\n"); | |
274 | break; | |
275 | case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: | |
276 | printf("Received session rotation ongoing notification\n"); | |
277 | break; | |
278 | case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: | |
279 | printf("Received session rotation completed notification\n"); | |
280 | break; | |
281 | case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: | |
282 | { | |
283 | const char *trigger_name; | |
284 | enum lttng_evaluation_status evaluation_status; | |
285 | char time_str[64]; | |
286 | struct timeval tv; | |
287 | time_t the_time; | |
288 | ||
289 | gettimeofday(&tv, NULL); | |
290 | the_time = tv.tv_sec; | |
291 | ||
292 | strftime(time_str, sizeof(time_str), "[%m-%d-%Y] %T", | |
293 | localtime(&the_time)); | |
294 | printf("%s.%ld - ", time_str, tv.tv_usec); | |
295 | ||
296 | evaluation_status = | |
297 | lttng_evaluation_event_rule_get_trigger_name( | |
298 | evaluation, &trigger_name); | |
299 | if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) { | |
300 | fprintf(stderr, "Failed to get trigger name of event rule notification\n"); | |
301 | ret = -1; | |
302 | break; | |
303 | } | |
304 | ||
305 | printf("Received notification of event rule trigger \"%s\"\n", | |
306 | trigger_name); | |
307 | ret = print_captures(notification); | |
308 | break; | |
309 | } | |
310 | default: | |
311 | fprintf(stderr, "Unknown notification type (%d)\n", type); | |
312 | } | |
313 | ||
314 | return ret; | |
315 | } | |
316 | ||
317 | int main(int argc, char **argv) | |
318 | { | |
319 | int ret; | |
320 | struct lttng_triggers *triggers = NULL; | |
321 | unsigned int count, i, j, subcription_count = 0, trigger_count; | |
322 | enum lttng_trigger_status trigger_status; | |
323 | struct lttng_notification_channel *notification_channel = NULL; | |
324 | ||
325 | if (argc < 2) { | |
326 | fprintf(stderr, "Missing trigger name(s)\n"); | |
327 | fprintf(stderr, "Usage: notification-client TRIGGER_NAME ..."); | |
328 | ret = -1; | |
329 | goto end; | |
330 | } | |
331 | ||
332 | trigger_count = argc - 1; | |
333 | ||
334 | notification_channel = lttng_notification_channel_create( | |
335 | lttng_session_daemon_notification_endpoint); | |
336 | if (!notification_channel) { | |
337 | fprintf(stderr, "Failed to create notification channel\n"); | |
338 | ret = -1; | |
339 | goto end; | |
340 | } | |
341 | ||
342 | ret = lttng_list_triggers(&triggers); | |
343 | if (ret) { | |
344 | fprintf(stderr, "Failed to list triggers\n"); | |
345 | goto end; | |
346 | } | |
347 | ||
348 | trigger_status = lttng_triggers_get_count(triggers, &count); | |
349 | if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { | |
350 | fprintf(stderr, "Failed to get trigger count\n"); | |
351 | ret = -1; | |
352 | goto end; | |
353 | } | |
354 | ||
355 | for (i = 0; i < count; i++) { | |
356 | const struct lttng_trigger *trigger = | |
357 | lttng_triggers_get_at_index(triggers, i); | |
358 | const struct lttng_condition *condition = | |
359 | lttng_trigger_get_const_condition(trigger); | |
360 | const struct lttng_action *action = | |
361 | lttng_trigger_get_const_action(trigger); | |
362 | const enum lttng_action_type action_type = | |
363 | lttng_action_get_type(action); | |
364 | enum lttng_notification_channel_status channel_status; | |
365 | const char *trigger_name = NULL; | |
366 | bool subscribe = false; | |
367 | ||
368 | lttng_trigger_get_name(trigger, &trigger_name); | |
369 | for (j = 0; j < trigger_count; j++) { | |
370 | if (!strcmp(trigger_name, argv[j + 1])) { | |
371 | subscribe = true; | |
372 | break; | |
373 | } | |
374 | } | |
375 | ||
376 | if (!subscribe) { | |
377 | continue; | |
378 | } | |
379 | ||
380 | if (!((action_type == LTTNG_ACTION_TYPE_GROUP && | |
381 | action_group_contains_notify(action)) || | |
382 | action_type == LTTNG_ACTION_TYPE_NOTIFY)) { | |
383 | printf("The action of trigger \"%s\" is not \"notify\", skipping.\n", | |
384 | trigger_name); | |
385 | continue; | |
386 | } | |
387 | ||
388 | channel_status = lttng_notification_channel_subscribe( | |
389 | notification_channel, condition); | |
390 | if (channel_status == | |
391 | LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED) { | |
392 | continue; | |
393 | } | |
394 | if (channel_status) { | |
395 | fprintf(stderr, "Failed to subscribe to notifications of trigger \"%s\"\n", | |
396 | trigger_name); | |
397 | ret = -1; | |
398 | goto end; | |
399 | } | |
400 | ||
401 | printf("Subscribed to notifications of trigger \"%s\"\n", | |
402 | trigger_name); | |
403 | subcription_count++; | |
404 | } | |
405 | ||
406 | if (subcription_count == 0) { | |
407 | printf("No matching trigger with a notify action found.\n"); | |
408 | ret = 0; | |
409 | goto end; | |
410 | } | |
411 | ||
412 | for (;;) { | |
413 | struct lttng_notification *notification; | |
414 | enum lttng_notification_channel_status channel_status; | |
415 | ||
416 | channel_status = | |
417 | lttng_notification_channel_get_next_notification( | |
418 | notification_channel, | |
419 | ¬ification); | |
420 | switch (channel_status) { | |
421 | case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED: | |
422 | printf("Dropped notification\n"); | |
423 | break; | |
424 | case LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED: | |
425 | ret = 0; | |
426 | goto end; | |
427 | case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK: | |
428 | break; | |
429 | case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED: | |
430 | printf("Notification channel was closed by peer.\n"); | |
431 | break; | |
432 | default: | |
433 | fprintf(stderr, "A communication error occurred on the notification channel.\n"); | |
434 | ret = -1; | |
435 | goto end; | |
436 | } | |
437 | ||
438 | ret = print_notification(notification); | |
439 | lttng_notification_destroy(notification); | |
440 | if (ret) { | |
441 | goto end; | |
442 | } | |
443 | } | |
444 | end: | |
445 | lttng_triggers_destroy(triggers); | |
446 | lttng_notification_channel_destroy(notification_channel); | |
447 | return !!ret; | |
448 | } |