actions: list: Add `for_each_action_{const, mutable}()` macros
[lttng-tools.git] / src / bin / lttng / utils.cpp
1 /*
2 * Copyright (C) 2011 EfficiOS Inc.
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
8 #define _LGPL_SOURCE
9 #include "command.hpp"
10 #include "conf.hpp"
11 #include "utils.hpp"
12
13 #include <common/defaults.hpp>
14 #include <common/error.hpp>
15 #include <common/exception.hpp>
16 #include <common/make-unique-wrapper.hpp>
17 #include <common/utils.hpp>
18
19 #include <arpa/inet.h>
20 #include <ctype.h>
21 #include <fnmatch.h>
22 #include <inttypes.h>
23 #include <limits.h>
24 #include <netinet/in.h>
25 #include <signal.h>
26 #include <stdlib.h>
27 #include <sys/socket.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30
31 static const char *str_all = "ALL";
32 static const char *str_tracepoint = "Tracepoint";
33 static const char *str_syscall = "Syscall";
34 static const char *str_probe = "Probe";
35 static const char *str_userspace_probe = "Userspace Probe";
36 static const char *str_function = "Function";
37
38 static char *_get_session_name(int quiet)
39 {
40 const char *path;
41 char *session_name = nullptr;
42
43 /* Get path to config file */
44 path = utils_get_home_dir();
45 if (path == nullptr) {
46 goto error;
47 }
48
49 /* Get session name from config */
50 session_name = quiet ? config_read_session_name_quiet(path) :
51 config_read_session_name(path);
52 if (session_name == nullptr) {
53 goto error;
54 }
55
56 DBG2("Config file path found: %s", path);
57 DBG("Session name found: %s", session_name);
58 return session_name;
59
60 error:
61 return nullptr;
62 }
63
64 /*
65 * get_session_name
66 *
67 * Return allocated string with the session name found in the config
68 * directory.
69 */
70 char *get_session_name()
71 {
72 return _get_session_name(0);
73 }
74
75 /*
76 * get_session_name_quiet (no warnings/errors emitted)
77 *
78 * Return allocated string with the session name found in the config
79 * directory.
80 */
81 char *get_session_name_quiet()
82 {
83 return _get_session_name(1);
84 }
85
86 /*
87 * list_commands
88 *
89 * List commands line by line. This is mostly for bash auto completion and to
90 * avoid difficult parsing.
91 */
92 void list_commands(struct cmd_struct *commands, FILE *ofp)
93 {
94 int i = 0;
95 struct cmd_struct *cmd = nullptr;
96
97 cmd = &commands[i];
98 while (cmd->name != nullptr) {
99 fprintf(ofp, "%s\n", cmd->name);
100 i++;
101 cmd = &commands[i];
102 }
103 }
104
105 /*
106 * list_cmd_options
107 *
108 * Prints a simple list of the options available to a command. This is intended
109 * to be easily parsed for bash completion.
110 */
111 void list_cmd_options(FILE *ofp, struct poptOption *options)
112 {
113 int i;
114 struct poptOption *option = nullptr;
115
116 for (i = 0; options[i].longName != nullptr; i++) {
117 option = &options[i];
118
119 fprintf(ofp, "--%s\n", option->longName);
120
121 if (isprint(option->shortName)) {
122 fprintf(ofp, "-%c\n", option->shortName);
123 }
124 }
125 }
126
127 /*
128 * Same as list_cmd_options, but for options specified for argpar.
129 */
130 void list_cmd_options_argpar(FILE *ofp, const struct argpar_opt_descr *options)
131 {
132 int i;
133
134 for (i = 0; options[i].long_name != nullptr; i++) {
135 const struct argpar_opt_descr *option = &options[i];
136
137 fprintf(ofp, "--%s\n", option->long_name);
138
139 if (isprint(option->short_name)) {
140 fprintf(ofp, "-%c\n", option->short_name);
141 }
142 }
143 }
144
145 /*
146 * fls: returns the position of the most significant bit.
147 * Returns 0 if no bit is set, else returns the position of the most
148 * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit).
149 */
150 #if defined(__i386) || defined(__x86_64)
151 static inline unsigned int fls_u32(uint32_t x)
152 {
153 int r;
154
155 asm("bsrl %1,%0\n\t"
156 "jnz 1f\n\t"
157 "movl $-1,%0\n\t"
158 "1:\n\t"
159 : "=r"(r)
160 : "rm"(x));
161 return r + 1;
162 }
163 #define HAS_FLS_U32
164 #endif
165
166 #if defined(__x86_64) && defined(__LP64__)
167 static inline unsigned int fls_u64(uint64_t x)
168 {
169 long r;
170
171 asm("bsrq %1,%0\n\t"
172 "jnz 1f\n\t"
173 "movq $-1,%0\n\t"
174 "1:\n\t"
175 : "=r"(r)
176 : "rm"(x));
177 return r + 1;
178 }
179 #define HAS_FLS_U64
180 #endif
181
182 #ifndef HAS_FLS_U64
183 static __attribute__((unused)) unsigned int fls_u64(uint64_t x)
184 {
185 unsigned int r = 64;
186
187 if (!x)
188 return 0;
189
190 if (!(x & 0xFFFFFFFF00000000ULL)) {
191 x <<= 32;
192 r -= 32;
193 }
194 if (!(x & 0xFFFF000000000000ULL)) {
195 x <<= 16;
196 r -= 16;
197 }
198 if (!(x & 0xFF00000000000000ULL)) {
199 x <<= 8;
200 r -= 8;
201 }
202 if (!(x & 0xF000000000000000ULL)) {
203 x <<= 4;
204 r -= 4;
205 }
206 if (!(x & 0xC000000000000000ULL)) {
207 x <<= 2;
208 r -= 2;
209 }
210 if (!(x & 0x8000000000000000ULL)) {
211 x <<= 1;
212 r -= 1;
213 }
214 return r;
215 }
216 #endif
217
218 #ifndef HAS_FLS_U32
219 static __attribute__((unused)) unsigned int fls_u32(uint32_t x)
220 {
221 unsigned int r = 32;
222
223 if (!x)
224 return 0;
225 if (!(x & 0xFFFF0000U)) {
226 x <<= 16;
227 r -= 16;
228 }
229 if (!(x & 0xFF000000U)) {
230 x <<= 8;
231 r -= 8;
232 }
233 if (!(x & 0xF0000000U)) {
234 x <<= 4;
235 r -= 4;
236 }
237 if (!(x & 0xC0000000U)) {
238 x <<= 2;
239 r -= 2;
240 }
241 if (!(x & 0x80000000U)) {
242 x <<= 1;
243 r -= 1;
244 }
245 return r;
246 }
247 #endif
248
249 static unsigned int fls_ulong(unsigned long x)
250 {
251 #if (CAA_BITS_PER_LONG == 32)
252 return fls_u32(x);
253 #else
254 return fls_u64(x);
255 #endif
256 }
257
258 /*
259 * Return the minimum order for which x <= (1UL << order).
260 * Return -1 if x is 0.
261 */
262 int get_count_order_u32(uint32_t x)
263 {
264 if (!x)
265 return -1;
266
267 return fls_u32(x - 1);
268 }
269
270 /*
271 * Return the minimum order for which x <= (1UL << order).
272 * Return -1 if x is 0.
273 */
274 int get_count_order_u64(uint64_t x)
275 {
276 if (!x)
277 return -1;
278
279 return fls_u64(x - 1);
280 }
281
282 /*
283 * Return the minimum order for which x <= (1UL << order).
284 * Return -1 if x is 0.
285 */
286 int get_count_order_ulong(unsigned long x)
287 {
288 if (!x)
289 return -1;
290
291 return fls_ulong(x - 1);
292 }
293
294 const char *get_event_type_str(enum lttng_event_type type)
295 {
296 const char *str_event_type;
297
298 switch (type) {
299 case LTTNG_EVENT_ALL:
300 str_event_type = str_all;
301 break;
302 case LTTNG_EVENT_TRACEPOINT:
303 str_event_type = str_tracepoint;
304 break;
305 case LTTNG_EVENT_SYSCALL:
306 str_event_type = str_syscall;
307 break;
308 case LTTNG_EVENT_PROBE:
309 str_event_type = str_probe;
310 break;
311 case LTTNG_EVENT_USERSPACE_PROBE:
312 str_event_type = str_userspace_probe;
313 break;
314 case LTTNG_EVENT_FUNCTION:
315 str_event_type = str_function;
316 break;
317 default:
318 /* Should not have an unknown event type or else define it. */
319 abort();
320 }
321
322 return str_event_type;
323 }
324
325 /*
326 * Spawn a lttng relayd daemon by forking and execv.
327 */
328 int spawn_relayd(const char *pathname, int port)
329 {
330 int ret = 0;
331 pid_t pid;
332 char url[255];
333
334 if (!port) {
335 port = DEFAULT_NETWORK_VIEWER_PORT;
336 }
337
338 ret = snprintf(url, sizeof(url), "tcp://localhost:%d", port);
339 if (ret < 0) {
340 goto end;
341 }
342
343 MSG("Spawning a relayd daemon");
344 pid = fork();
345 if (pid == 0) {
346 /*
347 * Spawn session daemon and tell
348 * it to signal us when ready.
349 */
350 execlp(pathname, "lttng-relayd", "-L", url, NULL);
351 /* execlp only returns if error happened */
352 if (errno == ENOENT) {
353 ERR("No relayd found. Use --relayd-path.");
354 } else {
355 PERROR("execlp");
356 }
357 kill(getppid(), SIGTERM); /* wake parent */
358 exit(EXIT_FAILURE);
359 } else if (pid > 0) {
360 goto end;
361 } else {
362 PERROR("fork");
363 ret = -1;
364 goto end;
365 }
366
367 end:
368 return ret;
369 }
370
371 /*
372 * Check if relayd is alive.
373 *
374 * Return 1 if found else 0 if NOT found. Negative value on error.
375 */
376 int check_relayd()
377 {
378 int ret, fd;
379 struct sockaddr_in sin;
380
381 fd = socket(AF_INET, SOCK_STREAM, 0);
382 if (fd < 0) {
383 PERROR("socket check relayd");
384 ret = -1;
385 goto error_socket;
386 }
387
388 sin.sin_family = AF_INET;
389 sin.sin_port = htons(DEFAULT_NETWORK_VIEWER_PORT);
390 ret = inet_pton(sin.sin_family, "127.0.0.1", &sin.sin_addr);
391 if (ret < 1) {
392 PERROR("inet_pton check relayd");
393 ret = -1;
394 goto error;
395 }
396
397 /*
398 * A successful connect means the relayd exists thus returning 0 else a
399 * negative value means it does NOT exists.
400 */
401 ret = connect(fd, (struct sockaddr *) &sin, sizeof(sin));
402 if (ret < 0) {
403 /* Not found. */
404 ret = 0;
405 } else {
406 /* Already spawned. */
407 ret = 1;
408 }
409
410 error:
411 if (close(fd) < 0) {
412 PERROR("close relayd fd");
413 }
414 error_socket:
415 return ret;
416 }
417
418 int print_missing_or_multiple_domains(unsigned int domain_count, bool include_agent_domains)
419 {
420 int ret = 0;
421
422 if (domain_count == 0) {
423 ERR("Please specify a domain (--kernel/--userspace%s).",
424 include_agent_domains ? "/--jul/--log4j/--python" : "");
425 ret = -1;
426 } else if (domain_count > 1) {
427 ERR("Only one domain must be specified.");
428 ret = -1;
429 }
430
431 return ret;
432 }
433
434 /*
435 * Get the discarded events and lost packet counts.
436 */
437 void print_session_stats(const char *session_name)
438 {
439 char *str;
440 const int ret = get_session_stats_str(session_name, &str);
441
442 if (ret >= 0 && str) {
443 MSG("%s", str);
444 free(str);
445 }
446 }
447
448 int get_session_stats_str(const char *session_name, char **out_str)
449 {
450 int count, nb_domains, domain_idx, channel_idx, session_idx, ret;
451 struct lttng_domain *domains = nullptr;
452 struct lttng_channel *channels = nullptr;
453 uint64_t discarded_events_total = 0, lost_packets_total = 0;
454 struct lttng_session *sessions = nullptr;
455 const struct lttng_session *selected_session = nullptr;
456 char *stats_str = nullptr;
457 bool print_discarded_events = false, print_lost_packets = false;
458
459 count = lttng_list_sessions(&sessions);
460 if (count < 1) {
461 ERR("Failed to retrieve session descriptions while printing session statistics.");
462 ret = -1;
463 goto end;
464 }
465
466 /* Identify the currently-selected sessions. */
467 for (session_idx = 0; session_idx < count; session_idx++) {
468 if (!strcmp(session_name, sessions[session_idx].name)) {
469 selected_session = &sessions[session_idx];
470 break;
471 }
472 }
473 if (!selected_session) {
474 ERR("Failed to retrieve session \"%s\" description while printing session statistics.",
475 session_name);
476 ret = -1;
477 goto end;
478 }
479
480 nb_domains = lttng_list_domains(session_name, &domains);
481 if (nb_domains < 0) {
482 ret = -1;
483 goto end;
484 }
485 for (domain_idx = 0; domain_idx < nb_domains; domain_idx++) {
486 struct lttng_handle *handle =
487 lttng_create_handle(session_name, &domains[domain_idx]);
488
489 if (!handle) {
490 ERR("Failed to create session handle while printing session statistics.");
491 ret = -1;
492 goto end;
493 }
494
495 free(channels);
496 channels = nullptr;
497 count = lttng_list_channels(handle, &channels);
498 for (channel_idx = 0; channel_idx < count; channel_idx++) {
499 uint64_t discarded_events = 0, lost_packets = 0;
500 struct lttng_channel *channel = &channels[channel_idx];
501
502 ret = lttng_channel_get_discarded_event_count(channel, &discarded_events);
503 if (ret) {
504 ERR("Failed to retrieve discarded event count from channel %s",
505 channel->name);
506 }
507
508 ret = lttng_channel_get_lost_packet_count(channel, &lost_packets);
509 if (ret) {
510 ERR("Failed to retrieve lost packet count from channel %s",
511 channel->name);
512 }
513
514 discarded_events_total += discarded_events;
515 lost_packets_total += lost_packets;
516 }
517 lttng_destroy_handle(handle);
518 }
519
520 print_discarded_events = discarded_events_total > 0 && !selected_session->snapshot_mode;
521 print_lost_packets = lost_packets_total > 0 && !selected_session->snapshot_mode;
522
523 if (print_discarded_events && print_lost_packets) {
524 ret = asprintf(&stats_str,
525 "Warning: %" PRIu64 " events were discarded and %" PRIu64
526 " packets were lost, please refer to "
527 "the documentation on channel configuration.",
528 discarded_events_total,
529 lost_packets_total);
530 } else if (print_discarded_events) {
531 ret = asprintf(&stats_str,
532 "Warning: %" PRIu64 " events were discarded, please refer to "
533 "the documentation on channel configuration.",
534 discarded_events_total);
535 } else if (print_lost_packets) {
536 ret = asprintf(&stats_str,
537 "Warning: %" PRIu64 " packets were lost, please refer to "
538 "the documentation on channel configuration.",
539 lost_packets_total);
540 } else {
541 ret = 0;
542 }
543
544 if (ret < 0) {
545 ERR("Failed to format lost packet and discarded events statistics");
546 } else {
547 *out_str = stats_str;
548 ret = 0;
549 }
550 end:
551 free(sessions);
552 free(channels);
553 free(domains);
554 return ret;
555 }
556
557 int show_cmd_help(const char *cmd_name, const char *help_msg)
558 {
559 int ret;
560 char page_name[32];
561
562 ret = sprintf(page_name, "lttng-%s", cmd_name);
563 LTTNG_ASSERT(ret > 0 && ret < 32);
564 ret = utils_show_help(1, page_name, help_msg);
565 if (ret && !help_msg) {
566 ERR("Cannot view man page `lttng-%s(1)`", cmd_name);
567 perror("exec");
568 }
569
570 return ret;
571 }
572
573 int print_trace_archive_location(const struct lttng_trace_archive_location *location,
574 const char *session_name)
575 {
576 int ret = 0;
577 enum lttng_trace_archive_location_type location_type;
578 enum lttng_trace_archive_location_status status;
579 bool printed_location = false;
580
581 location_type = lttng_trace_archive_location_get_type(location);
582
583 _MSG("Trace chunk archive for session %s is now readable", session_name);
584 switch (location_type) {
585 case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL:
586 {
587 const char *absolute_path;
588
589 status = lttng_trace_archive_location_local_get_absolute_path(location,
590 &absolute_path);
591 if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
592 ret = -1;
593 goto end;
594 }
595 MSG(" at %s", absolute_path);
596 printed_location = true;
597 break;
598 }
599 case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY:
600 {
601 uint16_t control_port, data_port;
602 const char *host, *relative_path, *protocol_str;
603 enum lttng_trace_archive_location_relay_protocol_type protocol;
604
605 /* Fetch all relay location parameters. */
606 status = lttng_trace_archive_location_relay_get_protocol_type(location, &protocol);
607 if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
608 ret = -1;
609 goto end;
610 }
611
612 status = lttng_trace_archive_location_relay_get_host(location, &host);
613 if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
614 ret = -1;
615 goto end;
616 }
617
618 status = lttng_trace_archive_location_relay_get_control_port(location,
619 &control_port);
620 if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
621 ret = -1;
622 goto end;
623 }
624
625 status = lttng_trace_archive_location_relay_get_data_port(location, &data_port);
626 if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
627 ret = -1;
628 goto end;
629 }
630
631 status = lttng_trace_archive_location_relay_get_relative_path(location,
632 &relative_path);
633 if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
634 ret = -1;
635 goto end;
636 }
637
638 switch (protocol) {
639 case LTTNG_TRACE_ARCHIVE_LOCATION_RELAY_PROTOCOL_TYPE_TCP:
640 protocol_str = "tcp";
641 break;
642 default:
643 protocol_str = "unknown";
644 break;
645 }
646
647 MSG(" on relay %s://%s/%s [control port %" PRIu16 ", data port %" PRIu16 "]",
648 protocol_str,
649 host,
650 relative_path,
651 control_port,
652 data_port);
653 printed_location = true;
654 break;
655 }
656 default:
657 break;
658 }
659 end:
660 if (!printed_location) {
661 MSG(" at an unknown location");
662 }
663 return ret;
664 }
665
666 namespace {
667 template <typename FilterFunctionType>
668 session_list get_sessions(const FilterFunctionType& filter, bool return_first_match_only = false)
669 {
670 session_list list;
671
672 {
673 int list_ret;
674 struct lttng_session *psessions;
675
676 list_ret = lttng_list_sessions(&psessions);
677
678 if (list_ret < 0) {
679 LTTNG_THROW_CTL("Failed to list sessions",
680 static_cast<lttng_error_code>(list_ret));
681 }
682
683 list = session_list(psessions, list_ret);
684 }
685
686 std::size_t write_to = 0;
687 for (std::size_t read_from = 0; read_from < list.size(); ++read_from) {
688 if (!filter(list[read_from])) {
689 continue;
690 }
691
692 if (read_from != write_to) {
693 list[write_to] = list[read_from];
694 }
695
696 ++write_to;
697
698 if (return_first_match_only) {
699 return session_list(std::move(list), 1);
700 }
701 }
702
703 list.resize(write_to);
704
705 return list;
706 }
707 } /* namespace */
708
709 session_list list_sessions(const struct session_spec& spec)
710 {
711 switch (spec.type_) {
712 case session_spec::type::NAME:
713 if (spec.value == nullptr) {
714 const auto configured_name =
715 lttng::make_unique_wrapper<char, lttng::free>(get_session_name());
716
717 if (configured_name) {
718 const struct session_spec new_spec(session_spec::type::NAME,
719 configured_name.get());
720
721 return list_sessions(new_spec);
722 }
723
724 return session_list();
725 }
726
727 return get_sessions(
728 [&spec](const lttng_session& session) {
729 return strcmp(session.name, spec.value) == 0;
730 },
731 true);
732 case session_spec::type::GLOB_PATTERN:
733 return get_sessions([&spec](const lttng_session& session) {
734 return fnmatch(spec.value, session.name, 0) == 0;
735 });
736 case session_spec::type::ALL:
737 return get_sessions([](const lttng_session&) { return true; });
738 }
739
740 return session_list();
741 }
This page took 0.044418 seconds and 4 git commands to generate.