Run clang-format on the whole tree
[lttng-tools.git] / src / bin / lttng / commands / list.cpp
CommitLineData
f3ed775e 1/*
21cf9b6b 2 * Copyright (C) 2011 EfficiOS Inc.
159b042f 3 * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
f3ed775e 4 *
ab5be9fa 5 * SPDX-License-Identifier: GPL-2.0-only
f3ed775e 6 *
f3ed775e
DG
7 */
8
159b042f 9#include <stdint.h>
6c1c0768 10#define _LGPL_SOURCE
28ab034a 11#include "../command.hpp"
f3ed775e 12
c9e313bc
SM
13#include <common/mi-lttng.hpp>
14#include <common/time.hpp>
15#include <common/tracker.hpp>
28ab034a 16
c9e313bc 17#include <lttng/domain-internal.hpp>
050dd639 18#include <lttng/lttng.h>
fb14d0d8 19
28ab034a
JG
20#include <inttypes.h>
21#include <popt.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
f3ed775e 25
9f19cc17
DG
26static int opt_userspace;
27static int opt_kernel;
3c6a091f 28static int opt_jul;
5cdb6027 29static int opt_log4j;
0e115563 30static int opt_python;
9f19cc17
DG
31static char *opt_channel;
32static int opt_domain;
f37d259d 33static int opt_fields;
834978fd 34static int opt_syscall;
9f19cc17
DG
35
36const char *indent4 = " ";
37const char *indent6 = " ";
38const char *indent8 = " ";
f3ed775e 39
4fc83d94
PP
40#ifdef LTTNG_EMBED_HELP
41static const char help_msg[] =
42#include <lttng-list.1.h>
28ab034a 43 ;
4fc83d94
PP
44#endif
45
f3ed775e
DG
46enum {
47 OPT_HELP = 1,
eeac7d46 48 OPT_USERSPACE,
679b4943 49 OPT_LIST_OPTIONS,
f3ed775e
DG
50};
51
41493f4a
SM
52static struct lttng_handle *the_handle;
53static struct mi_writer *the_writer;
cd80958d 54
e50e81d3 55/* Only set when listing a single session. */
41493f4a 56static struct lttng_session the_listed_session;
e50e81d3 57
f3ed775e
DG
58static struct poptOption long_options[] = {
59 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
28ab034a
JG
60 { "help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0 },
61 { "kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0 },
62 { "jul", 'j', POPT_ARG_VAL, &opt_jul, 1, 0, 0 },
63 { "log4j", 'l', POPT_ARG_VAL, &opt_log4j, 1, 0, 0 },
64 { "python", 'p', POPT_ARG_VAL, &opt_python, 1, 0, 0 },
65 { "userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0 },
66 { "channel", 'c', POPT_ARG_STRING, &opt_channel, 0, 0, 0 },
67 { "domain", 'd', POPT_ARG_VAL, &opt_domain, 1, 0, 0 },
68 { "fields", 'f', POPT_ARG_VAL, &opt_fields, 1, 0, 0 },
69 { "syscall", 'S', POPT_ARG_VAL, &opt_syscall, 1, 0, 0 },
70 { "list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL },
71 { 0, 0, 0, 0, 0, 0, 0 }
f3ed775e
DG
72};
73
f3ed775e 74/*
9f19cc17 75 * Get command line from /proc for a specific pid.
f3ed775e 76 *
9f19cc17
DG
77 * On success, return an allocated string pointer to the proc cmdline.
78 * On error, return NULL.
f3ed775e
DG
79 */
80static char *get_cmdline_by_pid(pid_t pid)
81{
82 int ret;
1ad31aec 83 FILE *fp = NULL;
f3ed775e 84 char *cmdline = NULL;
fae9a062
JG
85 /* Can't go bigger than /proc/LTTNG_MAX_PID/cmdline */
86 char path[sizeof("/proc//cmdline") + sizeof(LTTNG_MAX_PID_STR) - 1];
f3ed775e
DG
87
88 snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
89 fp = fopen(path, "r");
90 if (fp == NULL) {
91 goto end;
92 }
93
94 /* Caller must free() *cmdline */
64803277 95 cmdline = zmalloc<char>(PATH_MAX);
f6962219 96 if (!cmdline) {
6f04ed72 97 PERROR("malloc cmdline");
f6962219
JG
98 goto end;
99 }
64803277 100
f3ed775e 101 ret = fread(cmdline, 1, PATH_MAX, fp);
f40799e8 102 if (ret < 0) {
6f04ed72 103 PERROR("fread proc list");
f40799e8 104 }
f3ed775e
DG
105
106end:
1ad31aec
DG
107 if (fp) {
108 fclose(fp);
109 }
f3ed775e
DG
110 return cmdline;
111}
b551a063 112
28ab034a 113static const char *active_string(int value)
464dd62d
MD
114{
115 switch (value) {
28ab034a
JG
116 case 0:
117 return "inactive";
118 case 1:
119 return "active";
120 case -1:
121 return "";
122 default:
123 return NULL;
464dd62d
MD
124 }
125}
126
2cbf8fed
DG
127static const char *snapshot_string(int value)
128{
129 switch (value) {
130 case 1:
131 return " snapshot";
132 default:
133 return "";
134 }
135}
136
28ab034a 137static const char *enabled_string(int value)
464dd62d
MD
138{
139 switch (value) {
28ab034a
JG
140 case 0:
141 return " [disabled]";
142 case 1:
143 return " [enabled]";
144 case -1:
145 return "";
146 default:
147 return NULL;
464dd62d
MD
148 }
149}
150
28ab034a 151static const char *safe_string(const char *str)
fceb65df 152{
7b4a95b1 153 return str ? str : "";
4634f12e
JI
154}
155
1f0e17de
DG
156static const char *logleveltype_string(enum lttng_loglevel_type value)
157{
158 switch (value) {
af6bce80
DG
159 case LTTNG_EVENT_LOGLEVEL_ALL:
160 return ":";
1f0e17de 161 case LTTNG_EVENT_LOGLEVEL_RANGE:
af6bce80 162 return " <=";
1f0e17de 163 case LTTNG_EVENT_LOGLEVEL_SINGLE:
af6bce80 164 return " ==";
1f0e17de 165 default:
af6bce80 166 return " <<TYPE UNKN>>";
1f0e17de
DG
167 }
168}
169
834978fd
DG
170static const char *bitness_event(enum lttng_event_flag flags)
171{
172 if (flags & LTTNG_EVENT_FLAG_SYSCALL_32) {
173 if (flags & LTTNG_EVENT_FLAG_SYSCALL_64) {
174 return " [32/64-bit]";
175 } else {
176 return " [32-bit]";
177 }
178 } else if (flags & LTTNG_EVENT_FLAG_SYSCALL_64) {
179 return " [64-bit]";
180 } else {
181 return "";
182 }
183}
184
7b4a95b1
PP
185/*
186 * Get exclusion names message for a single event.
187 *
188 * Returned pointer must be freed by caller. Returns NULL on error.
189 */
190static char *get_exclusion_names_msg(struct lttng_event *event)
191{
192 int ret;
193 int exclusion_count;
194 char *exclusion_msg = NULL;
195 char *at;
7b4a95b1 196 size_t i;
28ab034a 197 const char *const exclusion_fmt = " [exclusions: ";
19ed7632 198 const size_t exclusion_fmt_len = strlen(exclusion_fmt);
7b4a95b1
PP
199
200 exclusion_count = lttng_event_get_exclusion_name_count(event);
201 if (exclusion_count < 0) {
202 goto end;
203 } else if (exclusion_count == 0) {
204 /*
205 * No exclusions: return copy of empty string so that
206 * it can be freed by caller.
207 */
208 exclusion_msg = strdup("");
209 goto end;
210 }
211
212 /*
213 * exclusion_msg's size is bounded by the exclusion_fmt string,
214 * a comma per entry, the entry count (fixed-size), a closing
215 * bracket, and a trailing \0.
216 */
28ab034a
JG
217 exclusion_msg = (char *) malloc(exclusion_count + exclusion_count * LTTNG_SYMBOL_NAME_LEN +
218 exclusion_fmt_len + 1);
7b4a95b1
PP
219 if (!exclusion_msg) {
220 goto end;
221 }
222
19ed7632 223 at = strcpy(exclusion_msg, exclusion_fmt) + exclusion_fmt_len;
7b4a95b1
PP
224 for (i = 0; i < exclusion_count; ++i) {
225 const char *name;
226
227 /* Append comma between exclusion names */
228 if (i > 0) {
229 *at = ',';
230 at++;
231 }
232
233 ret = lttng_event_get_exclusion_name(event, i, &name);
234 if (ret) {
235 /* Prints '?' on local error; should never happen */
236 *at = '?';
237 at++;
238 continue;
239 }
240
241 /* Append exclusion name */
31e40170 242 at += sprintf(at, "%s", name);
7b4a95b1
PP
243 }
244
245 /* This also puts a final '\0' at the end of exclusion_msg */
19ed7632 246 strcpy(at, "]");
7b4a95b1
PP
247
248end:
249 return exclusion_msg;
250}
251
b955b4d4
FD
252static void print_userspace_probe_location(struct lttng_event *event)
253{
87597c2c
JG
254 const struct lttng_userspace_probe_location *location;
255 const struct lttng_userspace_probe_location_lookup_method *lookup_method;
b955b4d4
FD
256 enum lttng_userspace_probe_location_lookup_method_type lookup_type;
257
258 location = lttng_event_get_userspace_probe_location(event);
259 if (!location) {
260 MSG("Event has no userspace probe location");
261 return;
262 }
263
264 lookup_method = lttng_userspace_probe_location_get_lookup_method(location);
265 if (!lookup_method) {
266 MSG("Event has no userspace probe location lookup method");
267 return;
268 }
269
270 MSG("%s%s (type: userspace-probe)%s", indent6, event->name, enabled_string(event->enabled));
271
272 lookup_type = lttng_userspace_probe_location_lookup_method_get_type(lookup_method);
273
274 switch (lttng_userspace_probe_location_get_type(location)) {
275 case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_UNKNOWN:
276 MSG("%sType: Unknown", indent8);
277 break;
278 case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
279 {
280 const char *function_name;
4df1f2d4 281 char *binary_path;
b955b4d4
FD
282
283 MSG("%sType: Function", indent8);
284 function_name = lttng_userspace_probe_location_function_get_function_name(location);
28ab034a
JG
285 binary_path = realpath(
286 lttng_userspace_probe_location_function_get_binary_path(location), NULL);
b955b4d4
FD
287
288 MSG("%sBinary path: %s", indent8, binary_path ? binary_path : "NULL");
289 MSG("%sFunction: %s()", indent8, function_name ? function_name : "NULL");
290 switch (lookup_type) {
291 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
292 MSG("%sLookup method: ELF", indent8);
293 break;
294 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT:
295 MSG("%sLookup method: default", indent8);
296 break;
297 default:
298 MSG("%sLookup method: INVALID LOOKUP TYPE ENCOUNTERED", indent8);
299 break;
300 }
4df1f2d4
JG
301
302 free(binary_path);
b955b4d4
FD
303 break;
304 }
305 case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
306 {
307 const char *probe_name, *provider_name;
4df1f2d4 308 char *binary_path;
b955b4d4
FD
309
310 MSG("%sType: Tracepoint", indent8);
311 probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name(location);
28ab034a
JG
312 provider_name =
313 lttng_userspace_probe_location_tracepoint_get_provider_name(location);
314 binary_path = realpath(
315 lttng_userspace_probe_location_tracepoint_get_binary_path(location), NULL);
b955b4d4 316 MSG("%sBinary path: %s", indent8, binary_path ? binary_path : "NULL");
28ab034a
JG
317 MSG("%sTracepoint: %s:%s",
318 indent8,
319 provider_name ? provider_name : "NULL",
320 probe_name ? probe_name : "NULL");
b955b4d4
FD
321 switch (lookup_type) {
322 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
323 MSG("%sLookup method: SDT", indent8);
324 break;
325 default:
326 MSG("%sLookup method: INVALID LOOKUP TYPE ENCOUNTERED", indent8);
327 break;
328 }
4df1f2d4
JG
329
330 free(binary_path);
b955b4d4
FD
331 break;
332 }
333 default:
334 ERR("Invalid probe type encountered");
335 }
336}
337
b551a063
DG
338/*
339 * Pretty print single event.
340 */
341static void print_events(struct lttng_event *event)
342{
4adbcc72
PP
343 int ret;
344 const char *filter_str;
345 char *filter_msg = NULL;
7b4a95b1 346 char *exclusion_msg = NULL;
4adbcc72 347
134e72ed 348 ret = lttng_event_get_filter_expression(event, &filter_str);
4adbcc72
PP
349
350 if (ret) {
351 filter_msg = strdup(" [failed to retrieve filter]");
352 } else if (filter_str) {
411b3154
SM
353 if (asprintf(&filter_msg, " [filter: '%s']", filter_str) == -1) {
354 filter_msg = NULL;
4adbcc72
PP
355 }
356 }
357
7b4a95b1
PP
358 exclusion_msg = get_exclusion_names_msg(event);
359 if (!exclusion_msg) {
360 exclusion_msg = strdup(" [failed to retrieve exclusions]");
361 }
362
b551a063
DG
363 switch (event->type) {
364 case LTTNG_EVENT_TRACEPOINT:
e4baff1e 365 {
8b703175 366 if (event->loglevel != -1) {
af6bce80 367 MSG("%s%s (loglevel%s %s (%d)) (type: tracepoint)%s%s%s",
28ab034a
JG
368 indent6,
369 event->name,
370 logleveltype_string(event->loglevel_type),
371 mi_lttng_loglevel_string(event->loglevel, the_handle->domain.type),
372 event->loglevel,
373 enabled_string(event->enabled),
374 safe_string(exclusion_msg),
375 safe_string(filter_msg));
8b703175 376 } else {
4634f12e 377 MSG("%s%s (type: tracepoint)%s%s%s",
28ab034a
JG
378 indent6,
379 event->name,
380 enabled_string(event->enabled),
381 safe_string(exclusion_msg),
382 safe_string(filter_msg));
8b703175 383 }
b551a063 384 break;
e4baff1e 385 }
1896972b 386 case LTTNG_EVENT_FUNCTION:
28ab034a
JG
387 MSG("%s%s (type: function)%s%s",
388 indent6,
389 event->name,
390 enabled_string(event->enabled),
391 safe_string(filter_msg));
1896972b
DG
392 if (event->attr.probe.addr != 0) {
393 MSG("%saddr: 0x%" PRIx64, indent8, event->attr.probe.addr);
394 } else {
395 MSG("%soffset: 0x%" PRIx64, indent8, event->attr.probe.offset);
396 MSG("%ssymbol: %s", indent8, event->attr.probe.symbol_name);
397 }
398 break;
b551a063 399 case LTTNG_EVENT_PROBE:
28ab034a
JG
400 MSG("%s%s (type: probe)%s%s",
401 indent6,
402 event->name,
403 enabled_string(event->enabled),
404 safe_string(filter_msg));
b551a063
DG
405 if (event->attr.probe.addr != 0) {
406 MSG("%saddr: 0x%" PRIx64, indent8, event->attr.probe.addr);
407 } else {
408 MSG("%soffset: 0x%" PRIx64, indent8, event->attr.probe.offset);
409 MSG("%ssymbol: %s", indent8, event->attr.probe.symbol_name);
410 }
411 break;
b955b4d4
FD
412 case LTTNG_EVENT_USERSPACE_PROBE:
413 print_userspace_probe_location(event);
414 break;
b551a063 415 case LTTNG_EVENT_FUNCTION_ENTRY:
28ab034a
JG
416 MSG("%s%s (type: function)%s%s",
417 indent6,
418 event->name,
419 enabled_string(event->enabled),
420 safe_string(filter_msg));
b551a063
DG
421 MSG("%ssymbol: \"%s\"", indent8, event->attr.ftrace.symbol_name);
422 break;
423 case LTTNG_EVENT_SYSCALL:
28ab034a
JG
424 MSG("%s%s%s%s%s%s",
425 indent6,
426 event->name,
427 (opt_syscall ? "" : " (type:syscall)"),
428 enabled_string(event->enabled),
429 bitness_event(event->flags),
430 safe_string(filter_msg));
b551a063
DG
431 break;
432 case LTTNG_EVENT_NOOP:
28ab034a
JG
433 MSG("%s (type: noop)%s%s",
434 indent6,
435 enabled_string(event->enabled),
436 safe_string(filter_msg));
b551a063
DG
437 break;
438 case LTTNG_EVENT_ALL:
410b78a0
FD
439 /* Fall-through. */
440 default:
b551a063 441 /* We should never have "all" events in list. */
a0377dfe 442 abort();
b551a063
DG
443 break;
444 }
4adbcc72
PP
445
446 free(filter_msg);
7b4a95b1 447 free(exclusion_msg);
b551a063
DG
448}
449
f37d259d
MD
450static const char *field_type(struct lttng_event_field *field)
451{
28ab034a 452 switch (field->type) {
f37d259d
MD
453 case LTTNG_EVENT_FIELD_INTEGER:
454 return "integer";
455 case LTTNG_EVENT_FIELD_ENUM:
456 return "enum";
457 case LTTNG_EVENT_FIELD_FLOAT:
458 return "float";
459 case LTTNG_EVENT_FIELD_STRING:
460 return "string";
461 case LTTNG_EVENT_FIELD_OTHER:
28ab034a 462 default: /* fall-through */
f37d259d
MD
463 return "unknown";
464 }
465}
466
467/*
468 * Pretty print single event fields.
469 */
470static void print_event_field(struct lttng_event_field *field)
471{
472 if (!field->field_name[0]) {
473 return;
474 }
28ab034a
JG
475 MSG("%sfield: %s (%s)%s",
476 indent8,
477 field->field_name,
478 field_type(field),
479 field->nowrite ? " [no write]" : "");
f37d259d
MD
480}
481
fb14d0d8
JR
482/*
483 * Machine interface
484 * Jul and ust event listing
485 */
28ab034a
JG
486static int
487mi_list_agent_ust_events(struct lttng_event *events, int count, struct lttng_domain *domain)
fb14d0d8
JR
488{
489 int ret, i;
490 pid_t cur_pid = 0;
491 char *cmdline = NULL;
492 int pid_element_open = 0;
493
494 /* Open domains element */
41493f4a 495 ret = mi_lttng_domains_open(the_writer);
fb14d0d8
JR
496 if (ret) {
497 goto end;
498 }
499
500 /* Write domain */
41493f4a 501 ret = mi_lttng_domain(the_writer, domain, 1);
fb14d0d8
JR
502 if (ret) {
503 goto end;
504 }
505
bf239d4c 506 /* Open pids element element */
41493f4a 507 ret = mi_lttng_pids_open(the_writer);
fb14d0d8
JR
508 if (ret) {
509 goto end;
510 }
511
512 for (i = 0; i < count; i++) {
513 if (cur_pid != events[i].pid) {
514 if (pid_element_open) {
2e9aecb9 515 /* Close the previous events and pid element */
28ab034a 516 ret = mi_lttng_close_multi_element(the_writer, 2);
fb14d0d8
JR
517 if (ret) {
518 goto end;
519 }
520 pid_element_open = 0;
521 }
522
523 cur_pid = events[i].pid;
524 cmdline = get_cmdline_by_pid(cur_pid);
525 if (!cmdline) {
526 ret = CMD_ERROR;
527 goto end;
528 }
529
530 if (!pid_element_open) {
531 /* Open and write a pid element */
28ab034a 532 ret = mi_lttng_pid(the_writer, cur_pid, cmdline, 1);
fb14d0d8
JR
533 if (ret) {
534 goto error;
535 }
536
537 /* Open events element */
41493f4a 538 ret = mi_lttng_events_open(the_writer);
fb14d0d8
JR
539 if (ret) {
540 goto error;
541 }
542
543 pid_element_open = 1;
544 }
545 free(cmdline);
546 }
547
548 /* Write an event */
28ab034a 549 ret = mi_lttng_event(the_writer, &events[i], 0, the_handle->domain.type);
fb14d0d8
JR
550 if (ret) {
551 goto end;
552 }
553 }
554
bf239d4c 555 /* Close pids */
41493f4a 556 ret = mi_lttng_writer_close_element(the_writer);
fb14d0d8
JR
557 if (ret) {
558 goto end;
559 }
560
561 /* Close domain, domains */
41493f4a 562 ret = mi_lttng_close_multi_element(the_writer, 2);
fb14d0d8
JR
563end:
564 return ret;
565error:
566 free(cmdline);
567 return ret;
568}
569
5cdb6027 570static int list_agent_events(void)
3c6a091f 571{
fb14d0d8 572 int i, size, ret = CMD_SUCCESS;
3c6a091f 573 struct lttng_domain domain;
26c610c9 574 struct lttng_handle *handle = NULL;
469b972b 575 struct lttng_event *event_list = NULL;
3c6a091f
DG
576 pid_t cur_pid = 0;
577 char *cmdline = NULL;
5cdb6027 578 const char *agent_domain_str;
3c6a091f
DG
579
580 memset(&domain, 0, sizeof(domain));
5cdb6027
DG
581 if (opt_jul) {
582 domain.type = LTTNG_DOMAIN_JUL;
583 } else if (opt_log4j) {
584 domain.type = LTTNG_DOMAIN_LOG4J;
0e115563
DG
585 } else if (opt_python) {
586 domain.type = LTTNG_DOMAIN_PYTHON;
26c610c9
JG
587 } else {
588 ERR("Invalid agent domain selected.");
589 ret = CMD_ERROR;
590 goto error;
5cdb6027
DG
591 }
592
1004b719 593 agent_domain_str = lttng_domain_type_str(domain.type);
5cdb6027
DG
594
595 DBG("Getting %s tracing events", agent_domain_str);
3c6a091f
DG
596
597 handle = lttng_create_handle(NULL, &domain);
598 if (handle == NULL) {
fb14d0d8
JR
599 ret = CMD_ERROR;
600 goto end;
3c6a091f
DG
601 }
602
603 size = lttng_list_tracepoints(handle, &event_list);
604 if (size < 0) {
28ab034a 605 ERR("Unable to list %s events: %s", agent_domain_str, lttng_strerror(size));
fb14d0d8
JR
606 ret = CMD_ERROR;
607 goto end;
3c6a091f
DG
608 }
609
fb14d0d8
JR
610 if (lttng_opt_mi) {
611 /* Mi print */
5cdb6027 612 ret = mi_list_agent_ust_events(event_list, size, &domain);
fb14d0d8
JR
613 if (ret) {
614 ret = CMD_ERROR;
615 goto error;
3c6a091f 616 }
fb14d0d8
JR
617 } else {
618 /* Pretty print */
28ab034a 619 MSG("%s events (Logger name):\n-------------------------", agent_domain_str);
3c6a091f 620
fb14d0d8
JR
621 if (size == 0) {
622 MSG("None");
623 }
3c6a091f 624
fb14d0d8
JR
625 for (i = 0; i < size; i++) {
626 if (cur_pid != event_list[i].pid) {
627 cur_pid = event_list[i].pid;
628 cmdline = get_cmdline_by_pid(cur_pid);
629 if (cmdline == NULL) {
630 ret = CMD_ERROR;
631 goto error;
632 }
633 MSG("\nPID: %d - Name: %s", cur_pid, cmdline);
634 free(cmdline);
635 }
636 MSG("%s- %s", indent6, event_list[i].name);
637 }
3c6a091f 638
fb14d0d8
JR
639 MSG("");
640 }
3c6a091f
DG
641
642error:
fb14d0d8
JR
643 free(event_list);
644end:
3c6a091f 645 lttng_destroy_handle(handle);
fb14d0d8 646 return ret;
3c6a091f
DG
647}
648
b551a063
DG
649/*
650 * Ask session daemon for all user space tracepoints available.
651 */
652static int list_ust_events(void)
653{
fb14d0d8 654 int i, size, ret = CMD_SUCCESS;
b551a063
DG
655 struct lttng_domain domain;
656 struct lttng_handle *handle;
bec97646 657 struct lttng_event *event_list = NULL;
b551a063 658 pid_t cur_pid = 0;
ea5cbc00 659 char *cmdline = NULL;
b551a063 660
441c16a7
MD
661 memset(&domain, 0, sizeof(domain));
662
b551a063
DG
663 DBG("Getting UST tracing events");
664
665 domain.type = LTTNG_DOMAIN_UST;
666
667 handle = lttng_create_handle(NULL, &domain);
668 if (handle == NULL) {
fb14d0d8
JR
669 ret = CMD_ERROR;
670 goto end;
b551a063
DG
671 }
672
673 size = lttng_list_tracepoints(handle, &event_list);
674 if (size < 0) {
60e835ca 675 ERR("Unable to list UST events: %s", lttng_strerror(size));
fb14d0d8
JR
676 ret = CMD_ERROR;
677 goto error;
b551a063
DG
678 }
679
fb14d0d8
JR
680 if (lttng_opt_mi) {
681 /* Mi print */
5cdb6027 682 ret = mi_list_agent_ust_events(event_list, size, &domain);
fb14d0d8
JR
683 } else {
684 /* Pretty print */
685 MSG("UST events:\n-------------");
686
687 if (size == 0) {
688 MSG("None");
689 }
690
691 for (i = 0; i < size; i++) {
692 if (cur_pid != event_list[i].pid) {
693 cur_pid = event_list[i].pid;
694 cmdline = get_cmdline_by_pid(cur_pid);
695 if (cmdline == NULL) {
696 ret = CMD_ERROR;
697 goto error;
698 }
699 MSG("\nPID: %d - Name: %s", cur_pid, cmdline);
700 free(cmdline);
701 }
702 print_events(&event_list[i]);
703 }
b551a063 704
fb14d0d8 705 MSG("");
b551a063
DG
706 }
707
fb14d0d8
JR
708error:
709 free(event_list);
710end:
711 lttng_destroy_handle(handle);
712 return ret;
713}
714
715/*
716 * Machine interface
717 * List all ust event with their fields
718 */
28ab034a
JG
719static int
720mi_list_ust_event_fields(struct lttng_event_field *fields, int count, struct lttng_domain *domain)
fb14d0d8
JR
721{
722 int ret, i;
723 pid_t cur_pid = 0;
724 char *cmdline = NULL;
725 int pid_element_open = 0;
726 int event_element_open = 0;
727 struct lttng_event cur_event;
728
d113fbc8
JR
729 memset(&cur_event, 0, sizeof(cur_event));
730
fb14d0d8 731 /* Open domains element */
41493f4a 732 ret = mi_lttng_domains_open(the_writer);
fb14d0d8
JR
733 if (ret) {
734 goto end;
735 }
736
737 /* Write domain */
41493f4a 738 ret = mi_lttng_domain(the_writer, domain, 1);
fb14d0d8
JR
739 if (ret) {
740 goto end;
741 }
742
bf239d4c 743 /* Open pids element */
41493f4a 744 ret = mi_lttng_pids_open(the_writer);
fb14d0d8
JR
745 if (ret) {
746 goto end;
747 }
748
749 for (i = 0; i < count; i++) {
750 if (cur_pid != fields[i].event.pid) {
751 if (pid_element_open) {
752 if (event_element_open) {
485ca16f 753 /* Close the previous field element and event. */
28ab034a 754 ret = mi_lttng_close_multi_element(the_writer, 2);
fb14d0d8
JR
755 if (ret) {
756 goto end;
757 }
758 event_element_open = 0;
759 }
760 /* Close the previous events, pid element */
28ab034a 761 ret = mi_lttng_close_multi_element(the_writer, 2);
fb14d0d8
JR
762 if (ret) {
763 goto end;
764 }
765 pid_element_open = 0;
766 }
767
768 cur_pid = fields[i].event.pid;
ea5cbc00 769 cmdline = get_cmdline_by_pid(cur_pid);
fb14d0d8 770 if (!pid_element_open) {
bf239d4c 771 /* Open and write a pid element */
28ab034a 772 ret = mi_lttng_pid(the_writer, cur_pid, cmdline, 1);
fb14d0d8
JR
773 if (ret) {
774 goto error;
775 }
776
777 /* Open events element */
41493f4a 778 ret = mi_lttng_events_open(the_writer);
fb14d0d8
JR
779 if (ret) {
780 goto error;
781 }
782 pid_element_open = 1;
783 }
ea5cbc00 784 free(cmdline);
fb14d0d8
JR
785 /* Wipe current event since we are about to print a new PID. */
786 memset(&cur_event, 0, sizeof(cur_event));
b551a063 787 }
b551a063 788
fb14d0d8
JR
789 if (strcmp(cur_event.name, fields[i].event.name) != 0) {
790 if (event_element_open) {
791 /* Close the previous fields element and the previous event */
28ab034a 792 ret = mi_lttng_close_multi_element(the_writer, 2);
fb14d0d8
JR
793 if (ret) {
794 goto end;
795 }
796 event_element_open = 0;
797 }
798
28ab034a 799 memcpy(&cur_event, &fields[i].event, sizeof(cur_event));
b551a063 800
fb14d0d8
JR
801 if (!event_element_open) {
802 /* Open and write the event */
28ab034a
JG
803 ret = mi_lttng_event(
804 the_writer, &cur_event, 1, the_handle->domain.type);
fb14d0d8
JR
805 if (ret) {
806 goto end;
807 }
808
809 /* Open a fields element */
41493f4a 810 ret = mi_lttng_event_fields_open(the_writer);
fb14d0d8
JR
811 if (ret) {
812 goto end;
813 }
814 event_element_open = 1;
815 }
816 }
b551a063 817
fb14d0d8 818 /* Print the event_field */
41493f4a 819 ret = mi_lttng_event_field(the_writer, &fields[i]);
fb14d0d8
JR
820 if (ret) {
821 goto end;
822 }
823 }
b551a063 824
83d6d6c4 825 /* Close pids, domain, domains */
41493f4a 826 ret = mi_lttng_close_multi_element(the_writer, 3);
fb14d0d8
JR
827end:
828 return ret;
b551a063 829error:
fb14d0d8
JR
830 free(cmdline);
831 return ret;
b551a063 832}
f3ed775e 833
f37d259d
MD
834/*
835 * Ask session daemon for all user space tracepoint fields available.
836 */
837static int list_ust_event_fields(void)
838{
fb14d0d8 839 int i, size, ret = CMD_SUCCESS;
f37d259d
MD
840 struct lttng_domain domain;
841 struct lttng_handle *handle;
842 struct lttng_event_field *event_field_list;
843 pid_t cur_pid = 0;
ea5cbc00
CB
844 char *cmdline = NULL;
845
f37d259d
MD
846 struct lttng_event cur_event;
847
848 memset(&domain, 0, sizeof(domain));
849 memset(&cur_event, 0, sizeof(cur_event));
850
851 DBG("Getting UST tracing event fields");
852
853 domain.type = LTTNG_DOMAIN_UST;
854
855 handle = lttng_create_handle(NULL, &domain);
856 if (handle == NULL) {
fb14d0d8
JR
857 ret = CMD_ERROR;
858 goto end;
f37d259d
MD
859 }
860
861 size = lttng_list_tracepoint_fields(handle, &event_field_list);
862 if (size < 0) {
60e835ca 863 ERR("Unable to list UST event fields: %s", lttng_strerror(size));
fb14d0d8
JR
864 ret = CMD_ERROR;
865 goto end;
f37d259d
MD
866 }
867
fb14d0d8
JR
868 if (lttng_opt_mi) {
869 /* Mi print */
870 ret = mi_list_ust_event_fields(event_field_list, size, &domain);
871 if (ret) {
872 ret = CMD_ERROR;
873 goto error;
874 }
875 } else {
876 /* Pretty print */
877 MSG("UST events:\n-------------");
f37d259d 878
fb14d0d8
JR
879 if (size == 0) {
880 MSG("None");
f37d259d 881 }
fb14d0d8
JR
882
883 for (i = 0; i < size; i++) {
884 if (cur_pid != event_field_list[i].event.pid) {
885 cur_pid = event_field_list[i].event.pid;
886 cmdline = get_cmdline_by_pid(cur_pid);
887 if (cmdline == NULL) {
888 ret = CMD_ERROR;
889 goto error;
890 }
891 MSG("\nPID: %d - Name: %s", cur_pid, cmdline);
892 free(cmdline);
893 /* Wipe current event since we are about to print a new PID. */
894 memset(&cur_event, 0, sizeof(cur_event));
895 }
896 if (strcmp(cur_event.name, event_field_list[i].event.name) != 0) {
897 print_events(&event_field_list[i].event);
28ab034a 898 memcpy(&cur_event, &event_field_list[i].event, sizeof(cur_event));
fb14d0d8
JR
899 }
900 print_event_field(&event_field_list[i]);
f37d259d 901 }
f37d259d 902
fb14d0d8
JR
903 MSG("");
904 }
f37d259d 905
fb14d0d8 906error:
f37d259d 907 free(event_field_list);
fb14d0d8 908end:
f37d259d 909 lttng_destroy_handle(handle);
fb14d0d8
JR
910 return ret;
911}
f37d259d 912
fb14d0d8
JR
913/*
914 * Machine interface
915 * Print a list of kernel events
916 */
28ab034a 917static int mi_list_kernel_events(struct lttng_event *events, int count, struct lttng_domain *domain)
fb14d0d8
JR
918{
919 int ret, i;
f37d259d 920
fb14d0d8 921 /* Open domains element */
41493f4a 922 ret = mi_lttng_domains_open(the_writer);
fb14d0d8
JR
923 if (ret) {
924 goto end;
925 }
926
927 /* Write domain */
41493f4a 928 ret = mi_lttng_domain(the_writer, domain, 1);
fb14d0d8
JR
929 if (ret) {
930 goto end;
931 }
932
933 /* Open events */
41493f4a 934 ret = mi_lttng_events_open(the_writer);
fb14d0d8
JR
935 if (ret) {
936 goto end;
937 }
938
939 for (i = 0; i < count; i++) {
28ab034a 940 ret = mi_lttng_event(the_writer, &events[i], 0, the_handle->domain.type);
fb14d0d8
JR
941 if (ret) {
942 goto end;
943 }
944 }
945
946 /* close events, domain and domains */
41493f4a 947 ret = mi_lttng_close_multi_element(the_writer, 3);
fb14d0d8
JR
948 if (ret) {
949 goto end;
950 }
951
952end:
953 return ret;
f37d259d
MD
954}
955
f3ed775e 956/*
fb14d0d8 957 * Ask for all trace events in the kernel
f3ed775e 958 */
9f19cc17 959static int list_kernel_events(void)
f3ed775e 960{
fb14d0d8 961 int i, size, ret = CMD_SUCCESS;
b551a063
DG
962 struct lttng_domain domain;
963 struct lttng_handle *handle;
9f19cc17 964 struct lttng_event *event_list;
f3ed775e 965
441c16a7
MD
966 memset(&domain, 0, sizeof(domain));
967
b551a063
DG
968 DBG("Getting kernel tracing events");
969
970 domain.type = LTTNG_DOMAIN_KERNEL;
971
972 handle = lttng_create_handle(NULL, &domain);
973 if (handle == NULL) {
fb14d0d8 974 ret = CMD_ERROR;
b551a063
DG
975 goto error;
976 }
f3ed775e 977
cd80958d 978 size = lttng_list_tracepoints(handle, &event_list);
9f19cc17 979 if (size < 0) {
60e835ca 980 ERR("Unable to list kernel events: %s", lttng_strerror(size));
ef021732 981 lttng_destroy_handle(handle);
fb14d0d8 982 return CMD_ERROR;
f3ed775e
DG
983 }
984
fb14d0d8
JR
985 if (lttng_opt_mi) {
986 /* Mi print */
987 ret = mi_list_kernel_events(event_list, size, &domain);
988 if (ret) {
989 ret = CMD_ERROR;
990 goto end;
991 }
992 } else {
993 MSG("Kernel events:\n-------------");
f3ed775e 994
fb14d0d8
JR
995 for (i = 0; i < size; i++) {
996 print_events(&event_list[i]);
997 }
f3ed775e 998
fb14d0d8
JR
999 MSG("");
1000 }
b551a063 1001
fb14d0d8 1002end:
f3ed775e
DG
1003 free(event_list);
1004
ef021732 1005 lttng_destroy_handle(handle);
fb14d0d8 1006 return ret;
b551a063
DG
1007
1008error:
ef021732 1009 lttng_destroy_handle(handle);
fb14d0d8
JR
1010 return ret;
1011}
1012
834978fd
DG
1013/*
1014 * Machine interface
1015 * Print a list of system calls.
1016 */
1017static int mi_list_syscalls(struct lttng_event *events, int count)
1018{
1019 int ret, i;
1020
1021 /* Open events */
41493f4a 1022 ret = mi_lttng_events_open(the_writer);
834978fd
DG
1023 if (ret) {
1024 goto end;
1025 }
1026
1027 for (i = 0; i < count; i++) {
28ab034a 1028 ret = mi_lttng_event(the_writer, &events[i], 0, the_handle->domain.type);
834978fd
DG
1029 if (ret) {
1030 goto end;
1031 }
1032 }
1033
1034 /* Close events. */
41493f4a 1035 ret = mi_lttng_writer_close_element(the_writer);
834978fd
DG
1036 if (ret) {
1037 goto end;
1038 }
1039
1040end:
1041 return ret;
1042}
1043
1044/*
1045 * Ask for kernel system calls.
1046 */
1047static int list_syscalls(void)
1048{
1049 int i, size, ret = CMD_SUCCESS;
1050 struct lttng_event *event_list;
1051
1052 DBG("Getting kernel system call events");
1053
1054 size = lttng_list_syscalls(&event_list);
1055 if (size < 0) {
1056 ERR("Unable to list system calls: %s", lttng_strerror(size));
1057 ret = CMD_ERROR;
1058 goto error;
1059 }
1060
1061 if (lttng_opt_mi) {
1062 /* Mi print */
1063 ret = mi_list_syscalls(event_list, size);
1064 if (ret) {
1065 ret = CMD_ERROR;
1066 goto end;
1067 }
1068 } else {
1069 MSG("System calls:\n-------------");
1070
1071 for (i = 0; i < size; i++) {
1072 print_events(&event_list[i]);
1073 }
1074
1075 MSG("");
1076 }
1077
1078end:
1079 free(event_list);
1080 return ret;
1081
1082error:
1083 return ret;
1084}
1085
fb14d0d8
JR
1086/*
1087 * Machine Interface
5cdb6027 1088 * Print a list of agent events
fb14d0d8 1089 */
5cdb6027 1090static int mi_list_session_agent_events(struct lttng_event *events, int count)
fb14d0d8
JR
1091{
1092 int ret, i;
1093
1094 /* Open events element */
41493f4a 1095 ret = mi_lttng_events_open(the_writer);
fb14d0d8
JR
1096 if (ret) {
1097 goto end;
1098 }
1099
1100 for (i = 0; i < count; i++) {
28ab034a 1101 ret = mi_lttng_event(the_writer, &events[i], 0, the_handle->domain.type);
fb14d0d8
JR
1102 if (ret) {
1103 goto end;
1104 }
1105 }
1106
1107 /* Close events element */
41493f4a 1108 ret = mi_lttng_writer_close_element(the_writer);
fb14d0d8
JR
1109
1110end:
1111 return ret;
f3ed775e
DG
1112}
1113
3c6a091f 1114/*
5cdb6027 1115 * List agent events for a specific session using the handle.
3c6a091f
DG
1116 *
1117 * Return CMD_SUCCESS on success else a negative value.
1118 */
5cdb6027 1119static int list_session_agent_events(void)
3c6a091f 1120{
fb14d0d8 1121 int ret = CMD_SUCCESS, count, i;
3c6a091f
DG
1122 struct lttng_event *events = NULL;
1123
41493f4a 1124 count = lttng_list_events(the_handle, "", &events);
3c6a091f 1125 if (count < 0) {
fb14d0d8
JR
1126 ret = CMD_ERROR;
1127 ERR("%s", lttng_strerror(count));
3c6a091f
DG
1128 goto error;
1129 }
1130
fb14d0d8
JR
1131 if (lttng_opt_mi) {
1132 /* Mi print */
5cdb6027 1133 ret = mi_list_session_agent_events(events, count);
fb14d0d8
JR
1134 if (ret) {
1135 ret = CMD_ERROR;
1136 goto end;
1137 }
1138 } else {
1139 /* Pretty print */
1140 MSG("Events (Logger name):\n---------------------");
1141 if (count == 0) {
1142 MSG("%sNone\n", indent6);
1143 goto end;
1144 }
1145
1146 for (i = 0; i < count; i++) {
9a890ce8
JG
1147 const char *filter_str;
1148 char *filter_msg = NULL;
1149 struct lttng_event *event = &events[i];
1150
28ab034a 1151 ret = lttng_event_get_filter_expression(event, &filter_str);
9a890ce8
JG
1152 if (ret) {
1153 filter_msg = strdup(" [failed to retrieve filter]");
1154 } else if (filter_str) {
411b3154
SM
1155 if (asprintf(&filter_msg, " [filter: '%s']", filter_str) == -1) {
1156 filter_msg = NULL;
9a890ce8
JG
1157 }
1158 }
1159
28ab034a
JG
1160 if (event->loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) {
1161 MSG("%s- %s%s (loglevel%s %s)%s",
1162 indent4,
1163 event->name,
1164 enabled_string(event->enabled),
1165 logleveltype_string(event->loglevel_type),
1166 mi_lttng_loglevel_string(event->loglevel,
1167 the_handle->domain.type),
1168 safe_string(filter_msg));
755066c2 1169 } else {
28ab034a
JG
1170 MSG("%s- %s%s%s",
1171 indent4,
1172 event->name,
1173 enabled_string(event->enabled),
1174 safe_string(filter_msg));
755066c2 1175 }
9a890ce8 1176 free(filter_msg);
fb14d0d8
JR
1177 }
1178
1179 MSG("");
1180 }
1181
1182end:
1183 free(events);
1184error:
1185 return ret;
1186}
1187
1188/*
1189 * Machine interface
1190 * print a list of event
1191 */
1192static int mi_list_events(struct lttng_event *events, int count)
1193{
1194 int ret, i;
1195
1196 /* Open events element */
41493f4a 1197 ret = mi_lttng_events_open(the_writer);
fb14d0d8 1198 if (ret) {
3c6a091f
DG
1199 goto end;
1200 }
1201
1202 for (i = 0; i < count; i++) {
28ab034a 1203 ret = mi_lttng_event(the_writer, &events[i], 0, the_handle->domain.type);
fb14d0d8
JR
1204 if (ret) {
1205 goto end;
1206 }
3c6a091f
DG
1207 }
1208
fb14d0d8 1209 /* Close events element */
41493f4a 1210 ret = mi_lttng_writer_close_element(the_writer);
3c6a091f
DG
1211
1212end:
3c6a091f
DG
1213 return ret;
1214}
1215
f3ed775e 1216/*
9f19cc17 1217 * List events of channel of session and domain.
f3ed775e 1218 */
cd80958d 1219static int list_events(const char *channel_name)
f3ed775e 1220{
fb14d0d8 1221 int ret = CMD_SUCCESS, count, i;
9f19cc17 1222 struct lttng_event *events = NULL;
f3ed775e 1223
41493f4a 1224 count = lttng_list_events(the_handle, channel_name, &events);
f3ed775e 1225 if (count < 0) {
fb14d0d8
JR
1226 ret = CMD_ERROR;
1227 ERR("%s", lttng_strerror(count));
f3ed775e
DG
1228 goto error;
1229 }
1230
fb14d0d8
JR
1231 if (lttng_opt_mi) {
1232 /* Mi print */
1233 ret = mi_list_events(events, count);
1234 if (ret) {
1235 ret = CMD_ERROR;
1236 goto end;
1237 }
1238 } else {
1239 /* Pretty print */
484b2a0c 1240 MSG("\n%sRecording event rules:", indent4);
fb14d0d8
JR
1241 if (count == 0) {
1242 MSG("%sNone\n", indent6);
1243 goto end;
1244 }
f3ed775e 1245
fb14d0d8
JR
1246 for (i = 0; i < count; i++) {
1247 print_events(&events[i]);
1248 }
f3ed775e 1249
fb14d0d8
JR
1250 MSG("");
1251 }
9f19cc17 1252end:
0e428499 1253 free(events);
f3ed775e
DG
1254error:
1255 return ret;
1256}
1257
28ab034a 1258static void print_timer(const char *timer_name, uint32_t space_count, int64_t value)
f3b3a67b
JG
1259{
1260 uint32_t i;
1261
1262 _MSG("%s%s:", indent6, timer_name);
1263 for (i = 0; i < space_count; i++) {
1264 _MSG(" ");
1265 }
1266
1267 if (value) {
2a1135fa 1268 MSG("%" PRId64 " %s", value, USEC_UNIT);
f3b3a67b
JG
1269 } else {
1270 MSG("inactive");
1271 }
1272}
1273
f3ed775e 1274/*
9f19cc17
DG
1275 * Pretty print channel
1276 */
1277static void print_channel(struct lttng_channel *channel)
1278{
fb83fe64 1279 int ret;
cf0bcb51 1280 uint64_t discarded_events, lost_packets, monitor_timer_interval;
491d1539 1281 int64_t blocking_timeout;
fb83fe64 1282
28ab034a 1283 ret = lttng_channel_get_discarded_event_count(channel, &discarded_events);
fb83fe64
JD
1284 if (ret) {
1285 ERR("Failed to retrieve discarded event count of channel");
1286 return;
1287 }
1288
28ab034a 1289 ret = lttng_channel_get_lost_packet_count(channel, &lost_packets);
fb83fe64
JD
1290 if (ret) {
1291 ERR("Failed to retrieve lost packet count of channel");
1292 return;
1293 }
1294
28ab034a 1295 ret = lttng_channel_get_monitor_timer_interval(channel, &monitor_timer_interval);
cf0bcb51
JG
1296 if (ret) {
1297 ERR("Failed to retrieve monitor interval of channel");
1298 return;
1299 }
1300
28ab034a 1301 ret = lttng_channel_get_blocking_timeout(channel, &blocking_timeout);
491d1539
MD
1302 if (ret) {
1303 ERR("Failed to retrieve blocking timeout of channel");
1304 return;
1305 }
1306
464dd62d 1307 MSG("- %s:%s\n", channel->name, enabled_string(channel->enabled));
9f19cc17 1308 MSG("%sAttributes:", indent4);
f3b3a67b
JG
1309 MSG("%sEvent-loss mode: %s", indent6, channel->attr.overwrite ? "overwrite" : "discard");
1310 MSG("%sSub-buffer size: %" PRIu64 " bytes", indent6, channel->attr.subbuf_size);
1311 MSG("%sSub-buffer count: %" PRIu64, indent6, channel->attr.num_subbuf);
1312
1313 print_timer("Switch timer", 5, channel->attr.switch_timer_interval);
28ab034a 1314 print_timer("Read timer", 7, channel->attr.read_timer_interval);
f3b3a67b
JG
1315 print_timer("Monitor timer", 4, monitor_timer_interval);
1316
1317 if (!channel->attr.overwrite) {
1318 if (blocking_timeout == -1) {
1319 MSG("%sBlocking timeout: infinite", indent6);
1320 } else {
28ab034a
JG
1321 MSG("%sBlocking timeout: %" PRId64 " %s",
1322 indent6,
1323 blocking_timeout,
1324 USEC_UNIT);
f3b3a67b
JG
1325 }
1326 }
1327
28ab034a
JG
1328 MSG("%sTrace file count: %" PRIu64 " per stream",
1329 indent6,
1330 channel->attr.tracefile_count == 0 ? 1 : channel->attr.tracefile_count);
1331 if (channel->attr.tracefile_size != 0) {
1332 MSG("%sTrace file size: %" PRIu64 " bytes", indent6, channel->attr.tracefile_size);
f3b3a67b
JG
1333 } else {
1334 MSG("%sTrace file size: %s", indent6, "unlimited");
1335 }
9f19cc17 1336 switch (channel->attr.output) {
28ab034a
JG
1337 case LTTNG_EVENT_SPLICE:
1338 MSG("%sOutput mode: splice", indent6);
1339 break;
1340 case LTTNG_EVENT_MMAP:
1341 MSG("%sOutput mode: mmap", indent6);
1342 break;
9f19cc17 1343 }
e50e81d3 1344
f3b3a67b 1345 MSG("\n%sStatistics:", indent4);
41493f4a 1346 if (the_listed_session.snapshot_mode) {
e50e81d3
JG
1347 /*
1348 * The lost packet count is omitted for sessions in snapshot
1349 * mode as it is misleading: it would indicate the number of
1350 * packets that the consumer could not extract during the
1351 * course of recording the snapshot. It does not have the
1352 * same meaning as the "regular" lost packet count that
1353 * would result from the consumer not keeping up with
1354 * event production in an overwrite-mode channel.
1355 *
1356 * A more interesting statistic would be the number of
1357 * packets lost between the first and last extracted
1358 * packets of a given snapshot (which prevents most analyses).
1359 */
f3b3a67b 1360 MSG("%sNone", indent6);
e50e81d3
JG
1361 goto skip_stats_printing;
1362 }
1363
1364 if (!channel->attr.overwrite) {
f3b3a67b 1365 MSG("%sDiscarded events: %" PRIu64, indent6, discarded_events);
e50e81d3 1366 } else {
f3b3a67b 1367 MSG("%sLost packets: %" PRIu64, indent6, lost_packets);
e50e81d3
JG
1368 }
1369skip_stats_printing:
1370 return;
9f19cc17
DG
1371}
1372
fb14d0d8
JR
1373/*
1374 * Machine interface
1375 * Print a list of channel
1376 *
1377 */
28ab034a 1378static int mi_list_channels(struct lttng_channel *channels, int count, const char *channel_name)
fb14d0d8
JR
1379{
1380 int i, ret;
1381 unsigned int chan_found = 0;
1382
1383 /* Open channels element */
41493f4a 1384 ret = mi_lttng_channels_open(the_writer);
fb14d0d8
JR
1385 if (ret) {
1386 goto error;
1387 }
1388
1389 for (i = 0; i < count; i++) {
1390 if (channel_name != NULL) {
1391 if (strncmp(channels[i].name, channel_name, NAME_MAX) == 0) {
1392 chan_found = 1;
1393 } else {
1394 continue;
1395 }
1396 }
1397
1398 /* Write channel element and leave it open */
41493f4a 1399 ret = mi_lttng_channel(the_writer, &channels[i], 1);
fb14d0d8
JR
1400 if (ret) {
1401 goto error;
1402 }
1403
1404 /* Listing events per channel */
1405 ret = list_events(channels[i].name);
1406 if (ret) {
1407 goto error;
1408 }
1409
1410 /* Closing the channel element we opened earlier */
41493f4a 1411 ret = mi_lttng_writer_close_element(the_writer);
fb14d0d8
JR
1412 if (ret) {
1413 goto error;
1414 }
1415
1416 if (chan_found) {
1417 break;
1418 }
1419 }
1420
1421 /* Close channels element */
41493f4a 1422 ret = mi_lttng_writer_close_element(the_writer);
fb14d0d8
JR
1423 if (ret) {
1424 goto error;
1425 }
1426
1427error:
1428 return ret;
1429}
1430
9f19cc17
DG
1431/*
1432 * List channel(s) of session and domain.
f3ed775e 1433 *
9f19cc17 1434 * If channel_name is NULL, all channels are listed.
f3ed775e 1435 */
cd80958d 1436static int list_channels(const char *channel_name)
f3ed775e 1437{
9f19cc17
DG
1438 int count, i, ret = CMD_SUCCESS;
1439 unsigned int chan_found = 0;
1440 struct lttng_channel *channels = NULL;
f3ed775e 1441
28ab034a 1442 DBG("Listing channel(s) (%s)", channel_name ?: "<all>");
9f19cc17 1443
41493f4a 1444 count = lttng_list_channels(the_handle, &channels);
f3ed775e 1445 if (count < 0) {
c7d620a2
DG
1446 switch (-count) {
1447 case LTTNG_ERR_KERN_CHAN_NOT_FOUND:
fb14d0d8
JR
1448 if (lttng_opt_mi) {
1449 /* When printing mi this is not an error
1450 * but an empty channels element */
1451 count = 0;
1452 } else {
1453 ret = CMD_SUCCESS;
fb14d0d8
JR
1454 goto error_channels;
1455 }
c7d620a2
DG
1456 break;
1457 default:
1458 /* We had a real error */
fb14d0d8
JR
1459 ret = CMD_ERROR;
1460 ERR("%s", lttng_strerror(count));
1461 goto error_channels;
60e835ca 1462 break;
c7d620a2 1463 }
f3ed775e
DG
1464 }
1465
fb14d0d8
JR
1466 if (lttng_opt_mi) {
1467 /* Mi print */
1468 ret = mi_list_channels(channels, count, channel_name);
1469 if (ret) {
1470 ret = CMD_ERROR;
1471 goto error;
1472 }
1473 } else {
1474 /* Pretty print */
ade7ce52 1475 if (count) {
fb14d0d8
JR
1476 MSG("Channels:\n-------------");
1477 }
9f19cc17 1478
fb14d0d8
JR
1479 for (i = 0; i < count; i++) {
1480 if (channel_name != NULL) {
1481 if (strncmp(channels[i].name, channel_name, NAME_MAX) == 0) {
1482 chan_found = 1;
1483 } else {
1484 continue;
1485 }
1486 }
1487 print_channel(&channels[i]);
1488
1489 /* Listing events per channel */
1490 ret = list_events(channels[i].name);
1491 if (ret) {
1492 goto error;
1493 }
1494
1495 if (chan_found) {
1496 break;
9f19cc17
DG
1497 }
1498 }
9f19cc17 1499
fb14d0d8
JR
1500 if (!chan_found && channel_name != NULL) {
1501 ret = CMD_ERROR;
1502 ERR("Channel %s not found", channel_name);
1503 goto error;
9f19cc17 1504 }
fb14d0d8
JR
1505 }
1506error:
1507 free(channels);
9f19cc17 1508
fb14d0d8
JR
1509error_channels:
1510 return ret;
1511}
1512
159b042f 1513static const char *get_capitalized_process_attr_str(enum lttng_process_attr process_attr)
83d6d6c4 1514{
159b042f
JG
1515 switch (process_attr) {
1516 case LTTNG_PROCESS_ATTR_PROCESS_ID:
1517 return "Process ID";
1518 case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID:
1519 return "Virtual process ID";
1520 case LTTNG_PROCESS_ATTR_USER_ID:
1521 return "User ID";
1522 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID:
1523 return "Virtual user ID";
1524 case LTTNG_PROCESS_ATTR_GROUP_ID:
1525 return "Group ID";
1526 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID:
1527 return "Virtual group ID";
1528 default:
1529 return "Unknown";
83d6d6c4
JR
1530 }
1531 return NULL;
1532}
1533
159b042f 1534static int handle_process_attr_status(enum lttng_process_attr process_attr,
28ab034a 1535 enum lttng_process_attr_tracker_handle_status status)
159b042f
JG
1536{
1537 int ret = CMD_SUCCESS;
1538
1539 switch (status) {
1540 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_INVALID_TRACKING_POLICY:
1541 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK:
1542 /* Carry on. */
1543 break;
1544 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_COMMUNICATION_ERROR:
1545 ERR("Communication occurred while fetching %s tracker",
28ab034a 1546 lttng_process_attr_to_string(process_attr));
159b042f
JG
1547 ret = CMD_ERROR;
1548 break;
1549 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_SESSION_DOES_NOT_EXIST:
1550 ERR("Failed to get the inclusion set of the %s tracker: session `%s` no longer exists",
28ab034a
JG
1551 lttng_process_attr_to_string(process_attr),
1552 the_handle->session_name);
159b042f
JG
1553 ret = CMD_ERROR;
1554 break;
1555 default:
1556 ERR("Unknown error occurred while fetching the inclusion set of the %s tracker",
28ab034a 1557 lttng_process_attr_to_string(process_attr));
159b042f
JG
1558 ret = CMD_ERROR;
1559 break;
1560 }
1561
1562 return ret;
1563}
1564
1565static int mi_output_empty_tracker(enum lttng_process_attr process_attr)
1566{
1567 int ret;
1568
41493f4a 1569 ret = mi_lttng_process_attribute_tracker_open(the_writer, process_attr);
159b042f
JG
1570 if (ret) {
1571 goto end;
1572 }
1573
41493f4a 1574 ret = mi_lttng_close_multi_element(the_writer, 2);
159b042f
JG
1575end:
1576 return ret;
1577}
1578
28ab034a 1579static inline bool is_value_type_name(enum lttng_process_attr_value_type value_type)
159b042f
JG
1580{
1581 return value_type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME ||
28ab034a 1582 value_type == LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME;
159b042f
JG
1583}
1584
a5dfbb9d 1585/*
159b042f 1586 * List a process attribute tracker for a session and domain tuple.
a5dfbb9d 1587 */
159b042f 1588static int list_process_attr_tracker(enum lttng_process_attr process_attr)
a5dfbb9d 1589{
ebbf5ab7 1590 int ret = 0;
159b042f
JG
1591 unsigned int count, i;
1592 enum lttng_tracking_policy policy;
1593 enum lttng_error_code ret_code;
1594 enum lttng_process_attr_tracker_handle_status handle_status;
1595 enum lttng_process_attr_values_status values_status;
1596 const struct lttng_process_attr_values *values;
1597 struct lttng_process_attr_tracker_handle *tracker_handle = NULL;
1598
28ab034a
JG
1599 ret_code = lttng_session_get_tracker_handle(
1600 the_handle->session_name, the_handle->domain.type, process_attr, &tracker_handle);
159b042f 1601 if (ret_code != LTTNG_OK) {
28ab034a 1602 ERR("Failed to get process attribute tracker handle: %s", lttng_strerror(ret_code));
159b042f
JG
1603 ret = CMD_ERROR;
1604 goto end;
1605 }
a5dfbb9d 1606
28ab034a
JG
1607 handle_status =
1608 lttng_process_attr_tracker_handle_get_inclusion_set(tracker_handle, &values);
159b042f
JG
1609 ret = handle_process_attr_status(process_attr, handle_status);
1610 if (ret != CMD_SUCCESS) {
1611 goto end;
a5dfbb9d 1612 }
a7a533cd 1613
28ab034a
JG
1614 handle_status =
1615 lttng_process_attr_tracker_handle_get_tracking_policy(tracker_handle, &policy);
159b042f
JG
1616 ret = handle_process_attr_status(process_attr, handle_status);
1617 if (ret != CMD_SUCCESS) {
e283e4a0
JR
1618 goto end;
1619 }
1620
159b042f
JG
1621 {
1622 char *process_attr_name;
28ab034a
JG
1623 const int print_ret = asprintf(
1624 &process_attr_name, "%ss:", get_capitalized_process_attr_str(process_attr));
159b042f
JG
1625
1626 if (print_ret == -1) {
1627 ret = CMD_FATAL;
1628 goto end;
a7a533cd 1629 }
159b042f
JG
1630 _MSG(" %-22s", process_attr_name);
1631 free(process_attr_name);
1632 }
1633 switch (policy) {
1634 case LTTNG_TRACKING_POLICY_INCLUDE_SET:
1635 break;
1636 case LTTNG_TRACKING_POLICY_EXCLUDE_ALL:
41493f4a 1637 if (the_writer) {
159b042f
JG
1638 mi_output_empty_tracker(process_attr);
1639 }
1640 MSG("none");
1641 ret = CMD_SUCCESS;
1642 goto end;
1643 case LTTNG_TRACKING_POLICY_INCLUDE_ALL:
1644 MSG("all");
1645 ret = CMD_SUCCESS;
1646 goto end;
1647 default:
1648 ERR("Unknown tracking policy encoutered while listing the %s process attribute tracker of session `%s`",
28ab034a
JG
1649 lttng_process_attr_to_string(process_attr),
1650 the_handle->session_name);
159b042f
JG
1651 ret = CMD_FATAL;
1652 goto end;
83d6d6c4 1653 }
a7a533cd 1654
159b042f
JG
1655 values_status = lttng_process_attr_values_get_count(values, &count);
1656 if (values_status != LTTNG_PROCESS_ATTR_VALUES_STATUS_OK) {
1657 ERR("Failed to get the count of values in the inclusion set of the %s process attribute tracker of session `%s`",
28ab034a
JG
1658 lttng_process_attr_to_string(process_attr),
1659 the_handle->session_name);
159b042f
JG
1660 ret = CMD_FATAL;
1661 goto end;
1662 }
a5dfbb9d 1663
159b042f
JG
1664 if (count == 0) {
1665 /* Functionally equivalent to the 'exclude all' policy. */
41493f4a 1666 if (the_writer) {
159b042f 1667 mi_output_empty_tracker(process_attr);
ebbf5ab7 1668 }
159b042f
JG
1669 MSG("none");
1670 ret = CMD_SUCCESS;
1671 goto end;
1672 }
ebbf5ab7 1673
159b042f 1674 /* Mi tracker_id element */
41493f4a 1675 if (the_writer) {
159b042f 1676 /* Open tracker_id and targets elements */
28ab034a 1677 ret = mi_lttng_process_attribute_tracker_open(the_writer, process_attr);
159b042f
JG
1678 if (ret) {
1679 goto end;
1680 }
1681 }
2d97a006 1682
159b042f
JG
1683 for (i = 0; i < count; i++) {
1684 const enum lttng_process_attr_value_type value_type =
28ab034a 1685 lttng_process_attr_values_get_type_at_index(values, i);
159b042f
JG
1686 int64_t integral_value = INT64_MAX;
1687 const char *name = "error";
1688
1689 if (i >= 1) {
1690 _MSG(", ");
1691 }
1692 switch (value_type) {
1693 case LTTNG_PROCESS_ATTR_VALUE_TYPE_PID:
1694 {
1695 pid_t pid;
1696
28ab034a 1697 values_status = lttng_process_attr_values_get_pid_at_index(values, i, &pid);
159b042f
JG
1698 integral_value = (int64_t) pid;
1699 break;
1700 }
1701 case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID:
1702 {
1703 uid_t uid;
a7a533cd 1704
28ab034a 1705 values_status = lttng_process_attr_values_get_uid_at_index(values, i, &uid);
159b042f
JG
1706 integral_value = (int64_t) uid;
1707 break;
1708 }
1709 case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID:
1710 {
1711 gid_t gid;
2d97a006 1712
28ab034a 1713 values_status = lttng_process_attr_values_get_gid_at_index(values, i, &gid);
159b042f
JG
1714 integral_value = (int64_t) gid;
1715 break;
1716 }
1717 case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME:
28ab034a
JG
1718 values_status =
1719 lttng_process_attr_values_get_user_name_at_index(values, i, &name);
159b042f
JG
1720 break;
1721 case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME:
28ab034a
JG
1722 values_status =
1723 lttng_process_attr_values_get_group_name_at_index(values, i, &name);
159b042f
JG
1724 break;
1725 default:
1726 ret = CMD_ERROR;
1727 goto end;
1728 }
83d6d6c4 1729
159b042f
JG
1730 if (values_status != LTTNG_PROCESS_ATTR_VALUES_STATUS_OK) {
1731 /*
1732 * Not possible given the current liblttng-ctl
1733 * implementation.
1734 */
1735 ERR("Unknown error occurred while fetching process attribute value in inclusion list");
1736 ret = CMD_FATAL;
1737 goto end;
1738 }
ebbf5ab7 1739
159b042f
JG
1740 if (is_value_type_name(value_type)) {
1741 _MSG("`%s`", name);
1742 } else {
1743 _MSG("%" PRIi64, integral_value);
a5dfbb9d 1744 }
ebbf5ab7 1745
159b042f 1746 /* Mi */
41493f4a 1747 if (the_writer) {
159b042f 1748 ret = is_value_type_name(value_type) ?
28ab034a
JG
1749 mi_lttng_string_process_attribute_value(
1750 the_writer, process_attr, name, false) :
1751 mi_lttng_integral_process_attribute_value(
1752 the_writer, process_attr, integral_value, false);
ebbf5ab7
JR
1753 if (ret) {
1754 goto end;
1755 }
1756 }
a5dfbb9d 1757 }
159b042f
JG
1758 MSG("");
1759
1760 /* Mi close tracker_id and targets */
41493f4a
SM
1761 if (the_writer) {
1762 ret = mi_lttng_close_multi_element(the_writer, 2);
159b042f
JG
1763 if (ret) {
1764 goto end;
1765 }
1766 }
ebbf5ab7 1767end:
159b042f 1768 lttng_process_attr_tracker_handle_destroy(tracker_handle);
ebbf5ab7 1769 return ret;
ebbf5ab7
JR
1770}
1771
1772/*
83d6d6c4 1773 * List all trackers of a domain
ebbf5ab7 1774 */
83d6d6c4 1775static int list_trackers(const struct lttng_domain *domain)
ebbf5ab7 1776{
0dda6728 1777 int ret = 0;
ebbf5ab7 1778
159b042f 1779 MSG("Tracked process attributes");
ebbf5ab7
JR
1780 /* Trackers listing */
1781 if (lttng_opt_mi) {
41493f4a 1782 ret = mi_lttng_trackers_open(the_writer);
ebbf5ab7
JR
1783 if (ret) {
1784 goto end;
1785 }
1786 }
1787
83d6d6c4
JR
1788 switch (domain->type) {
1789 case LTTNG_DOMAIN_KERNEL:
1790 /* pid tracker */
159b042f 1791 ret = list_process_attr_tracker(LTTNG_PROCESS_ATTR_PROCESS_ID);
83d6d6c4
JR
1792 if (ret) {
1793 goto end;
1794 }
1795 /* vpid tracker */
28ab034a 1796 ret = list_process_attr_tracker(LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID);
83d6d6c4
JR
1797 if (ret) {
1798 goto end;
1799 }
1800 /* uid tracker */
159b042f 1801 ret = list_process_attr_tracker(LTTNG_PROCESS_ATTR_USER_ID);
83d6d6c4
JR
1802 if (ret) {
1803 goto end;
1804 }
1805 /* vuid tracker */
28ab034a 1806 ret = list_process_attr_tracker(LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID);
83d6d6c4
JR
1807 if (ret) {
1808 goto end;
1809 }
1810 /* gid tracker */
159b042f 1811 ret = list_process_attr_tracker(LTTNG_PROCESS_ATTR_GROUP_ID);
83d6d6c4
JR
1812 if (ret) {
1813 goto end;
1814 }
1815 /* vgid tracker */
28ab034a 1816 ret = list_process_attr_tracker(LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID);
83d6d6c4
JR
1817 if (ret) {
1818 goto end;
1819 }
1820 break;
1821 case LTTNG_DOMAIN_UST:
1822 /* vpid tracker */
28ab034a 1823 ret = list_process_attr_tracker(LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID);
83d6d6c4
JR
1824 if (ret) {
1825 goto end;
1826 }
1827 /* vuid tracker */
28ab034a 1828 ret = list_process_attr_tracker(LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID);
83d6d6c4
JR
1829 if (ret) {
1830 goto end;
1831 }
1832 /* vgid tracker */
28ab034a 1833 ret = list_process_attr_tracker(LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID);
83d6d6c4
JR
1834 if (ret) {
1835 goto end;
1836 }
1837 break;
1838 default:
1839 break;
ebbf5ab7 1840 }
159b042f 1841 MSG();
ebbf5ab7
JR
1842 if (lttng_opt_mi) {
1843 /* Close trackers element */
41493f4a 1844 ret = mi_lttng_writer_close_element(the_writer);
ebbf5ab7
JR
1845 if (ret) {
1846 goto end;
1847 }
1848 }
1849
1850end:
1851 return ret;
a5dfbb9d
MD
1852}
1853
28ab034a
JG
1854static enum cmd_error_code
1855print_periodic_rotation_schedule(const struct lttng_rotation_schedule *schedule)
66ea93b1
JG
1856{
1857 enum cmd_error_code ret;
1858 enum lttng_rotation_status status;
1859 uint64_t value;
1860
28ab034a 1861 status = lttng_rotation_schedule_periodic_get_period(schedule, &value);
66ea93b1
JG
1862 if (status != LTTNG_ROTATION_STATUS_OK) {
1863 ERR("Failed to retrieve period parameter from periodic rotation schedule.");
1864 ret = CMD_ERROR;
1865 goto end;
1866 }
1867
28ab034a 1868 MSG(" timer period: %" PRIu64 " %s", value, USEC_UNIT);
66ea93b1
JG
1869 ret = CMD_SUCCESS;
1870end:
1871 return ret;
1872}
1873
28ab034a
JG
1874static enum cmd_error_code
1875print_size_threshold_rotation_schedule(const struct lttng_rotation_schedule *schedule)
66ea93b1
JG
1876{
1877 enum cmd_error_code ret;
1878 enum lttng_rotation_status status;
1879 uint64_t value;
1880
28ab034a 1881 status = lttng_rotation_schedule_size_threshold_get_threshold(schedule, &value);
66ea93b1
JG
1882 if (status != LTTNG_ROTATION_STATUS_OK) {
1883 ERR("Failed to retrieve size parameter from size-based rotation schedule.");
1884 ret = CMD_ERROR;
1885 goto end;
1886 }
1887
28ab034a 1888 MSG(" size threshold: %" PRIu64 " bytes", value);
66ea93b1
JG
1889 ret = CMD_SUCCESS;
1890end:
1891 return ret;
1892}
1893
28ab034a 1894static enum cmd_error_code print_rotation_schedule(const struct lttng_rotation_schedule *schedule)
66ea93b1
JG
1895{
1896 enum cmd_error_code ret;
1897
1898 switch (lttng_rotation_schedule_get_type(schedule)) {
1899 case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD:
1900 ret = print_size_threshold_rotation_schedule(schedule);
1901 break;
1902 case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC:
1903 ret = print_periodic_rotation_schedule(schedule);
1904 break;
1905 default:
1906 ret = CMD_ERROR;
1907 }
1908 return ret;
1909}
1910
329f3443 1911/*
66ea93b1 1912 * List the automatic rotation settings.
329f3443 1913 */
66ea93b1 1914static enum cmd_error_code list_rotate_settings(const char *session_name)
329f3443
JD
1915{
1916 int ret;
66ea93b1
JG
1917 enum cmd_error_code cmd_ret = CMD_SUCCESS;
1918 unsigned int count, i;
1919 struct lttng_rotation_schedules *schedules = NULL;
1920 enum lttng_rotation_status status;
1921
1922 ret = lttng_session_list_rotation_schedules(session_name, &schedules);
1923 if (ret != LTTNG_OK) {
1924 ERR("Failed to list session rotation schedules: %s", lttng_strerror(ret));
1925 cmd_ret = CMD_ERROR;
329f3443
JD
1926 goto end;
1927 }
1928
66ea93b1
JG
1929 status = lttng_rotation_schedules_get_count(schedules, &count);
1930 if (status != LTTNG_ROTATION_STATUS_OK) {
1931 ERR("Failed to retrieve the number of session rotation schedules.");
1932 cmd_ret = CMD_ERROR;
329f3443
JD
1933 goto end;
1934 }
1935
66ea93b1
JG
1936 if (count == 0) {
1937 cmd_ret = CMD_SUCCESS;
329f3443
JD
1938 goto end;
1939 }
1940
66ea93b1
JG
1941 MSG("Automatic rotation schedules:");
1942 if (lttng_opt_mi) {
28ab034a 1943 ret = mi_lttng_writer_open_element(the_writer, mi_lttng_element_rotation_schedules);
66ea93b1
JG
1944 if (ret) {
1945 cmd_ret = CMD_ERROR;
1946 goto end;
329f3443
JD
1947 }
1948 }
66ea93b1
JG
1949
1950 for (i = 0; i < count; i++) {
1951 enum cmd_error_code tmp_ret = CMD_SUCCESS;
1952 const struct lttng_rotation_schedule *schedule;
1953
1954 schedule = lttng_rotation_schedules_get_at_index(schedules, i);
1955 if (!schedule) {
1956 ERR("Failed to retrieve session rotation schedule.");
1957 cmd_ret = CMD_ERROR;
1958 goto end;
1959 }
1960
329f3443 1961 if (lttng_opt_mi) {
41493f4a 1962 ret = mi_lttng_rotation_schedule(the_writer, schedule);
329f3443 1963 if (ret) {
66ea93b1 1964 tmp_ret = CMD_ERROR;
329f3443 1965 }
66ea93b1
JG
1966 } else {
1967 tmp_ret = print_rotation_schedule(schedule);
329f3443 1968 }
66ea93b1
JG
1969
1970 /*
1971 * Report an error if the serialization of any of the
1972 * descriptors failed.
1973 */
1974 cmd_ret = cmd_ret ? cmd_ret : tmp_ret;
329f3443 1975 }
329f3443 1976
66ea93b1
JG
1977 _MSG("\n");
1978 if (lttng_opt_mi) {
1979 /* Close the rotation_schedules element. */
41493f4a 1980 ret = mi_lttng_writer_close_element(the_writer);
66ea93b1
JG
1981 if (ret) {
1982 cmd_ret = CMD_ERROR;
1983 goto end;
1984 }
1985 }
329f3443 1986end:
66ea93b1
JG
1987 lttng_rotation_schedules_destroy(schedules);
1988 return cmd_ret;
329f3443
JD
1989}
1990
fb14d0d8
JR
1991/*
1992 * Machine interface
1993 * Find the session with session_name as name
1994 * and print his informations.
1995 */
28ab034a 1996static int mi_list_session(const char *session_name, struct lttng_session *sessions, int count)
fb14d0d8
JR
1997{
1998 int ret, i;
1999 unsigned int session_found = 0;
2000
2001 if (session_name == NULL) {
2002 ret = -LTTNG_ERR_SESS_NOT_FOUND;
2003 goto end;
2004 }
2005
2006 for (i = 0; i < count; i++) {
2007 if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) {
2008 /* We need to leave it open to append other informations
2009 * like domain, channel, events etc.*/
2010 session_found = 1;
41493f4a 2011 ret = mi_lttng_session(the_writer, &sessions[i], 1);
fb14d0d8
JR
2012 if (ret) {
2013 goto end;
2014 }
9f19cc17 2015 break;
f3ed775e 2016 }
f3ed775e
DG
2017 }
2018
fb14d0d8
JR
2019 if (!session_found) {
2020 ERR("Session '%s' not found", session_name);
2021 ret = -LTTNG_ERR_SESS_NOT_FOUND;
2022 goto end;
9f19cc17 2023 }
f3ed775e 2024
fb14d0d8
JR
2025end:
2026 return ret;
2027}
f3ed775e 2028
fb14d0d8
JR
2029/*
2030 * Machine interface
2031 * List all availables session
2032 */
2033static int mi_list_sessions(struct lttng_session *sessions, int count)
2034{
2035 int ret, i;
ae856491 2036
fb14d0d8 2037 /* Opening sessions element */
41493f4a 2038 ret = mi_lttng_sessions_open(the_writer);
fb14d0d8
JR
2039 if (ret) {
2040 goto end;
2041 }
2042
2043 /* Listing sessions */
2044 for (i = 0; i < count; i++) {
41493f4a 2045 ret = mi_lttng_session(the_writer, &sessions[i], 0);
fb14d0d8
JR
2046 if (ret) {
2047 goto end;
2048 }
2049 }
2050
2051 /* Closing sessions element */
41493f4a 2052 ret = mi_lttng_writer_close_element(the_writer);
fb14d0d8
JR
2053 if (ret) {
2054 goto end;
2055 }
2056
2057end:
f3ed775e
DG
2058 return ret;
2059}
2060
2061/*
9f19cc17 2062 * List available tracing session. List only basic information.
f3ed775e 2063 *
9f19cc17 2064 * If session_name is NULL, all sessions are listed.
f3ed775e 2065 */
9f19cc17 2066static int list_sessions(const char *session_name)
f3ed775e 2067{
fb14d0d8
JR
2068 int ret = CMD_SUCCESS;
2069 int count, i;
9f19cc17 2070 unsigned int session_found = 0;
aff0fa72 2071 struct lttng_session *sessions = NULL;
9f19cc17
DG
2072
2073 count = lttng_list_sessions(&sessions);
2074 DBG("Session count %d", count);
2075 if (count < 0) {
fb14d0d8
JR
2076 ret = CMD_ERROR;
2077 ERR("%s", lttng_strerror(count));
d32fb093 2078 goto end;
9f19cc17
DG
2079 }
2080
fb14d0d8
JR
2081 if (lttng_opt_mi) {
2082 /* Mi */
2083 if (session_name == NULL) {
aff0fa72 2084 /* List all sessions */
fb14d0d8
JR
2085 ret = mi_list_sessions(sessions, count);
2086 } else {
2087 /* Note : this return an open session element */
2088 ret = mi_list_session(session_name, sessions, count);
2089 }
2090 if (ret) {
2091 ret = CMD_ERROR;
aff0fa72 2092 goto end;
fb14d0d8
JR
2093 }
2094 } else {
2095 /* Pretty print */
2096 if (count == 0) {
e9711845 2097 MSG("Currently no available recording session");
fb14d0d8
JR
2098 goto end;
2099 }
9f19cc17 2100
fb14d0d8 2101 if (session_name == NULL) {
e9711845 2102 MSG("Available recording sessions:");
fb14d0d8
JR
2103 }
2104
fb14d0d8
JR
2105 for (i = 0; i < count; i++) {
2106 if (session_name != NULL) {
2107 if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) {
2108 session_found = 1;
28ab034a
JG
2109 MSG("Recording session %s: [%s%s]",
2110 session_name,
2111 active_string(sessions[i].enabled),
2112 snapshot_string(sessions[i].snapshot_mode));
5b89fc92 2113 if (*sessions[i].path) {
28ab034a
JG
2114 MSG("%sTrace output: %s\n",
2115 indent4,
2116 sessions[i].path);
5b89fc92 2117 }
41493f4a 2118 memcpy(&the_listed_session,
28ab034a
JG
2119 &sessions[i],
2120 sizeof(the_listed_session));
fb14d0d8
JR
2121 break;
2122 }
2123 } else {
28ab034a
JG
2124 MSG(" %d) %s [%s%s]",
2125 i + 1,
2126 sessions[i].name,
2127 active_string(sessions[i].enabled),
2128 snapshot_string(sessions[i].snapshot_mode));
5b89fc92
JG
2129 if (*sessions[i].path) {
2130 MSG("%sTrace output: %s", indent4, sessions[i].path);
2131 }
fcbc96a2 2132 if (sessions[i].live_timer_interval != 0) {
28ab034a
JG
2133 MSG("%sLive timer interval: %u %s",
2134 indent4,
2135 sessions[i].live_timer_interval,
2136 USEC_UNIT);
fcbc96a2
JG
2137 }
2138 MSG("");
9f19cc17 2139 }
fb14d0d8
JR
2140 }
2141
2142 if (!session_found && session_name != NULL) {
2143 ERR("Session '%s' not found", session_name);
2144 ret = CMD_ERROR;
aff0fa72 2145 goto end;
fb14d0d8
JR
2146 }
2147
2148 if (session_name == NULL) {
2149 MSG("\nUse lttng list <session_name> for more details");
9f19cc17
DG
2150 }
2151 }
2152
fb14d0d8 2153end:
aff0fa72 2154 free(sessions);
fb14d0d8
JR
2155 return ret;
2156}
9f19cc17 2157
fb14d0d8
JR
2158/*
2159 * Machine Interface
2160 * list available domain(s) for a session.
2161 */
2162static int mi_list_domains(struct lttng_domain *domains, int count)
2163{
2164 int i, ret;
2165 /* Open domains element */
41493f4a 2166 ret = mi_lttng_domains_open(the_writer);
fb14d0d8
JR
2167 if (ret) {
2168 goto end;
9f19cc17
DG
2169 }
2170
fb14d0d8 2171 for (i = 0; i < count; i++) {
41493f4a 2172 ret = mi_lttng_domain(the_writer, &domains[i], 0);
fb14d0d8
JR
2173 if (ret) {
2174 goto end;
2175 }
9f19cc17 2176 }
f3ed775e 2177
fb14d0d8 2178 /* Closing domains element */
41493f4a 2179 ret = mi_lttng_writer_close_element(the_writer);
fb14d0d8
JR
2180 if (ret) {
2181 goto end;
2182 }
d32fb093 2183end:
f3ed775e
DG
2184 return ret;
2185}
f3ed775e
DG
2186
2187/*
9f19cc17 2188 * List available domain(s) for a session.
f3ed775e 2189 */
330be774 2190static int list_domains(const char *session_name)
f3ed775e 2191{
9f19cc17
DG
2192 int i, count, ret = CMD_SUCCESS;
2193 struct lttng_domain *domains = NULL;
2194
330be774 2195 count = lttng_list_domains(session_name, &domains);
9f19cc17 2196 if (count < 0) {
fb14d0d8
JR
2197 ret = CMD_ERROR;
2198 ERR("%s", lttng_strerror(count));
9f19cc17
DG
2199 goto end;
2200 }
2201
fb14d0d8
JR
2202 if (lttng_opt_mi) {
2203 /* Mi output */
2204 ret = mi_list_domains(domains, count);
2205 if (ret) {
2206 ret = CMD_ERROR;
2207 goto error;
2208 }
2209 } else {
2210 /* Pretty print */
2211 MSG("Domains:\n-------------");
2212 if (count == 0) {
2213 MSG(" None");
2214 goto end;
2215 }
2216
2217 for (i = 0; i < count; i++) {
2218 switch (domains[i].type) {
2219 case LTTNG_DOMAIN_KERNEL:
2220 MSG(" - Kernel");
2221 break;
2222 case LTTNG_DOMAIN_UST:
2223 MSG(" - UST global");
2224 break;
2225 case LTTNG_DOMAIN_JUL:
2226 MSG(" - JUL (Java Util Logging)");
2227 break;
5cdb6027
DG
2228 case LTTNG_DOMAIN_LOG4J:
2229 MSG(" - LOG4j (Logging for Java)");
2230 break;
0e115563
DG
2231 case LTTNG_DOMAIN_PYTHON:
2232 MSG(" - Python (logging)");
2233 break;
fb14d0d8
JR
2234 default:
2235 break;
2236 }
9f19cc17
DG
2237 }
2238 }
2239
fb14d0d8 2240error:
9f19cc17
DG
2241 free(domains);
2242
fb14d0d8 2243end:
9f19cc17 2244 return ret;
f3ed775e 2245}
f3ed775e
DG
2246
2247/*
9f19cc17 2248 * The 'list <options>' first level command
f3ed775e
DG
2249 */
2250int cmd_list(int argc, const char **argv)
2251{
c617c0c6 2252 int opt, ret = CMD_SUCCESS;
5b915816 2253 const char *arg_session_name, *leftover = NULL;
f3ed775e 2254 static poptContext pc;
9f19cc17
DG
2255 struct lttng_domain domain;
2256 struct lttng_domain *domains = NULL;
f3ed775e 2257
441c16a7
MD
2258 memset(&domain, 0, sizeof(domain));
2259
9f19cc17 2260 if (argc < 1) {
ca1c3607 2261 ret = CMD_ERROR;
f3ed775e
DG
2262 goto end;
2263 }
2264
2265 pc = poptGetContext(NULL, argc, argv, long_options, 0);
2266 poptReadDefaultConfig(pc, 0);
2267
2268 while ((opt = poptGetNextOpt(pc)) != -1) {
2269 switch (opt) {
2270 case OPT_HELP:
4ba92f18 2271 SHOW_HELP();
f3ed775e 2272 goto end;
eeac7d46
MD
2273 case OPT_USERSPACE:
2274 opt_userspace = 1;
eeac7d46 2275 break;
679b4943
SM
2276 case OPT_LIST_OPTIONS:
2277 list_cmd_options(stdout, long_options);
679b4943 2278 goto end;
f3ed775e 2279 default:
f3ed775e
DG
2280 ret = CMD_UNDEFINED;
2281 goto end;
2282 }
2283 }
2284
fb14d0d8
JR
2285 /* Mi check */
2286 if (lttng_opt_mi) {
28ab034a 2287 the_writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi);
41493f4a 2288 if (!the_writer) {
fb14d0d8
JR
2289 ret = CMD_ERROR;
2290 goto end;
2291 }
2292
2293 /* Open command element */
28ab034a 2294 ret = mi_lttng_writer_command_open(the_writer, mi_lttng_element_command_list);
fb14d0d8
JR
2295 if (ret) {
2296 ret = CMD_ERROR;
2297 goto end;
2298 }
2299
2300 /* Open output element */
28ab034a 2301 ret = mi_lttng_writer_open_element(the_writer, mi_lttng_element_command_output);
fb14d0d8
JR
2302 if (ret) {
2303 ret = CMD_ERROR;
2304 goto end;
2305 }
2306 }
2307
9f19cc17 2308 /* Get session name (trailing argument) */
5b915816
MJ
2309 arg_session_name = poptGetArg(pc);
2310 DBG2("Session name: %s", arg_session_name);
9f19cc17 2311
68c7f6e5
JD
2312 leftover = poptGetArg(pc);
2313 if (leftover) {
2314 ERR("Unknown argument: %s", leftover);
2315 ret = CMD_ERROR;
2316 goto end;
2317 }
2318
cd80958d
DG
2319 if (opt_kernel) {
2320 domain.type = LTTNG_DOMAIN_KERNEL;
b551a063
DG
2321 } else if (opt_userspace) {
2322 DBG2("Listing userspace global domain");
2323 domain.type = LTTNG_DOMAIN_UST;
3c6a091f
DG
2324 } else if (opt_jul) {
2325 DBG2("Listing JUL domain");
2326 domain.type = LTTNG_DOMAIN_JUL;
edf60534 2327 } else if (opt_log4j) {
5cdb6027 2328 domain.type = LTTNG_DOMAIN_LOG4J;
0e115563
DG
2329 } else if (opt_python) {
2330 domain.type = LTTNG_DOMAIN_PYTHON;
cd80958d
DG
2331 }
2332
834978fd
DG
2333 if (!opt_kernel && opt_syscall) {
2334 WARN("--syscall will only work with the Kernel domain (-k)");
2335 ret = CMD_ERROR;
2336 goto end;
2337 }
2338
0e115563 2339 if (opt_kernel || opt_userspace || opt_jul || opt_log4j || opt_python) {
5b915816 2340 the_handle = lttng_create_handle(arg_session_name, &domain);
41493f4a 2341 if (the_handle == NULL) {
aa5de748
MD
2342 ret = CMD_FATAL;
2343 goto end;
2344 }
cd80958d
DG
2345 }
2346
5b915816 2347 if (arg_session_name == NULL) {
28ab034a 2348 if (!opt_kernel && !opt_userspace && !opt_jul && !opt_log4j && !opt_python) {
b551a063 2349 ret = list_sessions(NULL);
fb14d0d8 2350 if (ret) {
b551a063
DG
2351 goto end;
2352 }
2353 }
9f19cc17 2354 if (opt_kernel) {
834978fd
DG
2355 if (opt_syscall) {
2356 ret = list_syscalls();
2357 if (ret) {
2358 goto end;
2359 }
2360 } else {
2361 ret = list_kernel_events();
2362 if (ret) {
2363 goto end;
2364 }
9f19cc17 2365 }
b551a063
DG
2366 }
2367 if (opt_userspace) {
f37d259d
MD
2368 if (opt_fields) {
2369 ret = list_ust_event_fields();
2370 } else {
2371 ret = list_ust_events();
2372 }
fb14d0d8 2373 if (ret) {
9f19cc17
DG
2374 goto end;
2375 }
2376 }
0e115563 2377 if (opt_jul || opt_log4j || opt_python) {
5cdb6027 2378 ret = list_agent_events();
fb14d0d8 2379 if (ret) {
3c6a091f
DG
2380 goto end;
2381 }
2382 }
9f19cc17
DG
2383 } else {
2384 /* List session attributes */
fb14d0d8
JR
2385 if (lttng_opt_mi) {
2386 /* Open element sessions
2387 * Present for xml consistency */
41493f4a 2388 ret = mi_lttng_sessions_open(the_writer);
fb14d0d8
JR
2389 if (ret) {
2390 goto end;
2391 }
2392 }
2393 /* MI: the ouptut of list_sessions is an unclosed session element */
5b915816 2394 ret = list_sessions(arg_session_name);
fb14d0d8 2395 if (ret) {
9f19cc17
DG
2396 goto end;
2397 }
2398
5b915816 2399 ret = list_rotate_settings(arg_session_name);
329f3443
JD
2400 if (ret) {
2401 goto end;
2402 }
2403
9f19cc17
DG
2404 /* Domain listing */
2405 if (opt_domain) {
5b915816 2406 ret = list_domains(arg_session_name);
9f19cc17
DG
2407 goto end;
2408 }
2409
fb14d0d8 2410 /* Channel listing */
1c3de747 2411 if (opt_kernel || opt_userspace) {
fb14d0d8
JR
2412 if (lttng_opt_mi) {
2413 /* Add of domains and domain element for xml
2414 * consistency and validation
2415 */
41493f4a 2416 ret = mi_lttng_domains_open(the_writer);
fb14d0d8
JR
2417 if (ret) {
2418 goto end;
2419 }
2420
2421 /* Open domain and leave it open for
2422 * nested channels printing */
41493f4a 2423 ret = mi_lttng_domain(the_writer, &domain, 1);
fb14d0d8
JR
2424 if (ret) {
2425 goto end;
2426 }
fb14d0d8
JR
2427 }
2428
ebbf5ab7 2429 /* Trackers */
83d6d6c4 2430 ret = list_trackers(&domain);
a5dfbb9d
MD
2431 if (ret) {
2432 goto end;
2433 }
2434
ebbf5ab7 2435 /* Channels */
cd80958d 2436 ret = list_channels(opt_channel);
fb14d0d8
JR
2437 if (ret) {
2438 goto end;
2439 }
2440
2441 if (lttng_opt_mi) {
2442 /* Close domain and domain element */
28ab034a 2443 ret = mi_lttng_close_multi_element(the_writer, 2);
fb14d0d8
JR
2444 }
2445 if (ret) {
9f19cc17
DG
2446 goto end;
2447 }
fb14d0d8 2448
9f19cc17 2449 } else {
c617c0c6
MD
2450 int i, nb_domain;
2451
9f19cc17 2452 /* We want all domain(s) */
5b915816 2453 nb_domain = lttng_list_domains(arg_session_name, &domains);
b551a063 2454 if (nb_domain < 0) {
fb14d0d8
JR
2455 ret = CMD_ERROR;
2456 ERR("%s", lttng_strerror(nb_domain));
9f19cc17
DG
2457 goto end;
2458 }
2459
fb14d0d8 2460 if (lttng_opt_mi) {
41493f4a 2461 ret = mi_lttng_domains_open(the_writer);
fb14d0d8
JR
2462 if (ret) {
2463 ret = CMD_ERROR;
2464 goto end;
2465 }
2466 }
2467
b551a063 2468 for (i = 0; i < nb_domain; i++) {
9f19cc17
DG
2469 switch (domains[i].type) {
2470 case LTTNG_DOMAIN_KERNEL:
ac41e67e 2471 MSG("=== Domain: Linux kernel ===\n");
9f19cc17 2472 break;
b551a063 2473 case LTTNG_DOMAIN_UST:
ac41e67e 2474 MSG("=== Domain: User space ===\n");
74707e6e 2475 MSG("Buffering scheme: %s\n",
28ab034a
JG
2476 domains[i].buf_type == LTTNG_BUFFER_PER_PID ?
2477 "per-process" :
2478 "per-user");
b551a063 2479 break;
3c6a091f 2480 case LTTNG_DOMAIN_JUL:
ac41e67e 2481 MSG("=== Domain: java.util.logging (JUL) ===\n");
3c6a091f 2482 break;
5cdb6027 2483 case LTTNG_DOMAIN_LOG4J:
ac41e67e 2484 MSG("=== Domain: log4j ===\n");
5cdb6027 2485 break;
0e115563 2486 case LTTNG_DOMAIN_PYTHON:
ac41e67e 2487 MSG("=== Domain: Python logging ===\n");
0e115563 2488 break;
9f19cc17
DG
2489 default:
2490 MSG("=== Domain: Unimplemented ===\n");
2491 break;
2492 }
2493
fb14d0d8 2494 if (lttng_opt_mi) {
28ab034a 2495 ret = mi_lttng_domain(the_writer, &domains[i], 1);
fb14d0d8
JR
2496 if (ret) {
2497 ret = CMD_ERROR;
2498 goto end;
2499 }
2500 }
2501
cd80958d 2502 /* Clean handle before creating a new one */
41493f4a
SM
2503 if (the_handle) {
2504 lttng_destroy_handle(the_handle);
aa5de748 2505 }
cd80958d 2506
28ab034a 2507 the_handle = lttng_create_handle(arg_session_name, &domains[i]);
41493f4a 2508 if (the_handle == NULL) {
ca1c3607 2509 ret = CMD_FATAL;
cd80958d
DG
2510 goto end;
2511 }
2512
5cdb6027 2513 if (domains[i].type == LTTNG_DOMAIN_JUL ||
28ab034a
JG
2514 domains[i].type == LTTNG_DOMAIN_LOG4J ||
2515 domains[i].type == LTTNG_DOMAIN_PYTHON) {
5cdb6027 2516 ret = list_session_agent_events();
fb14d0d8 2517 if (ret) {
3c6a091f
DG
2518 goto end;
2519 }
cd7b2927
PP
2520
2521 goto next_domain;
3c6a091f 2522 }
a5dfbb9d
MD
2523
2524 switch (domains[i].type) {
2525 case LTTNG_DOMAIN_KERNEL:
2526 case LTTNG_DOMAIN_UST:
83d6d6c4 2527 ret = list_trackers(&domains[i]);
a5dfbb9d
MD
2528 if (ret) {
2529 goto end;
2530 }
2531 break;
2532 default:
2533 break;
2534 }
3c6a091f 2535
cd80958d 2536 ret = list_channels(opt_channel);
fb14d0d8
JR
2537 if (ret) {
2538 goto end;
2539 }
2540
28ab034a 2541 next_domain:
fb14d0d8
JR
2542 if (lttng_opt_mi) {
2543 /* Close domain element */
28ab034a 2544 ret = mi_lttng_writer_close_element(the_writer);
fb14d0d8
JR
2545 if (ret) {
2546 ret = CMD_ERROR;
2547 goto end;
2548 }
2549 }
fb14d0d8
JR
2550 }
2551 if (lttng_opt_mi) {
2552 /* Close the domains, session and sessions element */
28ab034a 2553 ret = mi_lttng_close_multi_element(the_writer, 3);
fb14d0d8
JR
2554 if (ret) {
2555 ret = CMD_ERROR;
9f19cc17
DG
2556 goto end;
2557 }
2558 }
2559 }
f3ed775e
DG
2560 }
2561
fb14d0d8
JR
2562 /* Mi closing */
2563 if (lttng_opt_mi) {
2564 /* Close output element */
41493f4a 2565 ret = mi_lttng_writer_close_element(the_writer);
fb14d0d8
JR
2566 if (ret) {
2567 ret = CMD_ERROR;
2568 goto end;
2569 }
2570
2571 /* Command element close */
41493f4a 2572 ret = mi_lttng_writer_command_close(the_writer);
fb14d0d8
JR
2573 if (ret) {
2574 ret = CMD_ERROR;
2575 goto end;
2576 }
2577 }
f3ed775e 2578end:
fb14d0d8 2579 /* Mi clean-up */
41493f4a 2580 if (the_writer && mi_lttng_writer_destroy(the_writer)) {
fb14d0d8
JR
2581 /* Preserve original error code */
2582 ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL;
2583 }
2584
0e428499 2585 free(domains);
41493f4a
SM
2586 if (the_handle) {
2587 lttng_destroy_handle(the_handle);
aa5de748 2588 }
cd80958d 2589
ca1c3607 2590 poptFreeContext(pc);
f3ed775e
DG
2591 return ret;
2592}
This page took 0.210734 seconds and 4 git commands to generate.