lttng: reuse random_access_container_wrapper for session_list
[lttng-tools.git] / src / bin / lttng / utils.cpp
... / ...
CommitLineData
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
31static const char *str_all = "ALL";
32static const char *str_tracepoint = "Tracepoint";
33static const char *str_syscall = "Syscall";
34static const char *str_probe = "Probe";
35static const char *str_userspace_probe = "Userspace Probe";
36static const char *str_function = "Function";
37
38static 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
60error:
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 */
70char *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 */
81char *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 */
92void 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 */
111void 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 */
130void 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)
151static 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__)
167static 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
183static __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
219static __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
249static 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 */
262int 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 */
274int 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 */
286int get_count_order_ulong(unsigned long x)
287{
288 if (!x)
289 return -1;
290
291 return fls_ulong(x - 1);
292}
293
294const 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 */
328int 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
367end:
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 */
376int 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
410error:
411 if (close(fd) < 0) {
412 PERROR("close relayd fd");
413 }
414error_socket:
415 return ret;
416}
417
418int 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 */
437void 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
448int 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 }
550end:
551 free(sessions);
552 free(channels);
553 free(domains);
554 return ret;
555}
556
557int 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
573int 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 }
659end:
660 if (!printed_location) {
661 MSG(" at an unknown location");
662 }
663 return ret;
664}
665
666namespace {
667template <typename FilterFunctionType>
668lttng::cli::session_list get_sessions(const FilterFunctionType& filter,
669 bool return_first_match_only = false)
670{
671 lttng::cli::session_list list = []() {
672 int list_ret;
673 struct lttng_session *psessions;
674
675 list_ret = lttng_list_sessions(&psessions);
676
677 if (list_ret < 0) {
678 LTTNG_THROW_CTL("Failed to list sessions",
679 static_cast<lttng_error_code>(list_ret));
680 }
681
682 return lttng::cli::session_list(psessions, list_ret);
683 }();
684
685 std::size_t write_to = 0;
686 for (std::size_t read_from = 0; read_from < list.size(); ++read_from) {
687 if (!filter(list[read_from])) {
688 continue;
689 }
690
691 if (read_from != write_to) {
692 list[write_to] = list[read_from];
693 }
694
695 ++write_to;
696
697 if (return_first_match_only) {
698 return lttng::cli::session_list(std::move(list), 1);
699 }
700 }
701
702 list.resize(write_to);
703
704 return list;
705}
706} /* namespace */
707
708lttng::cli::session_list lttng::cli::list_sessions(const struct session_spec& spec)
709{
710 switch (spec.type_) {
711 case lttng::cli::session_spec::type::NAME:
712 if (spec.value == nullptr) {
713 const auto configured_name =
714 lttng::make_unique_wrapper<char, lttng::free>(get_session_name());
715
716 if (configured_name) {
717 const struct lttng::cli::session_spec new_spec(
718 lttng::cli::session_spec::type::NAME,
719 configured_name.get());
720
721 return list_sessions(new_spec);
722 }
723
724 return lttng::cli::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 lttng::cli::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 lttng::cli::session_spec::type::ALL:
737 return get_sessions([](const lttng_session&) { return true; });
738 }
739
740 return lttng::cli::session_list();
741}
This page took 0.034625 seconds and 4 git commands to generate.