From: Simon Marchi Date: Fri, 3 Sep 2021 21:31:28 +0000 (-0400) Subject: bin: compile lttng as C++ X-Git-Url: https://git.liburcu.org/?p=lttng-tools.git;a=commitdiff_plain;h=48a4000561343808724f7cb5fa8c131877489ccd bin: compile lttng as C++ Compile the code of the lttng binary as C++ source. - start by renaming all files under src/bin/lttng to have the .cpp extension, adjust Makefile.am accordingly - apply the sophisticated algorithm: while does_not_build(): fix_error() until completion Fixes fall in these categories: - add extern "C" to headers of functions implemented in C. This is likely temporary: at some point some of these things will be implemented in C++, at which point we'll remove the extern "C". - rename mi_lttng_version to mi_lttng_version_data, to avoid a -Wshadow warning about the mi_lttng_version function hiding the mi_lttng_version's struct constructor - we have the same warning about lttng_calibrate, but we can't rename it, it's exposed in a public header. Add some pragmas to disable the warning around there. We will need more macro smartness in case we need to support a compiler that doesn't understand these pragmas. - in filter-ast.h, add a dummy field to the empty struct, to avoid a -Wextern-c-compat warning with clang++ (it warns us that the struct has size 0 in C but size 1 in C++). - in add_context.cpp, we can't initialize ctx_opts' union field like we did in C. Fix that by adding a ctx_opts constructor for each kind of context and implement the PERF_* macros to use them. - need to explicitly cast void pointer to type of the destination, for example the eturn value of allocation functions, or parameter of "destroy" functions - need to explicitly cast when passing an int to an enum parameter, for example an lttng_error_code parameter - remove use of designated array initializers, for example for schedule_type_str in disable_rotation.cpp - fix order of struct initializers to match order of field declarations, for example in list_triggers.cpp, function cmd_list_triggers - rename some things to avoid clashing with keywords, for example in runas.h Change-Id: Id743b141552a412b4104af4dda8969eef5032388 Signed-off-by: Simon Marchi Signed-off-by: Jérémie Galarneau --- diff --git a/configure.ac b/configure.ac index ea327f9b8..12cc7a17e 100644 --- a/configure.ac +++ b/configure.ac @@ -71,6 +71,9 @@ m4_define([WARN_FLAGS_LIST], [ dnl -Wmissing-parameter-type dnl -Wshadow dnl -Wno-gnu-folding-constant dnl + dnl GCC enables this with -Wall in C++, and that generates a + dnl lot of warnings that have on average a low value to fix. + -Wno-sign-compare dnl ]) # Pass -Werror as an extra flag during the test: this is needed to make the diff --git a/include/lttng/condition/condition-internal.h b/include/lttng/condition/condition-internal.h index 5bafaf05b..a491d3fe3 100644 --- a/include/lttng/condition/condition-internal.h +++ b/include/lttng/condition/condition-internal.h @@ -19,6 +19,10 @@ #include #include +#if defined(__cplusplus) +extern "C" { +#endif + struct mi_writer; struct mi_lttng_error_query_callbacks; struct lttng_trigger; @@ -82,4 +86,8 @@ enum lttng_error_code lttng_condition_mi_serialize( const char *lttng_condition_type_str(enum lttng_condition_type type); +#if defined(__cplusplus) +} +#endif + #endif /* LTTNG_CONDITION_INTERNAL_H */ diff --git a/include/lttng/event-rule/event-rule-internal.h b/include/lttng/event-rule/event-rule-internal.h index 802674863..98d1f449d 100644 --- a/include/lttng/event-rule/event-rule-internal.h +++ b/include/lttng/event-rule/event-rule-internal.h @@ -20,6 +20,10 @@ #include #include +#if defined(__cplusplus) +extern "C" { +#endif + struct lttng_payload; struct lttng_payload_view; struct mi_writer; @@ -153,4 +157,8 @@ bool lttng_event_rule_targets_agent_domain(const struct lttng_event_rule *rule); enum lttng_error_code lttng_event_rule_mi_serialize( const struct lttng_event_rule *rule, struct mi_writer *writer); +#if defined(__cplusplus) +} +#endif + #endif /* LTTNG_EVENT_RULE_INTERNAL_H */ diff --git a/include/lttng/event-rule/kernel-syscall-internal.h b/include/lttng/event-rule/kernel-syscall-internal.h index 31905cef6..3e5e0991d 100644 --- a/include/lttng/event-rule/kernel-syscall-internal.h +++ b/include/lttng/event-rule/kernel-syscall-internal.h @@ -13,6 +13,10 @@ #include #include +#if defined(__cplusplus) +extern "C" { +#endif + struct lttng_event_rule_kernel_syscall { struct lttng_event_rule parent; enum lttng_event_rule_kernel_syscall_emission_site emission_site; @@ -46,4 +50,9 @@ ssize_t lttng_event_rule_kernel_syscall_create_from_payload( const char *lttng_event_rule_kernel_syscall_emission_site_str( enum lttng_event_rule_kernel_syscall_emission_site emission_site); + +#if defined(__cplusplus) +} +#endif + #endif /* LTTNG_EVENT_RULE_KERNEL_SYSCALL_INTERNAL_H */ diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index 5f3df9113..01c4089e5 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -148,8 +148,11 @@ extern int lttng_stop_tracing_no_wait(const char *session_name); * Deprecated: As of LTTng 2.9, this function always returns * -LTTNG_ERR_UND. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" extern int lttng_calibrate(struct lttng_handle *handle, struct lttng_calibrate *calibrate); +#pragma GCC diagnostic pop /* * Set URL for a consumer for a session and domain. diff --git a/include/lttng/trigger/trigger-internal.h b/include/lttng/trigger/trigger-internal.h index dab46ae07..69226bce7 100644 --- a/include/lttng/trigger/trigger-internal.h +++ b/include/lttng/trigger/trigger-internal.h @@ -19,6 +19,10 @@ #include #include +#if defined(__cplusplus) +extern "C" { +#endif + struct lttng_payload; struct lttng_payload_view; struct mi_writer; @@ -278,4 +282,8 @@ enum lttng_trigger_status lttng_trigger_add_action_error_query_results( enum lttng_trigger_status lttng_trigger_set_name( struct lttng_trigger *trigger, const char *name); +#if defined(__cplusplus) +} +#endif + #endif /* LTTNG_TRIGGER_INTERNAL_H */ diff --git a/src/bin/lttng/Makefile.am b/src/bin/lttng/Makefile.am index 50ab92988..5d01c45a8 100644 --- a/src/bin/lttng/Makefile.am +++ b/src/bin/lttng/Makefile.am @@ -10,33 +10,33 @@ AUTOMAKE_OPTIONS = subdir-objects bin_PROGRAMS = lttng -lttng_SOURCES = command.h conf.c conf.h commands/start.c \ - commands/list.c commands/create.c commands/destroy.c \ - commands/stop.c commands/enable_events.c \ - commands/disable_events.c commands/enable_channels.c \ - commands/disable_channels.c commands/add_context.c \ - commands/set_session.c commands/version.c \ - commands/view.c \ - commands/snapshot.c \ - commands/save.c \ - commands/load.c \ - commands/track-untrack.c \ - commands/status.c \ - commands/metadata.c \ - commands/regenerate.c \ - commands/help.c \ - commands/rotate.c \ - commands/enable_rotation.c \ - commands/disable_rotation.c \ - commands/clear.c \ - loglevel.c loglevel.h \ - commands/add_trigger.c \ - commands/list_triggers.c \ - commands/remove_trigger.c \ - utils.c utils.h lttng.c \ - uprobe.c uprobe.h +lttng_SOURCES = command.h conf.cpp conf.h commands/start.cpp \ + commands/list.cpp commands/create.cpp commands/destroy.cpp \ + commands/stop.cpp commands/enable_events.cpp \ + commands/disable_events.cpp commands/enable_channels.cpp \ + commands/disable_channels.cpp commands/add_context.cpp \ + commands/set_session.cpp commands/version.cpp \ + commands/view.cpp \ + commands/snapshot.cpp \ + commands/save.cpp \ + commands/load.cpp \ + commands/track-untrack.cpp \ + commands/status.cpp \ + commands/metadata.cpp \ + commands/regenerate.cpp \ + commands/help.cpp \ + commands/rotate.cpp \ + commands/enable_rotation.cpp \ + commands/disable_rotation.cpp \ + commands/clear.cpp \ + loglevel.cpp loglevel.h \ + commands/add_trigger.cpp \ + commands/list_triggers.cpp \ + commands/remove_trigger.cpp \ + utils.cpp utils.h lttng.cpp \ + uprobe.cpp uprobe.h -lttng_CFLAGS = $(AM_CFLAGS) $(POPT_CFLAGS) +lttng_CXXFLAGS = $(AM_CXXFLAGS) $(POPT_CFLAGS) lttng_LDADD = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \ $(top_builddir)/src/common/libcommon.la \ diff --git a/src/bin/lttng/commands/add_context.c b/src/bin/lttng/commands/add_context.c deleted file mode 100644 index 90a54f7ee..000000000 --- a/src/bin/lttng/commands/add_context.c +++ /dev/null @@ -1,1209 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * Copyright (C) 2016 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "../command.h" - -static char *opt_channel_name; -static char *opt_session_name; -static int opt_kernel; -static int opt_userspace; -static int opt_jul; -static int opt_log4j; -static char *opt_type; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_TYPE, - OPT_USERSPACE, - OPT_JUL, - OPT_LOG4J, - OPT_LIST_OPTIONS, - OPT_LIST, -}; - -static struct lttng_handle *handle; -static struct mi_writer *writer; - -/* - * Taken from the LTTng ABI - */ -enum context_type { - CONTEXT_PID = 0, - CONTEXT_PERF_COUNTER = 1, /* Backward compat. */ - CONTEXT_PROCNAME = 2, - CONTEXT_PRIO = 3, - CONTEXT_NICE = 4, - CONTEXT_VPID = 5, - CONTEXT_TID = 6, - CONTEXT_VTID = 7, - CONTEXT_PPID = 8, - CONTEXT_VPPID = 9, - CONTEXT_PTHREAD_ID = 10, - CONTEXT_HOSTNAME = 11, - CONTEXT_IP = 12, - CONTEXT_PERF_CPU_COUNTER = 13, - CONTEXT_PERF_THREAD_COUNTER = 14, - CONTEXT_APP_CONTEXT = 15, - CONTEXT_INTERRUPTIBLE = 16, - CONTEXT_PREEMPTIBLE = 17, - CONTEXT_NEED_RESCHEDULE = 18, - CONTEXT_MIGRATABLE = 19, - CONTEXT_CALLSTACK_KERNEL = 20, - CONTEXT_CALLSTACK_USER = 21, - CONTEXT_CGROUP_NS = 22, - CONTEXT_IPC_NS = 23, - CONTEXT_MNT_NS = 24, - CONTEXT_NET_NS = 25, - CONTEXT_PID_NS = 26, - CONTEXT_USER_NS = 27, - CONTEXT_UTS_NS = 28, - CONTEXT_UID = 29, - CONTEXT_EUID = 30, - CONTEXT_SUID = 31, - CONTEXT_GID = 32, - CONTEXT_EGID = 33, - CONTEXT_SGID = 34, - CONTEXT_VUID = 35, - CONTEXT_VEUID = 36, - CONTEXT_VSUID = 37, - CONTEXT_VGID = 38, - CONTEXT_VEGID = 39, - CONTEXT_VSGID = 40, - CONTEXT_TIME_NS = 41, -}; - -/* - * Taken from the Perf ABI (all enum perf_*) - */ -enum perf_type { - PERF_TYPE_HARDWARE = 0, - PERF_TYPE_SOFTWARE = 1, - PERF_TYPE_HW_CACHE = 3, - PERF_TYPE_RAW = 4, -}; - -enum perf_count_hard { - PERF_COUNT_HW_CPU_CYCLES = 0, - PERF_COUNT_HW_INSTRUCTIONS = 1, - PERF_COUNT_HW_CACHE_REFERENCES = 2, - PERF_COUNT_HW_CACHE_MISSES = 3, - PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, - PERF_COUNT_HW_BRANCH_MISSES = 5, - PERF_COUNT_HW_BUS_CYCLES = 6, - PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7, - PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8, -}; - -enum perf_count_soft { - PERF_COUNT_SW_CPU_CLOCK = 0, - PERF_COUNT_SW_TASK_CLOCK = 1, - PERF_COUNT_SW_PAGE_FAULTS = 2, - PERF_COUNT_SW_CONTEXT_SWITCHES = 3, - PERF_COUNT_SW_CPU_MIGRATIONS = 4, - PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, - PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, - PERF_COUNT_SW_ALIGNMENT_FAULTS = 7, - PERF_COUNT_SW_EMULATION_FAULTS = 8, -}; - -/* - * Generalized hardware cache events: - * - * { L1-D, L1-I, LLC, ITLB, DTLB, BPU } x - * { read, write, prefetch } x - * { accesses, misses } - */ -enum perf_hw_cache_id { - PERF_COUNT_HW_CACHE_L1D = 0, - PERF_COUNT_HW_CACHE_L1I = 1, - PERF_COUNT_HW_CACHE_LL = 2, - PERF_COUNT_HW_CACHE_DTLB = 3, - PERF_COUNT_HW_CACHE_ITLB = 4, - PERF_COUNT_HW_CACHE_BPU = 5, - - PERF_COUNT_HW_CACHE_MAX, /* non-ABI */ -}; - -enum perf_hw_cache_op_id { - PERF_COUNT_HW_CACHE_OP_READ = 0, - PERF_COUNT_HW_CACHE_OP_WRITE = 1, - PERF_COUNT_HW_CACHE_OP_PREFETCH = 2, - - PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */ -}; - -enum perf_hw_cache_op_result_id { - PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0, - PERF_COUNT_HW_CACHE_RESULT_MISS = 1, - - PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */ -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, - {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0}, - {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, - {"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0}, - {"jul", 'j', POPT_ARG_NONE, 0, OPT_JUL, 0, 0}, - {"log4j", 'l', POPT_ARG_NONE, 0, OPT_LOG4J, 0, 0}, - {"type", 't', POPT_ARG_STRING, &opt_type, OPT_TYPE, 0, 0}, - {"list", 0, POPT_ARG_NONE, NULL, OPT_LIST, NULL, NULL}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * Context options - */ -#define PERF_HW(optstr, name, type, hide) \ - { \ - (char *) optstr, type, hide, \ - .u.perf = { PERF_TYPE_HARDWARE, PERF_COUNT_HW_##name, },\ - } - -#define PERF_SW(optstr, name, type, hide) \ - { \ - (char *) optstr, type, hide, \ - .u.perf = { PERF_TYPE_SOFTWARE, PERF_COUNT_SW_##name, },\ - } - -#define _PERF_HW_CACHE(optstr, name, type, op, result, hide) \ - { \ - (char *) optstr, type, hide, \ - .u.perf = { \ - PERF_TYPE_HW_CACHE, \ - (uint64_t) PERF_COUNT_HW_CACHE_##name \ - | ((uint64_t) PERF_COUNT_HW_CACHE_OP_##op << 8) \ - | ((uint64_t) PERF_COUNT_HW_CACHE_RESULT_##result << 16), \ - }, \ - } - -#define PERF_HW_CACHE(optstr, name, type, hide) \ - _PERF_HW_CACHE(optstr "-loads", name, type, \ - READ, ACCESS, hide), \ - _PERF_HW_CACHE(optstr "-load-misses", name, type, \ - READ, MISS, hide), \ - _PERF_HW_CACHE(optstr "-stores", name, type, \ - WRITE, ACCESS, hide), \ - _PERF_HW_CACHE(optstr "-store-misses", name, type, \ - WRITE, MISS, hide), \ - _PERF_HW_CACHE(optstr "-prefetches", name, type, \ - PREFETCH, ACCESS, hide), \ - _PERF_HW_CACHE(optstr "-prefetch-misses", name, type, \ - PREFETCH, MISS, hide) - -static -const struct ctx_opts { - char *symbol; - enum context_type ctx_type; - int hide_help; /* Hide from --help */ - union { - struct { - uint32_t type; - uint64_t config; - } perf; - struct { - char *provider_name; - char *ctx_name; - } app_ctx; - } u; -} ctx_opts[] = { - /* - * These (char *) casts (as well as those in the PERF_* macros) are - * safe because we never free these instances of `struct ctx_opts`. - */ - { (char *) "pid", CONTEXT_PID }, - { (char *) "procname", CONTEXT_PROCNAME }, - { (char *) "prio", CONTEXT_PRIO }, - { (char *) "nice", CONTEXT_NICE }, - { (char *) "vpid", CONTEXT_VPID }, - { (char *) "tid", CONTEXT_TID }, - { (char *) "pthread_id", CONTEXT_PTHREAD_ID }, - { (char *) "vtid", CONTEXT_VTID }, - { (char *) "ppid", CONTEXT_PPID }, - { (char *) "vppid", CONTEXT_VPPID }, - { (char *) "hostname", CONTEXT_HOSTNAME }, - { (char *) "ip", CONTEXT_IP }, - { (char *) "interruptible", CONTEXT_INTERRUPTIBLE }, - { (char *) "preemptible", CONTEXT_PREEMPTIBLE }, - { (char *) "need_reschedule", CONTEXT_NEED_RESCHEDULE }, - { (char *) "migratable", CONTEXT_MIGRATABLE }, - { (char *) "callstack-kernel", CONTEXT_CALLSTACK_KERNEL }, -#if HAVE_MODULES_USERSPACE_CALLSTACK_CONTEXT - { (char *) "callstack-user", CONTEXT_CALLSTACK_USER }, -#endif - { (char *) "cgroup_ns", CONTEXT_CGROUP_NS }, - { (char *) "ipc_ns", CONTEXT_IPC_NS }, - { (char *) "mnt_ns", CONTEXT_MNT_NS }, - { (char *) "net_ns", CONTEXT_NET_NS }, - { (char *) "pid_ns", CONTEXT_PID_NS }, - { (char *) "time_ns", CONTEXT_TIME_NS }, - { (char *) "user_ns", CONTEXT_USER_NS }, - { (char *) "uts_ns", CONTEXT_UTS_NS }, - { (char *) "uid", CONTEXT_UID }, - { (char *) "euid", CONTEXT_EUID }, - { (char *) "suid", CONTEXT_SUID }, - { (char *) "gid", CONTEXT_GID }, - { (char *) "egid", CONTEXT_EGID }, - { (char *) "sgid", CONTEXT_SGID }, - { (char *) "vuid", CONTEXT_VUID }, - { (char *) "veuid", CONTEXT_VEUID }, - { (char *) "vsuid", CONTEXT_VSUID }, - { (char *) "vgid", CONTEXT_VGID }, - { (char *) "vegid", CONTEXT_VEGID }, - { (char *) "vsgid", CONTEXT_VSGID }, - - /* Perf options */ - - /* Perf per-CPU counters */ - PERF_HW("perf:cpu:cpu-cycles", CPU_CYCLES, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_HW("perf:cpu:cycles", CPU_CYCLES, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_HW("perf:cpu:stalled-cycles-frontend", STALLED_CYCLES_FRONTEND, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_HW("perf:cpu:idle-cycles-frontend", STALLED_CYCLES_FRONTEND, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_HW("perf:cpu:stalled-cycles-backend", STALLED_CYCLES_BACKEND, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_HW("perf:cpu:idle-cycles-backend", STALLED_CYCLES_BACKEND, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_HW("perf:cpu:instructions", INSTRUCTIONS, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_HW("perf:cpu:cache-references", CACHE_REFERENCES, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_HW("perf:cpu:cache-misses", CACHE_MISSES, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_HW("perf:cpu:branch-instructions", BRANCH_INSTRUCTIONS, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_HW("perf:cpu:branches", BRANCH_INSTRUCTIONS, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_HW("perf:cpu:branch-misses", BRANCH_MISSES, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_HW("perf:cpu:bus-cycles", BUS_CYCLES, - CONTEXT_PERF_CPU_COUNTER, 0), - - PERF_HW_CACHE("perf:cpu:L1-dcache", L1D, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_HW_CACHE("perf:cpu:L1-icache", L1I, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_HW_CACHE("perf:cpu:LLC", LL, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_HW_CACHE("perf:cpu:dTLB", DTLB, - CONTEXT_PERF_CPU_COUNTER, 0), - _PERF_HW_CACHE("perf:cpu:iTLB-loads", ITLB, - CONTEXT_PERF_CPU_COUNTER, READ, ACCESS, 0), - _PERF_HW_CACHE("perf:cpu:iTLB-load-misses", ITLB, - CONTEXT_PERF_CPU_COUNTER, READ, MISS, 0), - _PERF_HW_CACHE("perf:cpu:branch-loads", BPU, - CONTEXT_PERF_CPU_COUNTER, READ, ACCESS, 0), - _PERF_HW_CACHE("perf:cpu:branch-load-misses", BPU, - CONTEXT_PERF_CPU_COUNTER, READ, MISS, 0), - - PERF_SW("perf:cpu:cpu-clock", CPU_CLOCK, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_SW("perf:cpu:task-clock", TASK_CLOCK, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_SW("perf:cpu:page-fault", PAGE_FAULTS, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_SW("perf:cpu:faults", PAGE_FAULTS, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_SW("perf:cpu:major-faults", PAGE_FAULTS_MAJ, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_SW("perf:cpu:minor-faults", PAGE_FAULTS_MIN, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_SW("perf:cpu:context-switches", CONTEXT_SWITCHES, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_SW("perf:cpu:cs", CONTEXT_SWITCHES, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_SW("perf:cpu:cpu-migrations", CPU_MIGRATIONS, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_SW("perf:cpu:migrations", CPU_MIGRATIONS, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_SW("perf:cpu:alignment-faults", ALIGNMENT_FAULTS, - CONTEXT_PERF_CPU_COUNTER, 0), - PERF_SW("perf:cpu:emulation-faults", EMULATION_FAULTS, - CONTEXT_PERF_CPU_COUNTER, 0), - - /* Perf per-thread counters */ - PERF_HW("perf:thread:cpu-cycles", CPU_CYCLES, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_HW("perf:thread:cycles", CPU_CYCLES, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_HW("perf:thread:stalled-cycles-frontend", STALLED_CYCLES_FRONTEND, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_HW("perf:thread:idle-cycles-frontend", STALLED_CYCLES_FRONTEND, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_HW("perf:thread:stalled-cycles-backend", STALLED_CYCLES_BACKEND, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_HW("perf:thread:idle-cycles-backend", STALLED_CYCLES_BACKEND, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_HW("perf:thread:instructions", INSTRUCTIONS, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_HW("perf:thread:cache-references", CACHE_REFERENCES, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_HW("perf:thread:cache-misses", CACHE_MISSES, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_HW("perf:thread:branch-instructions", BRANCH_INSTRUCTIONS, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_HW("perf:thread:branches", BRANCH_INSTRUCTIONS, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_HW("perf:thread:branch-misses", BRANCH_MISSES, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_HW("perf:thread:bus-cycles", BUS_CYCLES, - CONTEXT_PERF_THREAD_COUNTER, 0), - - PERF_HW_CACHE("perf:thread:L1-dcache", L1D, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_HW_CACHE("perf:thread:L1-icache", L1I, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_HW_CACHE("perf:thread:LLC", LL, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_HW_CACHE("perf:thread:dTLB", DTLB, - CONTEXT_PERF_THREAD_COUNTER, 0), - _PERF_HW_CACHE("perf:thread:iTLB-loads", ITLB, - CONTEXT_PERF_THREAD_COUNTER, READ, ACCESS, 0), - _PERF_HW_CACHE("perf:thread:iTLB-load-misses", ITLB, - CONTEXT_PERF_THREAD_COUNTER, READ, MISS, 0), - _PERF_HW_CACHE("perf:thread:branch-loads", BPU, - CONTEXT_PERF_THREAD_COUNTER, READ, ACCESS, 0), - _PERF_HW_CACHE("perf:thread:branch-load-misses", BPU, - CONTEXT_PERF_THREAD_COUNTER, READ, MISS, 0), - - PERF_SW("perf:thread:cpu-clock", CPU_CLOCK, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_SW("perf:thread:task-clock", TASK_CLOCK, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_SW("perf:thread:page-fault", PAGE_FAULTS, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_SW("perf:thread:faults", PAGE_FAULTS, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_SW("perf:thread:major-faults", PAGE_FAULTS_MAJ, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_SW("perf:thread:minor-faults", PAGE_FAULTS_MIN, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_SW("perf:thread:context-switches", CONTEXT_SWITCHES, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_SW("perf:thread:cs", CONTEXT_SWITCHES, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_SW("perf:thread:cpu-migrations", CPU_MIGRATIONS, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_SW("perf:thread:migrations", CPU_MIGRATIONS, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_SW("perf:thread:alignment-faults", ALIGNMENT_FAULTS, - CONTEXT_PERF_THREAD_COUNTER, 0), - PERF_SW("perf:thread:emulation-faults", EMULATION_FAULTS, - CONTEXT_PERF_THREAD_COUNTER, 0), - - /* - * Perf per-CPU counters, backward compatibilty for names. - * Hidden from help listing. - */ - PERF_HW("perf:cpu-cycles", CPU_CYCLES, - CONTEXT_PERF_COUNTER, 1), - PERF_HW("perf:cycles", CPU_CYCLES, - CONTEXT_PERF_COUNTER, 1), - PERF_HW("perf:stalled-cycles-frontend", STALLED_CYCLES_FRONTEND, - CONTEXT_PERF_COUNTER, 1), - PERF_HW("perf:idle-cycles-frontend", STALLED_CYCLES_FRONTEND, - CONTEXT_PERF_COUNTER, 1), - PERF_HW("perf:stalled-cycles-backend", STALLED_CYCLES_BACKEND, - CONTEXT_PERF_COUNTER, 1), - PERF_HW("perf:idle-cycles-backend", STALLED_CYCLES_BACKEND, - CONTEXT_PERF_COUNTER, 1), - PERF_HW("perf:instructions", INSTRUCTIONS, - CONTEXT_PERF_COUNTER, 1), - PERF_HW("perf:cache-references", CACHE_REFERENCES, - CONTEXT_PERF_COUNTER, 1), - PERF_HW("perf:cache-misses", CACHE_MISSES, - CONTEXT_PERF_COUNTER, 1), - PERF_HW("perf:branch-instructions", BRANCH_INSTRUCTIONS, - CONTEXT_PERF_COUNTER, 1), - PERF_HW("perf:branches", BRANCH_INSTRUCTIONS, - CONTEXT_PERF_COUNTER, 1), - PERF_HW("perf:branch-misses", BRANCH_MISSES, - CONTEXT_PERF_COUNTER, 1), - PERF_HW("perf:bus-cycles", BUS_CYCLES, - CONTEXT_PERF_COUNTER, 1), - - PERF_HW_CACHE("perf:L1-dcache", L1D, - CONTEXT_PERF_COUNTER, 1), - PERF_HW_CACHE("perf:L1-icache", L1I, - CONTEXT_PERF_COUNTER, 1), - PERF_HW_CACHE("perf:LLC", LL, - CONTEXT_PERF_COUNTER, 1), - PERF_HW_CACHE("perf:dTLB", DTLB, - CONTEXT_PERF_COUNTER, 1), - _PERF_HW_CACHE("perf:iTLB-loads", ITLB, - CONTEXT_PERF_COUNTER, READ, ACCESS, 1), - _PERF_HW_CACHE("perf:iTLB-load-misses", ITLB, - CONTEXT_PERF_COUNTER, READ, MISS, 1), - _PERF_HW_CACHE("perf:branch-loads", BPU, - CONTEXT_PERF_COUNTER, READ, ACCESS, 1), - _PERF_HW_CACHE("perf:branch-load-misses", BPU, - CONTEXT_PERF_COUNTER, READ, MISS, 1), - - PERF_SW("perf:cpu-clock", CPU_CLOCK, - CONTEXT_PERF_COUNTER, 1), - PERF_SW("perf:task-clock", TASK_CLOCK, - CONTEXT_PERF_COUNTER, 1), - PERF_SW("perf:page-fault", PAGE_FAULTS, - CONTEXT_PERF_COUNTER, 1), - PERF_SW("perf:faults", PAGE_FAULTS, - CONTEXT_PERF_COUNTER, 1), - PERF_SW("perf:major-faults", PAGE_FAULTS_MAJ, - CONTEXT_PERF_COUNTER, 1), - PERF_SW("perf:minor-faults", PAGE_FAULTS_MIN, - CONTEXT_PERF_COUNTER, 1), - PERF_SW("perf:context-switches", CONTEXT_SWITCHES, - CONTEXT_PERF_COUNTER, 1), - PERF_SW("perf:cs", CONTEXT_SWITCHES, - CONTEXT_PERF_COUNTER, 1), - PERF_SW("perf:cpu-migrations", CPU_MIGRATIONS, - CONTEXT_PERF_COUNTER, 1), - PERF_SW("perf:migrations", CPU_MIGRATIONS, - CONTEXT_PERF_COUNTER, 1), - PERF_SW("perf:alignment-faults", ALIGNMENT_FAULTS, - CONTEXT_PERF_COUNTER, 1), - PERF_SW("perf:emulation-faults", EMULATION_FAULTS, - CONTEXT_PERF_COUNTER, 1), - - { NULL, -1 }, /* Closure */ -}; - -#undef PERF_HW_CACHE -#undef _PERF_HW_CACHE -#undef PERF_SW -#undef PERF_HW - -/* - * Context type for command line option parsing. - */ -struct ctx_type { - struct ctx_opts *opt; - struct cds_list_head list; -}; - -/* - * List of context type. Use to enable multiple context on a single command - * line entry. - */ -struct ctx_type_list { - struct cds_list_head head; -} ctx_type_list = { - .head = CDS_LIST_HEAD_INIT(ctx_type_list.head), -}; - - - -/* - * Find context numerical value from string. - * - * Return -1 if not found. - */ -static int find_ctx_type_idx(const char *opt) -{ - int ret, i = 0; - - while (ctx_opts[i].symbol != NULL) { - if (strcmp(opt, ctx_opts[i].symbol) == 0) { - ret = i; - goto end; - } - i++; - } - - ret = -1; -end: - return ret; -} - -static -enum lttng_domain_type get_domain(void) -{ - if (opt_kernel) { - return LTTNG_DOMAIN_KERNEL; - } else if (opt_userspace) { - return LTTNG_DOMAIN_UST; - } else if (opt_jul) { - return LTTNG_DOMAIN_JUL; - } else if (opt_log4j) { - return LTTNG_DOMAIN_LOG4J; - } else { - abort(); - } -} - -static -int mi_open(void) -{ - int ret; - - /* MI check */ - if (!lttng_opt_mi) { - ret = 0; - goto end; - } - - ret = fileno(stdout); - if (ret < 0) { - PERROR("Unable to retrieve fileno of stdout"); - ret = CMD_ERROR; - goto end; - } - - writer = mi_lttng_writer_create(ret, lttng_opt_mi); - if (!writer) { - ret = CMD_ERROR; - goto end; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_add_context); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } -end: - return ret; -} - -static -int mi_close(enum cmd_error_code success) -{ - int ret; - - /* MI closing */ - if (!lttng_opt_mi) { - ret = 0; - goto end; - } - /* Close output element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, !success); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } -end: - return ret; -} - -static -void populate_context(struct lttng_event_context *context, - const struct ctx_opts *opt) -{ - char *ptr; - - context->ctx = (enum lttng_event_context_type) opt->ctx_type; - switch (context->ctx) { - case LTTNG_EVENT_CONTEXT_PERF_COUNTER: - case LTTNG_EVENT_CONTEXT_PERF_CPU_COUNTER: - case LTTNG_EVENT_CONTEXT_PERF_THREAD_COUNTER: - context->u.perf_counter.type = opt->u.perf.type; - context->u.perf_counter.config = opt->u.perf.config; - strncpy(context->u.perf_counter.name, opt->symbol, - LTTNG_SYMBOL_NAME_LEN); - context->u.perf_counter.name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - /* Replace : and - by _ */ - while ((ptr = strchr(context->u.perf_counter.name, '-')) != NULL) { - *ptr = '_'; - } - while ((ptr = strchr(context->u.perf_counter.name, ':')) != NULL) { - *ptr = '_'; - } - break; - case LTTNG_EVENT_CONTEXT_APP_CONTEXT: - context->u.app_ctx.provider_name = - opt->u.app_ctx.provider_name; - context->u.app_ctx.ctx_name = - opt->u.app_ctx.ctx_name; - break; - default: - break; - } -} - -/* - * Pretty print context type. - */ -static -int print_ctx_type(void) -{ - - FILE *ofp = stdout; - int i = 0; - int ret; - struct lttng_event_context context; - - memset(&context, 0, sizeof(context)); - - ret = mi_open(); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - if (lttng_opt_mi) { - /* Open a contexts element */ - ret = mi_lttng_writer_open_element(writer, config_element_contexts); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - while (ctx_opts[i].symbol != NULL) { - if (!ctx_opts[i].hide_help) { - if (lttng_opt_mi) { - populate_context(&context, &ctx_opts[i]); - ret = mi_lttng_context(writer, &context, 1); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - ret = mi_lttng_writer_write_element_string( - writer, - mi_lttng_element_context_symbol, - ctx_opts[i].symbol); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } else { - fprintf(ofp, "%s\n", ctx_opts[i].symbol); - } - } - i++; - } - - if (lttng_opt_mi) { - /* Close contexts element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } - } - -end: - ret = mi_close(ret); - if (ret) { - ret = CMD_ERROR; - } - return ret; -} - -/* - * Add context to channel or event. - */ -static int add_context(char *session_name) -{ - int ret = CMD_SUCCESS, warn = 0, success = 0; - struct lttng_event_context context; - struct lttng_domain dom; - struct ctx_type *type; - - memset(&context, 0, sizeof(context)); - memset(&dom, 0, sizeof(dom)); - - dom.type = get_domain(); - handle = lttng_create_handle(session_name, &dom); - if (handle == NULL) { - ret = CMD_ERROR; - goto error; - } - - if (lttng_opt_mi) { - /* Open a contexts element */ - ret = mi_lttng_writer_open_element(writer, config_element_contexts); - if (ret) { - goto error; - } - } - - /* Iterate over all the context types given */ - cds_list_for_each_entry(type, &ctx_type_list.head, list) { - DBG("Adding context..."); - - populate_context(&context, type->opt); - - if (lttng_opt_mi) { - /* We leave context open the update the success of the command */ - ret = mi_lttng_context(writer, &context, 1); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_context_symbol, - type->opt->symbol); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - - ret = lttng_add_context(handle, &context, NULL, opt_channel_name); - if (ret < 0) { - ERR("%s: %s", type->opt->symbol, lttng_strerror(ret)); - warn = 1; - success = 0; - } else { - if (opt_channel_name) { - MSG("%s context %s added to channel %s", - lttng_domain_type_str(dom.type), - type->opt->symbol, - opt_channel_name); - } else { - MSG("%s context %s added to all channels", - lttng_domain_type_str(dom.type), - type->opt->symbol); - } - success = 1; - } - - if (lttng_opt_mi) { - /* Is the single operation a success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_success, success); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - /* Close the context element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - } - - if (lttng_opt_mi) { - /* Close contexts element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto error; - } - } - - ret = CMD_SUCCESS; - -error: - lttng_destroy_handle(handle); - - /* - * This means that at least one add_context failed and tells the user to - * look on stderr for error(s). - */ - if (!ret && warn) { - ret = CMD_WARNING; - } - return ret; -} - -static -void destroy_ctx_type(struct ctx_type *type) -{ - if (!type) { - return; - } - if (type->opt) { - free(type->opt->symbol); - } - free(type->opt); - free(type); -} - -static -struct ctx_type *create_ctx_type(void) -{ - struct ctx_type *type = zmalloc(sizeof(*type)); - - if (!type) { - PERROR("malloc ctx_type"); - goto end; - } - - type->opt = zmalloc(sizeof(*type->opt)); - if (!type->opt) { - PERROR("malloc ctx_type options"); - destroy_ctx_type(type); - type = NULL; - goto end; - } -end: - return type; -} - -static -int find_ctx_type_perf_raw(const char *ctx, struct ctx_type *type) -{ - int ret; - int field_pos = 0; - char *tmp_list, *cur_list; - - cur_list = tmp_list = strdup(ctx); - if (!tmp_list) { - PERROR("strdup temp list"); - ret = -ENOMEM; - goto end; - } - - /* Looking for "perf:[cpu|thread]:raw::". */ - for (;;) { - char *next; - - next = strtok(cur_list, ":"); - if (!next) { - break; - } - cur_list = NULL; - switch (field_pos) { - case 0: - if (strncmp(next, "perf", 4) != 0) { - ret = -1; - goto end; - } - break; - case 1: - if (strncmp(next, "cpu", 3) == 0) { - type->opt->ctx_type = CONTEXT_PERF_CPU_COUNTER; - } else if (strncmp(next, "thread", 4) == 0) { - type->opt->ctx_type = CONTEXT_PERF_THREAD_COUNTER; - } else { - ret = -1; - goto end; - } - break; - case 2: - if (strncmp(next, "raw", 3) != 0) { - ret = -1; - goto end; - } - break; - case 3: - { - char *endptr; - - if (strlen(next) < 2 || next[0] != 'r') { - ERR("Wrong perf raw mask format: expected rNNN"); - ret = -1; - goto end; - } - errno = 0; - type->opt->u.perf.config = strtoll(next + 1, &endptr, 16); - if (errno != 0 || !endptr || *endptr) { - ERR("Wrong perf raw mask format: expected rNNN"); - ret = -1; - goto end; - } - break; - } - case 4: - /* name */ - break; - case 5: - ERR("Too many ':' in perf raw format"); - ret = -1; - goto end; - }; - field_pos++; - } - - if (field_pos < 5) { - ERR("Invalid perf counter specifier, expected a specifier of " - "the form perf:cpu:raw:rNNN: or " - "perf:thread:raw:rNNN:"); - ret = -1; - goto end; - } - - ret = 0; - goto end; - -end: - free(tmp_list); - return ret; -} - -static -struct ctx_type *get_context_type(const char *ctx) -{ - int opt_index, ret; - struct ctx_type *type = NULL; - const char app_ctx_prefix[] = "$app."; - char *provider_name = NULL, *ctx_name = NULL; - size_t i, len, colon_pos = 0, provider_name_len, ctx_name_len; - - if (!ctx) { - goto not_found; - } - - type = create_ctx_type(); - if (!type) { - goto not_found; - } - - /* Check if ctx matches a known static context. */ - opt_index = find_ctx_type_idx(ctx); - if (opt_index >= 0) { - *type->opt = ctx_opts[opt_index]; - type->opt->symbol = strdup(ctx_opts[opt_index].symbol); - goto found; - } - - /* Check if ctx is a raw perf context. */ - ret = find_ctx_type_perf_raw(ctx, type); - if (ret == 0) { - type->opt->u.perf.type = PERF_TYPE_RAW; - type->opt->symbol = strdup(ctx); - if (!type->opt->symbol) { - PERROR("Copy perf field name"); - goto not_found; - } - goto found; - } - - /* - * No match found against static contexts; check if it is an app - * context. - */ - len = strlen(ctx); - if (len <= sizeof(app_ctx_prefix) - 1) { - goto not_found; - } - - /* String starts with $app. */ - if (strncmp(ctx, app_ctx_prefix, sizeof(app_ctx_prefix) - 1)) { - goto not_found; - } - - /* Validate that the ':' separator is present. */ - for (i = sizeof(app_ctx_prefix); i < len; i++) { - const char c = ctx[i]; - - if (c == ':') { - colon_pos = i; - break; - } - } - - /* - * No colon found or no ctx name ("$app.provider:") or no provider name - * given ("$app.:..."), which is invalid. - */ - if (!colon_pos || colon_pos == len || - colon_pos == sizeof(app_ctx_prefix)) { - ERR("Invalid application context provided: no provider or context name provided."); - goto not_found; - } - - provider_name_len = colon_pos - sizeof(app_ctx_prefix) + 2; - provider_name = zmalloc(provider_name_len); - if (!provider_name) { - PERROR("malloc provider_name"); - goto not_found; - } - strncpy(provider_name, ctx + sizeof(app_ctx_prefix) - 1, - provider_name_len - 1); - type->opt->u.app_ctx.provider_name = provider_name; - - ctx_name_len = len - colon_pos; - ctx_name = zmalloc(ctx_name_len); - if (!ctx_name) { - PERROR("malloc ctx_name"); - goto not_found; - } - strncpy(ctx_name, ctx + colon_pos + 1, ctx_name_len - 1); - type->opt->u.app_ctx.ctx_name = ctx_name; - type->opt->ctx_type = CONTEXT_APP_CONTEXT; - type->opt->symbol = strdup(ctx); -found: - return type; -not_found: - free(provider_name); - free(ctx_name); - destroy_ctx_type(type); - return NULL; -} - -/* - * Add context to channel or event. - */ -int cmd_add_context(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS; - static poptContext pc; - struct ctx_type *type, *tmptype; - char *session_name = NULL; - const char *leftover = NULL; - - if (argc < 2) { - ret = CMD_ERROR; - goto end; - } - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_LIST: - ret = print_ctx_type(); - goto end; - case OPT_TYPE: - { - type = get_context_type(opt_type); - if (!type) { - ERR("Unknown context type %s", opt_type); - ret = CMD_FATAL; - goto end; - } - cds_list_add_tail(&type->list, &ctx_type_list.head); - break; - } - case OPT_USERSPACE: - opt_userspace = 1; - break; - case OPT_JUL: - opt_jul = 1; - break; - case OPT_LOG4J: - opt_log4j = 1; - break; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - default: - ret = CMD_UNDEFINED; - goto end; - } - } - - leftover = poptGetArg(pc); - if (leftover) { - ERR("Unknown argument: %s", leftover); - ret = CMD_ERROR; - goto end; - } - - ret = print_missing_or_multiple_domains( - opt_kernel + opt_userspace + opt_jul + opt_log4j, true); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - if (!opt_type) { - ERR("Missing mandatory -t TYPE"); - ret = CMD_ERROR; - goto end; - } - - if (!opt_session_name) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = CMD_ERROR; - goto end; - } - } else { - session_name = opt_session_name; - } - - ret = mi_open(); - if (ret) { - goto end; - } - - command_ret = add_context(session_name); - ret = mi_close(command_ret); - if (ret) { - goto end; - } - -end: - if (!opt_session_name) { - free(session_name); - } - - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - ret = ret ? ret : LTTNG_ERR_MI_IO_FAIL; - } - - /* Cleanup allocated memory */ - cds_list_for_each_entry_safe(type, tmptype, &ctx_type_list.head, list) { - destroy_ctx_type(type); - } - - /* Overwrite ret if an error occurred during add_context() */ - ret = command_ret ? command_ret : ret; - - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/add_context.cpp b/src/bin/lttng/commands/add_context.cpp new file mode 100644 index 000000000..d078471d3 --- /dev/null +++ b/src/bin/lttng/commands/add_context.cpp @@ -0,0 +1,1239 @@ +/* + * Copyright (C) 2011 David Goulet + * Copyright (C) 2016 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "../command.h" + +static char *opt_channel_name; +static char *opt_session_name; +static int opt_kernel; +static int opt_userspace; +static int opt_jul; +static int opt_log4j; +static char *opt_type; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_TYPE, + OPT_USERSPACE, + OPT_JUL, + OPT_LOG4J, + OPT_LIST_OPTIONS, + OPT_LIST, +}; + +static struct lttng_handle *handle; +static struct mi_writer *writer; + +/* + * Taken from the LTTng ABI + */ +enum context_type { + CONTEXT_PID = 0, + CONTEXT_PERF_COUNTER = 1, /* Backward compat. */ + CONTEXT_PROCNAME = 2, + CONTEXT_PRIO = 3, + CONTEXT_NICE = 4, + CONTEXT_VPID = 5, + CONTEXT_TID = 6, + CONTEXT_VTID = 7, + CONTEXT_PPID = 8, + CONTEXT_VPPID = 9, + CONTEXT_PTHREAD_ID = 10, + CONTEXT_HOSTNAME = 11, + CONTEXT_IP = 12, + CONTEXT_PERF_CPU_COUNTER = 13, + CONTEXT_PERF_THREAD_COUNTER = 14, + CONTEXT_APP_CONTEXT = 15, + CONTEXT_INTERRUPTIBLE = 16, + CONTEXT_PREEMPTIBLE = 17, + CONTEXT_NEED_RESCHEDULE = 18, + CONTEXT_MIGRATABLE = 19, + CONTEXT_CALLSTACK_KERNEL = 20, + CONTEXT_CALLSTACK_USER = 21, + CONTEXT_CGROUP_NS = 22, + CONTEXT_IPC_NS = 23, + CONTEXT_MNT_NS = 24, + CONTEXT_NET_NS = 25, + CONTEXT_PID_NS = 26, + CONTEXT_USER_NS = 27, + CONTEXT_UTS_NS = 28, + CONTEXT_UID = 29, + CONTEXT_EUID = 30, + CONTEXT_SUID = 31, + CONTEXT_GID = 32, + CONTEXT_EGID = 33, + CONTEXT_SGID = 34, + CONTEXT_VUID = 35, + CONTEXT_VEUID = 36, + CONTEXT_VSUID = 37, + CONTEXT_VGID = 38, + CONTEXT_VEGID = 39, + CONTEXT_VSGID = 40, + CONTEXT_TIME_NS = 41, +}; + +/* + * Taken from the Perf ABI (all enum perf_*) + */ +enum perf_type { + PERF_TYPE_HARDWARE = 0, + PERF_TYPE_SOFTWARE = 1, + PERF_TYPE_HW_CACHE = 3, + PERF_TYPE_RAW = 4, +}; + +enum perf_count_hard { + PERF_COUNT_HW_CPU_CYCLES = 0, + PERF_COUNT_HW_INSTRUCTIONS = 1, + PERF_COUNT_HW_CACHE_REFERENCES = 2, + PERF_COUNT_HW_CACHE_MISSES = 3, + PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, + PERF_COUNT_HW_BRANCH_MISSES = 5, + PERF_COUNT_HW_BUS_CYCLES = 6, + PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7, + PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8, +}; + +enum perf_count_soft { + PERF_COUNT_SW_CPU_CLOCK = 0, + PERF_COUNT_SW_TASK_CLOCK = 1, + PERF_COUNT_SW_PAGE_FAULTS = 2, + PERF_COUNT_SW_CONTEXT_SWITCHES = 3, + PERF_COUNT_SW_CPU_MIGRATIONS = 4, + PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, + PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, + PERF_COUNT_SW_ALIGNMENT_FAULTS = 7, + PERF_COUNT_SW_EMULATION_FAULTS = 8, +}; + +/* + * Generalized hardware cache events: + * + * { L1-D, L1-I, LLC, ITLB, DTLB, BPU } x + * { read, write, prefetch } x + * { accesses, misses } + */ +enum perf_hw_cache_id { + PERF_COUNT_HW_CACHE_L1D = 0, + PERF_COUNT_HW_CACHE_L1I = 1, + PERF_COUNT_HW_CACHE_LL = 2, + PERF_COUNT_HW_CACHE_DTLB = 3, + PERF_COUNT_HW_CACHE_ITLB = 4, + PERF_COUNT_HW_CACHE_BPU = 5, + + PERF_COUNT_HW_CACHE_MAX, /* non-ABI */ +}; + +enum perf_hw_cache_op_id { + PERF_COUNT_HW_CACHE_OP_READ = 0, + PERF_COUNT_HW_CACHE_OP_WRITE = 1, + PERF_COUNT_HW_CACHE_OP_PREFETCH = 2, + + PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */ +}; + +enum perf_hw_cache_op_result_id { + PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0, + PERF_COUNT_HW_CACHE_RESULT_MISS = 1, + + PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */ +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, + {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0}, + {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, + {"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0}, + {"jul", 'j', POPT_ARG_NONE, 0, OPT_JUL, 0, 0}, + {"log4j", 'l', POPT_ARG_NONE, 0, OPT_LOG4J, 0, 0}, + {"type", 't', POPT_ARG_STRING, &opt_type, OPT_TYPE, 0, 0}, + {"list", 0, POPT_ARG_NONE, NULL, OPT_LIST, NULL, NULL}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * Context options + */ +#define PERF_HW(optstr, name, type, hide) \ + { \ + optstr, type, PERF_COUNT_HW_##name, hide \ + } + +#define PERF_SW(optstr, name, type, hide) \ + { \ + optstr, type, PERF_COUNT_SW_##name, hide \ + } + +#define _PERF_HW_CACHE(optstr, name, type, op, result, hide) \ + { \ + optstr, type, \ + PERF_COUNT_HW_CACHE_##name, \ + PERF_COUNT_HW_CACHE_OP_##op, \ + PERF_COUNT_HW_CACHE_RESULT_##result, \ + hide, \ + } + +#define PERF_HW_CACHE(optstr, name, type, hide) \ + _PERF_HW_CACHE(optstr "-loads", name, type, \ + READ, ACCESS, hide), \ + _PERF_HW_CACHE(optstr "-load-misses", name, type, \ + READ, MISS, hide), \ + _PERF_HW_CACHE(optstr "-stores", name, type, \ + WRITE, ACCESS, hide), \ + _PERF_HW_CACHE(optstr "-store-misses", name, type, \ + WRITE, MISS, hide), \ + _PERF_HW_CACHE(optstr "-prefetches", name, type, \ + PREFETCH, ACCESS, hide), \ + _PERF_HW_CACHE(optstr "-prefetch-misses", name, type, \ + PREFETCH, MISS, hide) + +static +const struct ctx_opts { + /* Needed for end-of-list item. */ + ctx_opts() + : symbol(nullptr) + {} + + ctx_opts(const char *symbol_, context_type ctx_type_, bool hide_help_ = false) + : symbol((char *) symbol_), ctx_type(ctx_type_), hide_help(hide_help_) + {} + + ctx_opts(const char *symbol_, context_type ctx_type_, perf_count_hard perf_count_hard, bool hide_help_) + : ctx_opts(symbol_, ctx_type_, hide_help_) + { + u.perf.type = PERF_TYPE_HARDWARE; + u.perf.config = perf_count_hard; + } + + ctx_opts(const char *symbol_, context_type ctx_type_, perf_count_soft perf_count_soft, bool hide_help_) + : ctx_opts(symbol_, ctx_type_, hide_help_) + { + u.perf.type = PERF_TYPE_SOFTWARE; + u.perf.config = perf_count_soft; + } + + ctx_opts(const char *symbol_, context_type ctx_type_, + perf_hw_cache_id perf_hw_cache_id, + perf_hw_cache_op_id perf_hw_cache_op_id, + perf_hw_cache_op_result_id perf_hw_cache_op_result_id, + bool hide_help_) + : ctx_opts(symbol_, ctx_type_, hide_help_) + { + u.perf.type = PERF_TYPE_HW_CACHE; + u.perf.config = perf_hw_cache_id | perf_hw_cache_op_id << 8 | perf_hw_cache_op_result_id << 16; + } + + char *symbol; + enum context_type ctx_type; + bool hide_help; /* Hide from --help */ + union { + struct { + uint32_t type; + uint64_t config; + } perf; + struct { + char *provider_name; + char *ctx_name; + } app_ctx; + } u; +} ctx_opts[] = { + /* + * These (char *) casts (as well as those in the PERF_* macros) are + * safe because we never free these instances of `struct ctx_opts`. + */ + { (char *) "pid", CONTEXT_PID }, + { (char *) "procname", CONTEXT_PROCNAME }, + { (char *) "prio", CONTEXT_PRIO }, + { (char *) "nice", CONTEXT_NICE }, + { (char *) "vpid", CONTEXT_VPID }, + { (char *) "tid", CONTEXT_TID }, + { (char *) "pthread_id", CONTEXT_PTHREAD_ID }, + { (char *) "vtid", CONTEXT_VTID }, + { (char *) "ppid", CONTEXT_PPID }, + { (char *) "vppid", CONTEXT_VPPID }, + { (char *) "hostname", CONTEXT_HOSTNAME }, + { (char *) "ip", CONTEXT_IP }, + { (char *) "interruptible", CONTEXT_INTERRUPTIBLE }, + { (char *) "preemptible", CONTEXT_PREEMPTIBLE }, + { (char *) "need_reschedule", CONTEXT_NEED_RESCHEDULE }, + { (char *) "migratable", CONTEXT_MIGRATABLE }, + { (char *) "callstack-kernel", CONTEXT_CALLSTACK_KERNEL }, +#if HAVE_MODULES_USERSPACE_CALLSTACK_CONTEXT + { (char *) "callstack-user", CONTEXT_CALLSTACK_USER }, +#endif + { (char *) "cgroup_ns", CONTEXT_CGROUP_NS }, + { (char *) "ipc_ns", CONTEXT_IPC_NS }, + { (char *) "mnt_ns", CONTEXT_MNT_NS }, + { (char *) "net_ns", CONTEXT_NET_NS }, + { (char *) "pid_ns", CONTEXT_PID_NS }, + { (char *) "time_ns", CONTEXT_TIME_NS }, + { (char *) "user_ns", CONTEXT_USER_NS }, + { (char *) "uts_ns", CONTEXT_UTS_NS }, + { (char *) "uid", CONTEXT_UID }, + { (char *) "euid", CONTEXT_EUID }, + { (char *) "suid", CONTEXT_SUID }, + { (char *) "gid", CONTEXT_GID }, + { (char *) "egid", CONTEXT_EGID }, + { (char *) "sgid", CONTEXT_SGID }, + { (char *) "vuid", CONTEXT_VUID }, + { (char *) "veuid", CONTEXT_VEUID }, + { (char *) "vsuid", CONTEXT_VSUID }, + { (char *) "vgid", CONTEXT_VGID }, + { (char *) "vegid", CONTEXT_VEGID }, + { (char *) "vsgid", CONTEXT_VSGID }, + + /* Perf options */ + + /* Perf per-CPU counters */ + PERF_HW("perf:cpu:cpu-cycles", CPU_CYCLES, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_HW("perf:cpu:cycles", CPU_CYCLES, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_HW("perf:cpu:stalled-cycles-frontend", STALLED_CYCLES_FRONTEND, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_HW("perf:cpu:idle-cycles-frontend", STALLED_CYCLES_FRONTEND, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_HW("perf:cpu:stalled-cycles-backend", STALLED_CYCLES_BACKEND, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_HW("perf:cpu:idle-cycles-backend", STALLED_CYCLES_BACKEND, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_HW("perf:cpu:instructions", INSTRUCTIONS, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_HW("perf:cpu:cache-references", CACHE_REFERENCES, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_HW("perf:cpu:cache-misses", CACHE_MISSES, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_HW("perf:cpu:branch-instructions", BRANCH_INSTRUCTIONS, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_HW("perf:cpu:branches", BRANCH_INSTRUCTIONS, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_HW("perf:cpu:branch-misses", BRANCH_MISSES, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_HW("perf:cpu:bus-cycles", BUS_CYCLES, + CONTEXT_PERF_CPU_COUNTER, 0), + + PERF_HW_CACHE("perf:cpu:L1-dcache", L1D, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_HW_CACHE("perf:cpu:L1-icache", L1I, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_HW_CACHE("perf:cpu:LLC", LL, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_HW_CACHE("perf:cpu:dTLB", DTLB, + CONTEXT_PERF_CPU_COUNTER, 0), + _PERF_HW_CACHE("perf:cpu:iTLB-loads", ITLB, + CONTEXT_PERF_CPU_COUNTER, READ, ACCESS, 0), + _PERF_HW_CACHE("perf:cpu:iTLB-load-misses", ITLB, + CONTEXT_PERF_CPU_COUNTER, READ, MISS, 0), + _PERF_HW_CACHE("perf:cpu:branch-loads", BPU, + CONTEXT_PERF_CPU_COUNTER, READ, ACCESS, 0), + _PERF_HW_CACHE("perf:cpu:branch-load-misses", BPU, + CONTEXT_PERF_CPU_COUNTER, READ, MISS, 0), + + PERF_SW("perf:cpu:cpu-clock", CPU_CLOCK, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_SW("perf:cpu:task-clock", TASK_CLOCK, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_SW("perf:cpu:page-fault", PAGE_FAULTS, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_SW("perf:cpu:faults", PAGE_FAULTS, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_SW("perf:cpu:major-faults", PAGE_FAULTS_MAJ, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_SW("perf:cpu:minor-faults", PAGE_FAULTS_MIN, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_SW("perf:cpu:context-switches", CONTEXT_SWITCHES, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_SW("perf:cpu:cs", CONTEXT_SWITCHES, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_SW("perf:cpu:cpu-migrations", CPU_MIGRATIONS, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_SW("perf:cpu:migrations", CPU_MIGRATIONS, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_SW("perf:cpu:alignment-faults", ALIGNMENT_FAULTS, + CONTEXT_PERF_CPU_COUNTER, 0), + PERF_SW("perf:cpu:emulation-faults", EMULATION_FAULTS, + CONTEXT_PERF_CPU_COUNTER, 0), + + /* Perf per-thread counters */ + PERF_HW("perf:thread:cpu-cycles", CPU_CYCLES, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_HW("perf:thread:cycles", CPU_CYCLES, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_HW("perf:thread:stalled-cycles-frontend", STALLED_CYCLES_FRONTEND, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_HW("perf:thread:idle-cycles-frontend", STALLED_CYCLES_FRONTEND, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_HW("perf:thread:stalled-cycles-backend", STALLED_CYCLES_BACKEND, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_HW("perf:thread:idle-cycles-backend", STALLED_CYCLES_BACKEND, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_HW("perf:thread:instructions", INSTRUCTIONS, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_HW("perf:thread:cache-references", CACHE_REFERENCES, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_HW("perf:thread:cache-misses", CACHE_MISSES, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_HW("perf:thread:branch-instructions", BRANCH_INSTRUCTIONS, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_HW("perf:thread:branches", BRANCH_INSTRUCTIONS, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_HW("perf:thread:branch-misses", BRANCH_MISSES, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_HW("perf:thread:bus-cycles", BUS_CYCLES, + CONTEXT_PERF_THREAD_COUNTER, 0), + + PERF_HW_CACHE("perf:thread:L1-dcache", L1D, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_HW_CACHE("perf:thread:L1-icache", L1I, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_HW_CACHE("perf:thread:LLC", LL, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_HW_CACHE("perf:thread:dTLB", DTLB, + CONTEXT_PERF_THREAD_COUNTER, 0), + _PERF_HW_CACHE("perf:thread:iTLB-loads", ITLB, + CONTEXT_PERF_THREAD_COUNTER, READ, ACCESS, 0), + _PERF_HW_CACHE("perf:thread:iTLB-load-misses", ITLB, + CONTEXT_PERF_THREAD_COUNTER, READ, MISS, 0), + _PERF_HW_CACHE("perf:thread:branch-loads", BPU, + CONTEXT_PERF_THREAD_COUNTER, READ, ACCESS, 0), + _PERF_HW_CACHE("perf:thread:branch-load-misses", BPU, + CONTEXT_PERF_THREAD_COUNTER, READ, MISS, 0), + + PERF_SW("perf:thread:cpu-clock", CPU_CLOCK, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_SW("perf:thread:task-clock", TASK_CLOCK, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_SW("perf:thread:page-fault", PAGE_FAULTS, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_SW("perf:thread:faults", PAGE_FAULTS, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_SW("perf:thread:major-faults", PAGE_FAULTS_MAJ, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_SW("perf:thread:minor-faults", PAGE_FAULTS_MIN, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_SW("perf:thread:context-switches", CONTEXT_SWITCHES, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_SW("perf:thread:cs", CONTEXT_SWITCHES, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_SW("perf:thread:cpu-migrations", CPU_MIGRATIONS, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_SW("perf:thread:migrations", CPU_MIGRATIONS, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_SW("perf:thread:alignment-faults", ALIGNMENT_FAULTS, + CONTEXT_PERF_THREAD_COUNTER, 0), + PERF_SW("perf:thread:emulation-faults", EMULATION_FAULTS, + CONTEXT_PERF_THREAD_COUNTER, 0), + + /* + * Perf per-CPU counters, backward compatibilty for names. + * Hidden from help listing. + */ + PERF_HW("perf:cpu-cycles", CPU_CYCLES, + CONTEXT_PERF_COUNTER, 1), + PERF_HW("perf:cycles", CPU_CYCLES, + CONTEXT_PERF_COUNTER, 1), + PERF_HW("perf:stalled-cycles-frontend", STALLED_CYCLES_FRONTEND, + CONTEXT_PERF_COUNTER, 1), + PERF_HW("perf:idle-cycles-frontend", STALLED_CYCLES_FRONTEND, + CONTEXT_PERF_COUNTER, 1), + PERF_HW("perf:stalled-cycles-backend", STALLED_CYCLES_BACKEND, + CONTEXT_PERF_COUNTER, 1), + PERF_HW("perf:idle-cycles-backend", STALLED_CYCLES_BACKEND, + CONTEXT_PERF_COUNTER, 1), + PERF_HW("perf:instructions", INSTRUCTIONS, + CONTEXT_PERF_COUNTER, 1), + PERF_HW("perf:cache-references", CACHE_REFERENCES, + CONTEXT_PERF_COUNTER, 1), + PERF_HW("perf:cache-misses", CACHE_MISSES, + CONTEXT_PERF_COUNTER, 1), + PERF_HW("perf:branch-instructions", BRANCH_INSTRUCTIONS, + CONTEXT_PERF_COUNTER, 1), + PERF_HW("perf:branches", BRANCH_INSTRUCTIONS, + CONTEXT_PERF_COUNTER, 1), + PERF_HW("perf:branch-misses", BRANCH_MISSES, + CONTEXT_PERF_COUNTER, 1), + PERF_HW("perf:bus-cycles", BUS_CYCLES, + CONTEXT_PERF_COUNTER, 1), + + PERF_HW_CACHE("perf:L1-dcache", L1D, + CONTEXT_PERF_COUNTER, 1), + PERF_HW_CACHE("perf:L1-icache", L1I, + CONTEXT_PERF_COUNTER, 1), + PERF_HW_CACHE("perf:LLC", LL, + CONTEXT_PERF_COUNTER, 1), + PERF_HW_CACHE("perf:dTLB", DTLB, + CONTEXT_PERF_COUNTER, 1), + _PERF_HW_CACHE("perf:iTLB-loads", ITLB, + CONTEXT_PERF_COUNTER, READ, ACCESS, 1), + _PERF_HW_CACHE("perf:iTLB-load-misses", ITLB, + CONTEXT_PERF_COUNTER, READ, MISS, 1), + _PERF_HW_CACHE("perf:branch-loads", BPU, + CONTEXT_PERF_COUNTER, READ, ACCESS, 1), + _PERF_HW_CACHE("perf:branch-load-misses", BPU, + CONTEXT_PERF_COUNTER, READ, MISS, 1), + + PERF_SW("perf:cpu-clock", CPU_CLOCK, + CONTEXT_PERF_COUNTER, 1), + PERF_SW("perf:task-clock", TASK_CLOCK, + CONTEXT_PERF_COUNTER, 1), + PERF_SW("perf:page-fault", PAGE_FAULTS, + CONTEXT_PERF_COUNTER, 1), + PERF_SW("perf:faults", PAGE_FAULTS, + CONTEXT_PERF_COUNTER, 1), + PERF_SW("perf:major-faults", PAGE_FAULTS_MAJ, + CONTEXT_PERF_COUNTER, 1), + PERF_SW("perf:minor-faults", PAGE_FAULTS_MIN, + CONTEXT_PERF_COUNTER, 1), + PERF_SW("perf:context-switches", CONTEXT_SWITCHES, + CONTEXT_PERF_COUNTER, 1), + PERF_SW("perf:cs", CONTEXT_SWITCHES, + CONTEXT_PERF_COUNTER, 1), + PERF_SW("perf:cpu-migrations", CPU_MIGRATIONS, + CONTEXT_PERF_COUNTER, 1), + PERF_SW("perf:migrations", CPU_MIGRATIONS, + CONTEXT_PERF_COUNTER, 1), + PERF_SW("perf:alignment-faults", ALIGNMENT_FAULTS, + CONTEXT_PERF_COUNTER, 1), + PERF_SW("perf:emulation-faults", EMULATION_FAULTS, + CONTEXT_PERF_COUNTER, 1), + + {}, /* Closure */ +}; + +#undef PERF_HW_CACHE +#undef _PERF_HW_CACHE +#undef PERF_SW +#undef PERF_HW + +/* + * Context type for command line option parsing. + */ +struct ctx_type { + struct ctx_opts *opt; + struct cds_list_head list; +}; + +/* + * List of context type. Use to enable multiple context on a single command + * line entry. + */ +struct ctx_type_list { + struct cds_list_head head; +} ctx_type_list = { + .head = CDS_LIST_HEAD_INIT(ctx_type_list.head), +}; + + + +/* + * Find context numerical value from string. + * + * Return -1 if not found. + */ +static int find_ctx_type_idx(const char *opt) +{ + int ret, i = 0; + + while (ctx_opts[i].symbol != NULL) { + if (strcmp(opt, ctx_opts[i].symbol) == 0) { + ret = i; + goto end; + } + i++; + } + + ret = -1; +end: + return ret; +} + +static +enum lttng_domain_type get_domain(void) +{ + if (opt_kernel) { + return LTTNG_DOMAIN_KERNEL; + } else if (opt_userspace) { + return LTTNG_DOMAIN_UST; + } else if (opt_jul) { + return LTTNG_DOMAIN_JUL; + } else if (opt_log4j) { + return LTTNG_DOMAIN_LOG4J; + } else { + abort(); + } +} + +static +int mi_open(void) +{ + int ret; + + /* MI check */ + if (!lttng_opt_mi) { + ret = 0; + goto end; + } + + ret = fileno(stdout); + if (ret < 0) { + PERROR("Unable to retrieve fileno of stdout"); + ret = CMD_ERROR; + goto end; + } + + writer = mi_lttng_writer_create(ret, lttng_opt_mi); + if (!writer) { + ret = CMD_ERROR; + goto end; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_add_context); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } +end: + return ret; +} + +static +int mi_close(enum cmd_error_code success) +{ + int ret; + + /* MI closing */ + if (!lttng_opt_mi) { + ret = 0; + goto end; + } + /* Close output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, !success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } +end: + return ret; +} + +static +void populate_context(struct lttng_event_context *context, + const struct ctx_opts *opt) +{ + char *ptr; + + context->ctx = (enum lttng_event_context_type) opt->ctx_type; + switch (context->ctx) { + case LTTNG_EVENT_CONTEXT_PERF_COUNTER: + case LTTNG_EVENT_CONTEXT_PERF_CPU_COUNTER: + case LTTNG_EVENT_CONTEXT_PERF_THREAD_COUNTER: + context->u.perf_counter.type = opt->u.perf.type; + context->u.perf_counter.config = opt->u.perf.config; + strncpy(context->u.perf_counter.name, opt->symbol, + LTTNG_SYMBOL_NAME_LEN); + context->u.perf_counter.name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + /* Replace : and - by _ */ + while ((ptr = strchr(context->u.perf_counter.name, '-')) != NULL) { + *ptr = '_'; + } + while ((ptr = strchr(context->u.perf_counter.name, ':')) != NULL) { + *ptr = '_'; + } + break; + case LTTNG_EVENT_CONTEXT_APP_CONTEXT: + context->u.app_ctx.provider_name = + opt->u.app_ctx.provider_name; + context->u.app_ctx.ctx_name = + opt->u.app_ctx.ctx_name; + break; + default: + break; + } +} + +/* + * Pretty print context type. + */ +static +int print_ctx_type(void) +{ + + FILE *ofp = stdout; + int i = 0; + int ret; + struct lttng_event_context context; + + memset(&context, 0, sizeof(context)); + + ret = mi_open(); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + if (lttng_opt_mi) { + /* Open a contexts element */ + ret = mi_lttng_writer_open_element(writer, config_element_contexts); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + while (ctx_opts[i].symbol != NULL) { + if (!ctx_opts[i].hide_help) { + if (lttng_opt_mi) { + populate_context(&context, &ctx_opts[i]); + ret = mi_lttng_context(writer, &context, 1); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + ret = mi_lttng_writer_write_element_string( + writer, + mi_lttng_element_context_symbol, + ctx_opts[i].symbol); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } else { + fprintf(ofp, "%s\n", ctx_opts[i].symbol); + } + } + i++; + } + + if (lttng_opt_mi) { + /* Close contexts element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + } + +end: + ret = mi_close((cmd_error_code) ret); + if (ret) { + ret = CMD_ERROR; + } + return ret; +} + +/* + * Add context to channel or event. + */ +static int add_context(char *session_name) +{ + int ret = CMD_SUCCESS, warn = 0, success = 0; + struct lttng_event_context context; + struct lttng_domain dom; + struct ctx_type *type; + + memset(&context, 0, sizeof(context)); + memset(&dom, 0, sizeof(dom)); + + dom.type = get_domain(); + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = CMD_ERROR; + goto error; + } + + if (lttng_opt_mi) { + /* Open a contexts element */ + ret = mi_lttng_writer_open_element(writer, config_element_contexts); + if (ret) { + goto error; + } + } + + /* Iterate over all the context types given */ + cds_list_for_each_entry(type, &ctx_type_list.head, list) { + DBG("Adding context..."); + + populate_context(&context, type->opt); + + if (lttng_opt_mi) { + /* We leave context open the update the success of the command */ + ret = mi_lttng_context(writer, &context, 1); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_context_symbol, + type->opt->symbol); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + + ret = lttng_add_context(handle, &context, NULL, opt_channel_name); + if (ret < 0) { + ERR("%s: %s", type->opt->symbol, lttng_strerror(ret)); + warn = 1; + success = 0; + } else { + if (opt_channel_name) { + MSG("%s context %s added to channel %s", + lttng_domain_type_str(dom.type), + type->opt->symbol, + opt_channel_name); + } else { + MSG("%s context %s added to all channels", + lttng_domain_type_str(dom.type), + type->opt->symbol); + } + success = 1; + } + + if (lttng_opt_mi) { + /* Is the single operation a success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_success, success); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* Close the context element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + } + + if (lttng_opt_mi) { + /* Close contexts element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto error; + } + } + + ret = CMD_SUCCESS; + +error: + lttng_destroy_handle(handle); + + /* + * This means that at least one add_context failed and tells the user to + * look on stderr for error(s). + */ + if (!ret && warn) { + ret = CMD_WARNING; + } + return ret; +} + +static +void destroy_ctx_type(struct ctx_type *type) +{ + if (!type) { + return; + } + if (type->opt) { + free(type->opt->symbol); + } + free(type->opt); + free(type); +} + +static +struct ctx_type *create_ctx_type(void) +{ + struct ctx_type *type = (ctx_type *) zmalloc(sizeof(*type)); + + if (!type) { + PERROR("malloc ctx_type"); + goto end; + } + + type->opt = (struct ctx_opts *) zmalloc(sizeof(*type->opt)); + if (!type->opt) { + PERROR("malloc ctx_type options"); + destroy_ctx_type(type); + type = NULL; + goto end; + } +end: + return type; +} + +static +int find_ctx_type_perf_raw(const char *ctx, struct ctx_type *type) +{ + int ret; + int field_pos = 0; + char *tmp_list, *cur_list; + + cur_list = tmp_list = strdup(ctx); + if (!tmp_list) { + PERROR("strdup temp list"); + ret = -ENOMEM; + goto end; + } + + /* Looking for "perf:[cpu|thread]:raw::". */ + for (;;) { + char *next; + + next = strtok(cur_list, ":"); + if (!next) { + break; + } + cur_list = NULL; + switch (field_pos) { + case 0: + if (strncmp(next, "perf", 4) != 0) { + ret = -1; + goto end; + } + break; + case 1: + if (strncmp(next, "cpu", 3) == 0) { + type->opt->ctx_type = CONTEXT_PERF_CPU_COUNTER; + } else if (strncmp(next, "thread", 4) == 0) { + type->opt->ctx_type = CONTEXT_PERF_THREAD_COUNTER; + } else { + ret = -1; + goto end; + } + break; + case 2: + if (strncmp(next, "raw", 3) != 0) { + ret = -1; + goto end; + } + break; + case 3: + { + char *endptr; + + if (strlen(next) < 2 || next[0] != 'r') { + ERR("Wrong perf raw mask format: expected rNNN"); + ret = -1; + goto end; + } + errno = 0; + type->opt->u.perf.config = strtoll(next + 1, &endptr, 16); + if (errno != 0 || !endptr || *endptr) { + ERR("Wrong perf raw mask format: expected rNNN"); + ret = -1; + goto end; + } + break; + } + case 4: + /* name */ + break; + case 5: + ERR("Too many ':' in perf raw format"); + ret = -1; + goto end; + }; + field_pos++; + } + + if (field_pos < 5) { + ERR("Invalid perf counter specifier, expected a specifier of " + "the form perf:cpu:raw:rNNN: or " + "perf:thread:raw:rNNN:"); + ret = -1; + goto end; + } + + ret = 0; + goto end; + +end: + free(tmp_list); + return ret; +} + +static +struct ctx_type *get_context_type(const char *ctx) +{ + int opt_index, ret; + struct ctx_type *type = NULL; + const char app_ctx_prefix[] = "$app."; + char *provider_name = NULL, *ctx_name = NULL; + size_t i, len, colon_pos = 0, provider_name_len, ctx_name_len; + + if (!ctx) { + goto not_found; + } + + type = create_ctx_type(); + if (!type) { + goto not_found; + } + + /* Check if ctx matches a known static context. */ + opt_index = find_ctx_type_idx(ctx); + if (opt_index >= 0) { + *type->opt = ctx_opts[opt_index]; + type->opt->symbol = strdup(ctx_opts[opt_index].symbol); + goto found; + } + + /* Check if ctx is a raw perf context. */ + ret = find_ctx_type_perf_raw(ctx, type); + if (ret == 0) { + type->opt->u.perf.type = PERF_TYPE_RAW; + type->opt->symbol = strdup(ctx); + if (!type->opt->symbol) { + PERROR("Copy perf field name"); + goto not_found; + } + goto found; + } + + /* + * No match found against static contexts; check if it is an app + * context. + */ + len = strlen(ctx); + if (len <= sizeof(app_ctx_prefix) - 1) { + goto not_found; + } + + /* String starts with $app. */ + if (strncmp(ctx, app_ctx_prefix, sizeof(app_ctx_prefix) - 1)) { + goto not_found; + } + + /* Validate that the ':' separator is present. */ + for (i = sizeof(app_ctx_prefix); i < len; i++) { + const char c = ctx[i]; + + if (c == ':') { + colon_pos = i; + break; + } + } + + /* + * No colon found or no ctx name ("$app.provider:") or no provider name + * given ("$app.:..."), which is invalid. + */ + if (!colon_pos || colon_pos == len || + colon_pos == sizeof(app_ctx_prefix)) { + ERR("Invalid application context provided: no provider or context name provided."); + goto not_found; + } + + provider_name_len = colon_pos - sizeof(app_ctx_prefix) + 2; + provider_name = (char *) zmalloc(provider_name_len); + if (!provider_name) { + PERROR("malloc provider_name"); + goto not_found; + } + strncpy(provider_name, ctx + sizeof(app_ctx_prefix) - 1, + provider_name_len - 1); + type->opt->u.app_ctx.provider_name = provider_name; + + ctx_name_len = len - colon_pos; + ctx_name = (char *) zmalloc(ctx_name_len); + if (!ctx_name) { + PERROR("malloc ctx_name"); + goto not_found; + } + strncpy(ctx_name, ctx + colon_pos + 1, ctx_name_len - 1); + type->opt->u.app_ctx.ctx_name = ctx_name; + type->opt->ctx_type = CONTEXT_APP_CONTEXT; + type->opt->symbol = strdup(ctx); +found: + return type; +not_found: + free(provider_name); + free(ctx_name); + destroy_ctx_type(type); + return NULL; +} + +/* + * Add context to channel or event. + */ +int cmd_add_context(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS; + static poptContext pc; + struct ctx_type *type, *tmptype; + char *session_name = NULL; + const char *leftover = NULL; + + if (argc < 2) { + ret = CMD_ERROR; + goto end; + } + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST: + ret = print_ctx_type(); + goto end; + case OPT_TYPE: + { + type = get_context_type(opt_type); + if (!type) { + ERR("Unknown context type %s", opt_type); + ret = CMD_FATAL; + goto end; + } + cds_list_add_tail(&type->list, &ctx_type_list.head); + break; + } + case OPT_USERSPACE: + opt_userspace = 1; + break; + case OPT_JUL: + opt_jul = 1; + break; + case OPT_LOG4J: + opt_log4j = 1; + break; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + default: + ret = CMD_UNDEFINED; + goto end; + } + } + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + ret = CMD_ERROR; + goto end; + } + + ret = print_missing_or_multiple_domains( + opt_kernel + opt_userspace + opt_jul + opt_log4j, true); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + if (!opt_type) { + ERR("Missing mandatory -t TYPE"); + ret = CMD_ERROR; + goto end; + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = CMD_ERROR; + goto end; + } + } else { + session_name = opt_session_name; + } + + ret = mi_open(); + if (ret) { + goto end; + } + + command_ret = add_context(session_name); + ret = mi_close((cmd_error_code) command_ret); + if (ret) { + goto end; + } + +end: + if (!opt_session_name) { + free(session_name); + } + + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : LTTNG_ERR_MI_IO_FAIL; + } + + /* Cleanup allocated memory */ + cds_list_for_each_entry_safe(type, tmptype, &ctx_type_list.head, list) { + destroy_ctx_type(type); + } + + /* Overwrite ret if an error occurred during add_context() */ + ret = command_ret ? command_ret : ret; + + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/add_trigger.c b/src/bin/lttng/commands/add_trigger.c deleted file mode 100644 index 27f6cc8c6..000000000 --- a/src/bin/lttng/commands/add_trigger.c +++ /dev/null @@ -1,2475 +0,0 @@ -/* - * Copyright (C) 2021 Simon Marchi - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include - -#include "../command.h" -#include "../loglevel.h" -#include "../uprobe.h" - -#include "common/argpar/argpar.h" -#include "common/dynamic-array.h" -#include "common/mi-lttng.h" -#include "common/string-utils/string-utils.h" -#include "common/utils.h" -#include -/* For lttng_event_rule_type_str(). */ -#include -#include -#include "common/filter/filter-ast.h" -#include "common/filter/filter-ir.h" -#include "common/dynamic-array.h" - -#if (LTTNG_SYMBOL_NAME_LEN == 256) -#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255" -#endif - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP, - OPT_LIST_OPTIONS, - - OPT_CONDITION, - OPT_ACTION, - OPT_ID, - OPT_OWNER_UID, - OPT_RATE_POLICY, - - OPT_NAME, - OPT_FILTER, - OPT_EXCLUDE_NAME, - OPT_EVENT_NAME, - OPT_LOG_LEVEL, - - OPT_TYPE, - OPT_LOCATION, - - OPT_MAX_SIZE, - OPT_DATA_URL, - OPT_CTRL_URL, - OPT_URL, - OPT_PATH, - - OPT_CAPTURE, -}; - -static const struct argpar_opt_descr event_rule_opt_descrs[] = { - { OPT_FILTER, 'f', "filter", true }, - { OPT_NAME, 'n', "name", true }, - { OPT_EXCLUDE_NAME, 'x', "exclude-name", true }, - { OPT_LOG_LEVEL, 'l', "log-level", true }, - { OPT_EVENT_NAME, 'E', "event-name", true }, - - { OPT_TYPE, 't', "type", true }, - { OPT_LOCATION, 'L', "location", true }, - - /* Capture descriptor */ - { OPT_CAPTURE, '\0', "capture", true }, - - ARGPAR_OPT_DESCR_SENTINEL -}; - -static -bool has_syscall_prefix(const char *arg) -{ - bool matches = false; - const char kernel_syscall_type_opt_prefix[] = "kernel:syscall"; - const size_t kernel_syscall_type_opt_prefix_len = - sizeof(kernel_syscall_type_opt_prefix) - 1; - const char syscall_type_opt_prefix[] = "syscall"; - const size_t syscall_type_opt_prefix_len = - sizeof(syscall_type_opt_prefix) - 1; - - if (strncmp(arg, syscall_type_opt_prefix, - syscall_type_opt_prefix_len) == 0) { - matches = true; - } else if (strncmp(arg, kernel_syscall_type_opt_prefix, - kernel_syscall_type_opt_prefix_len) == 0) { - matches = true; - } else { - matches = false; - } - - return matches; -} - -static -bool assign_event_rule_type(enum lttng_event_rule_type *dest, const char *arg) -{ - bool ret; - - if (*dest != LTTNG_EVENT_RULE_TYPE_UNKNOWN) { - ERR("More than one `--type` was specified."); - goto error; - } - - if (strcmp(arg, "user") == 0 || strcmp(arg, "user:tracepoint") == 0) { - *dest = LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT; - } else if (strcmp(arg, "kernel") == 0 || - strcmp(arg, "kernel:tracepoint") == 0) { - *dest = LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT; - } else if (strcmp(arg, "jul") == 0 || strcmp(arg, "jul:logging") == 0) { - *dest = LTTNG_EVENT_RULE_TYPE_JUL_LOGGING; - } else if (strcmp(arg, "log4j") == 0 || - strcmp(arg, "log4j:logging") == 0) { - *dest = LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING; - } else if (strcmp(arg, "python") == 0 || - strcmp(arg, "python:logging") == 0) { - *dest = LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING; - } else if (strcmp(arg, "kprobe") == 0 || - strcmp(arg, "kernel:kprobe") == 0) { - *dest = LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE; - } else if (strcmp(arg, "kernel:uprobe") == 0) { - *dest = LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE; - } else if (has_syscall_prefix(arg)) { - /* - * Matches the following: - * - syscall - * - syscall:entry - * - syscall:exit - * - syscall:entry+exit - * - syscall:* - * - kernel:syscall - * - kernel:syscall:entry - * - kernel:syscall:exit - * - kernel:syscall:entry+exit - * - kernel:syscall:* - * - * Validation for the right side is left to further usage sites. - */ - *dest = LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL; - } else { - ERR("Invalid `--type` value: %s", arg); - goto error; - } - - ret = true; - goto end; - -error: - ret = false; - -end: - return ret; -} - -static -bool assign_string(char **dest, const char *src, const char *opt_name) -{ - bool ret; - - if (*dest) { - ERR("Duplicate '%s' given.", opt_name); - goto error; - } - - *dest = strdup(src); - if (!*dest) { - PERROR("Failed to allocate string '%s'.", opt_name); - goto error; - } - - ret = true; - goto end; - -error: - ret = false; - -end: - return ret; -} - -static bool parse_syscall_emission_site_from_type(const char *str, - enum lttng_event_rule_kernel_syscall_emission_site *type) -{ - bool ret = false; - const char kernel_prefix[] = "kernel:"; - const size_t kernel_prefix_len = sizeof(kernel_prefix) - 1; - - /* - * If the passed string is of the form "kernel:syscall*", move the - * pointer passed "kernel:". - */ - if (strncmp(str, kernel_prefix, kernel_prefix_len) == 0) { - str = &str[kernel_prefix_len]; - } - - if (strcmp(str, "syscall") == 0 || - strcmp(str, "syscall:entry+exit") == 0) { - *type = LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY_EXIT; - } else if (strcmp(str, "syscall:entry") == 0) { - *type = LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY; - } else if (strcmp(str, "syscall:exit") == 0) { - *type = LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_EXIT; - } else { - goto error; - } - - ret = true; - -error: - return ret; -} - -/* - * Parse `str` as a log level against the passed event rule type. - * - * Return the log level in `*log_level`. Return true in `*log_level_only` if - * the string specifies exactly this log level, false if it specifies at least - * this log level. - * - * Return true if the string was successfully parsed as a log level string. - */ -static bool parse_log_level_string(const char *str, - enum lttng_event_rule_type event_rule_type, - int *log_level, - bool *log_level_only) -{ - bool ret; - - switch (event_rule_type) { - case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: - { - enum lttng_loglevel log_level_min, log_level_max; - if (!loglevel_parse_range_string( - str, &log_level_min, &log_level_max)) { - goto error; - } - - /* Only support VAL and VAL.. for now. */ - if (log_level_min != log_level_max && - log_level_max != LTTNG_LOGLEVEL_EMERG) { - goto error; - } - - *log_level = (int) log_level_min; - *log_level_only = log_level_min == log_level_max; - break; - } - case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: - { - enum lttng_loglevel_log4j log_level_min, log_level_max; - if (!loglevel_log4j_parse_range_string( - str, &log_level_min, &log_level_max)) { - goto error; - } - - /* Only support VAL and VAL.. for now. */ - if (log_level_min != log_level_max && - log_level_max != LTTNG_LOGLEVEL_LOG4J_FATAL) { - goto error; - } - - *log_level = (int) log_level_min; - *log_level_only = log_level_min == log_level_max; - break; - } - case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: - { - enum lttng_loglevel_jul log_level_min, log_level_max; - if (!loglevel_jul_parse_range_string( - str, &log_level_min, &log_level_max)) { - goto error; - } - - /* Only support VAL and VAL.. for now. */ - if (log_level_min != log_level_max && - log_level_max != LTTNG_LOGLEVEL_JUL_SEVERE) { - goto error; - } - - *log_level = (int) log_level_min; - *log_level_only = log_level_min == log_level_max; - break; - } - case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: - { - enum lttng_loglevel_python log_level_min, log_level_max; - if (!loglevel_python_parse_range_string( - str, &log_level_min, &log_level_max)) { - goto error; - } - - /* Only support VAL and VAL.. for now. */ - if (log_level_min != log_level_max && - log_level_max != - LTTNG_LOGLEVEL_PYTHON_CRITICAL) { - goto error; - } - - *log_level = (int) log_level_min; - *log_level_only = log_level_min == log_level_max; - break; - } - default: - /* Invalid domain type. */ - abort(); - } - - ret = true; - goto end; - -error: - ret = false; - -end: - return ret; -} - -static int parse_kernel_probe_opts(const char *source, - struct lttng_kernel_probe_location **location) -{ - int ret = 0; - int match; - char s_hex[19]; - char name[LTTNG_SYMBOL_NAME_LEN]; - char *symbol_name = NULL; - uint64_t offset; - - /* Check for symbol+offset. */ - match = sscanf(source, - "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API - "[^'+']+%18s", - name, s_hex); - if (match == 2) { - if (*s_hex == '\0') { - ERR("Kernel probe symbol offset is missing."); - goto error; - } - - symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN); - if (!symbol_name) { - PERROR("Failed to copy kernel probe location symbol name."); - goto error; - } - offset = strtoul(s_hex, NULL, 0); - - *location = lttng_kernel_probe_location_symbol_create( - symbol_name, offset); - if (!*location) { - ERR("Failed to create symbol kernel probe location."); - goto error; - } - - goto end; - } - - /* Check for symbol. */ - if (isalpha(name[0]) || name[0] == '_') { - match = sscanf(source, - "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API - "s", - name); - if (match == 1) { - symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN); - if (!symbol_name) { - ERR("Failed to copy kernel probe location symbol name."); - goto error; - } - - *location = lttng_kernel_probe_location_symbol_create( - symbol_name, 0); - if (!*location) { - ERR("Failed to create symbol kernel probe location."); - goto error; - } - - goto end; - } - } - - /* Check for address. */ - match = sscanf(source, "%18s", s_hex); - if (match > 0) { - uint64_t address; - - if (*s_hex == '\0') { - ERR("Invalid kernel probe location address."); - goto error; - } - - address = strtoul(s_hex, NULL, 0); - *location = lttng_kernel_probe_location_address_create(address); - if (!*location) { - ERR("Failed to create symbol kernel probe location."); - goto error; - } - - goto end; - } - -error: - /* No match */ - ret = -1; - *location = NULL; - -end: - free(symbol_name); - return ret; -} - -static -struct lttng_event_expr *ir_op_load_expr_to_event_expr( - const struct ir_load_expression *load_expr, - const char *capture_str) -{ - char *provider_name = NULL; - struct lttng_event_expr *event_expr = NULL; - const struct ir_load_expression_op *load_expr_op = load_expr->child; - const enum ir_load_expression_type load_expr_child_type = - load_expr_op->type; - - switch (load_expr_child_type) { - case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT: - case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT: - { - const char *field_name; - - load_expr_op = load_expr_op->next; - LTTNG_ASSERT(load_expr_op); - LTTNG_ASSERT(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL); - field_name = load_expr_op->u.symbol; - LTTNG_ASSERT(field_name); - - event_expr = load_expr_child_type == IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT ? - lttng_event_expr_event_payload_field_create(field_name) : - lttng_event_expr_channel_context_field_create(field_name); - if (!event_expr) { - ERR("Failed to create %s event expression: field name = `%s`.", - load_expr_child_type == IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT ? - "payload field" : "channel context", - field_name); - goto error; - } - - break; - } - case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT: - { - const char *colon; - const char *type_name; - const char *field_name; - - load_expr_op = load_expr_op->next; - LTTNG_ASSERT(load_expr_op); - LTTNG_ASSERT(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL); - field_name = load_expr_op->u.symbol; - LTTNG_ASSERT(field_name); - - /* - * The field name needs to be of the form PROVIDER:TYPE. We - * split it here. - */ - colon = strchr(field_name, ':'); - if (!colon) { - ERR("Invalid app-specific context field name: missing colon in `%s`.", - field_name); - goto error; - } - - type_name = colon + 1; - if (*type_name == '\0') { - ERR("Invalid app-specific context field name: missing type name after colon in `%s`.", - field_name); - goto error; - } - - provider_name = strndup(field_name, colon - field_name); - if (!provider_name) { - PERROR("Failed to allocate field name string"); - goto error; - } - - event_expr = lttng_event_expr_app_specific_context_field_create( - provider_name, type_name); - if (!event_expr) { - ERR("Failed to create app-specific context field event expression: provider name = `%s`, type name = `%s`", - provider_name, type_name); - goto error; - } - - break; - } - default: - ERR("%s: unexpected load expr type %d.", __func__, - load_expr_op->type); - abort(); - } - - load_expr_op = load_expr_op->next; - - /* There may be a single array index after that. */ - if (load_expr_op->type == IR_LOAD_EXPRESSION_GET_INDEX) { - struct lttng_event_expr *index_event_expr; - const uint64_t index = load_expr_op->u.index; - - index_event_expr = lttng_event_expr_array_field_element_create(event_expr, index); - if (!index_event_expr) { - ERR("Failed to create array field element event expression."); - goto error; - } - - event_expr = index_event_expr; - load_expr_op = load_expr_op->next; - } - - switch (load_expr_op->type) { - case IR_LOAD_EXPRESSION_LOAD_FIELD: - /* - * This is what we expect, IR_LOAD_EXPRESSION_LOAD_FIELD is - * always found at the end of the chain. - */ - break; - case IR_LOAD_EXPRESSION_GET_SYMBOL: - ERR("While parsing expression `%s`: Capturing subfields is not supported.", - capture_str); - goto error; - - default: - ERR("%s: unexpected load expression operator %s.", __func__, - ir_load_expression_type_str(load_expr_op->type)); - abort(); - } - - goto end; - -error: - lttng_event_expr_destroy(event_expr); - event_expr = NULL; - -end: - free(provider_name); - - return event_expr; -} - -static -struct lttng_event_expr *ir_op_load_to_event_expr( - const struct ir_op *ir, const char *capture_str) -{ - struct lttng_event_expr *event_expr = NULL; - - LTTNG_ASSERT(ir->op == IR_OP_LOAD); - - switch (ir->data_type) { - case IR_DATA_EXPRESSION: - { - const struct ir_load_expression *ir_load_expr = - ir->u.load.u.expression; - - event_expr = ir_op_load_expr_to_event_expr( - ir_load_expr, capture_str); - break; - } - default: - ERR("%s: unexpected data type: %s.", __func__, - ir_data_type_str(ir->data_type)); - abort(); - } - - return event_expr; -} - -static -const char *ir_operator_type_human_str(enum ir_op_type op) -{ - const char *name; - - switch (op) { - case IR_OP_BINARY: - name = "Binary"; - break; - case IR_OP_UNARY: - name = "Unary"; - break; - case IR_OP_LOGICAL: - name = "Logical"; - break; - default: - abort(); - } - - return name; -} - -static -struct lttng_event_expr *ir_op_root_to_event_expr(const struct ir_op *ir, - const char *capture_str) -{ - struct lttng_event_expr *event_expr = NULL; - - LTTNG_ASSERT(ir->op == IR_OP_ROOT); - ir = ir->u.root.child; - - switch (ir->op) { - case IR_OP_LOAD: - event_expr = ir_op_load_to_event_expr(ir, capture_str); - break; - case IR_OP_BINARY: - case IR_OP_UNARY: - case IR_OP_LOGICAL: - ERR("While parsing expression `%s`: %s operators are not allowed in capture expressions.", - capture_str, - ir_operator_type_human_str(ir->op)); - break; - default: - ERR("%s: unexpected IR op type: %s.", __func__, - ir_op_type_str(ir->op)); - abort(); - } - - return event_expr; -} - -static -void destroy_event_expr(void *ptr) -{ - lttng_event_expr_destroy(ptr); -} - -struct parse_event_rule_res { - /* Owned by this. */ - struct lttng_event_rule *er; - - /* Array of `struct lttng_event_expr *` */ - struct lttng_dynamic_pointer_array capture_descriptors; -}; - -static -struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) -{ - enum lttng_event_rule_type event_rule_type = - LTTNG_EVENT_RULE_TYPE_UNKNOWN; - struct argpar_state *state; - struct argpar_item *item = NULL; - char *error = NULL; - int consumed_args = -1; - struct lttng_kernel_probe_location *kernel_probe_location = NULL; - struct lttng_userspace_probe_location *userspace_probe_location = NULL; - struct parse_event_rule_res res = { 0 }; - struct lttng_event_expr *event_expr = NULL; - struct filter_parser_ctx *parser_ctx = NULL; - struct lttng_log_level_rule *log_level_rule = NULL; - - /* Event rule type option */ - char *event_rule_type_str = NULL; - - /* Tracepoint and syscall options. */ - char *name = NULL; - /* Array of strings. */ - struct lttng_dynamic_pointer_array exclude_names; - - /* For userspace / kernel probe and function. */ - char *location = NULL; - char *event_name = NULL; - - /* Filter. */ - char *filter = NULL; - - /* Log level. */ - char *log_level_str = NULL; - - lttng_dynamic_pointer_array_init(&res.capture_descriptors, - destroy_event_expr); - - lttng_dynamic_pointer_array_init(&exclude_names, free); - - state = argpar_state_create(*argc, *argv, event_rule_opt_descrs); - if (!state) { - ERR("Failed to allocate an argpar state."); - goto error; - } - - while (true) { - enum argpar_state_parse_next_status status; - - ARGPAR_ITEM_DESTROY_AND_RESET(item); - status = argpar_state_parse_next(state, &item, &error); - if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { - ERR("%s", error); - goto error; - } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { - /* Just stop parsing here. */ - break; - } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { - break; - } - - LTTNG_ASSERT(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); - - if (item->type == ARGPAR_ITEM_TYPE_OPT) { - const struct argpar_item_opt *item_opt = - (const struct argpar_item_opt *) item; - - switch (item_opt->descr->id) { - case OPT_TYPE: - if (!assign_event_rule_type(&event_rule_type, - item_opt->arg)) { - goto error; - } - - /* Save the string for later use. */ - if (!assign_string(&event_rule_type_str, - item_opt->arg, - "--type/-t")) { - goto error; - } - - break; - case OPT_LOCATION: - if (!assign_string(&location, - item_opt->arg, - "--location/-L")) { - goto error; - } - - break; - case OPT_EVENT_NAME: - if (!assign_string(&event_name, - item_opt->arg, - "--event-name/-E")) { - goto error; - } - - break; - case OPT_FILTER: - if (!assign_string(&filter, item_opt->arg, - "--filter/-f")) { - goto error; - } - - break; - case OPT_NAME: - if (!assign_string(&name, item_opt->arg, - "--name/-n")) { - goto error; - } - - break; - case OPT_EXCLUDE_NAME: - { - int ret; - - ret = lttng_dynamic_pointer_array_add_pointer( - &exclude_names, - strdup(item_opt->arg)); - if (ret != 0) { - ERR("Failed to add pointer to dynamic pointer array."); - goto error; - } - - break; - } - case OPT_LOG_LEVEL: - if (!assign_string(&log_level_str, - item_opt->arg, "--log-level/-l")) { - goto error; - } - - break; - case OPT_CAPTURE: - { - int ret; - const char *capture_str = item_opt->arg; - - ret = filter_parser_ctx_create_from_filter_expression( - capture_str, &parser_ctx); - if (ret) { - ERR("Failed to parse capture expression `%s`.", - capture_str); - goto error; - } - - event_expr = ir_op_root_to_event_expr( - parser_ctx->ir_root, - capture_str); - if (!event_expr) { - /* - * ir_op_root_to_event_expr has printed - * an error message. - */ - goto error; - } - - ret = lttng_dynamic_pointer_array_add_pointer( - &res.capture_descriptors, - event_expr); - if (ret) { - goto error; - } - - /* - * The ownership of event expression was - * transferred to the dynamic array. - */ - event_expr = NULL; - - break; - } - default: - abort(); - } - } else { - const struct argpar_item_non_opt *item_non_opt = - (const struct argpar_item_non_opt *) - item; - - /* Don't accept non-option arguments. */ - ERR("Unexpected argument '%s'", item_non_opt->arg); - goto error; - } - } - - if (event_rule_type == LTTNG_EVENT_RULE_TYPE_UNKNOWN) { - ERR("Event rule requires a --type."); - goto error; - } - - /* - * Option --name is applicable to event rules of type kernel, user, jul, - * log4j,python and syscall. If --name is omitted, it is implicitly - * "*". - */ - switch (event_rule_type) { - case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: - case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: - case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: - case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: - case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: - case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: - if (!name) { - name = strdup("*"); - } - break; - - default: - if (name) { - ERR("Can't use --name with %s event rules.", - lttng_event_rule_type_str( - event_rule_type)); - goto error; - } - - if (lttng_dynamic_pointer_array_get_count(&exclude_names) > 0) { - ERR("Can't use --exclude-name/-x with %s event rules.", - lttng_event_rule_type_str( - event_rule_type)); - goto error; - } - } - - /* - * Option --location is only applicable to (and mandatory for) event - * rules of type {k,u}probe and function. - * - * Option --event-name is only applicable to event rules of type probe. - * If omitted, it defaults to the location. - */ - switch (event_rule_type) { - case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE: - case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE: - if (!location) { - ERR("Event rule of type %s requires a --location.", - lttng_event_rule_type_str(event_rule_type)); - goto error; - } - - if (!event_name) { - event_name = strdup(location); - } - - break; - - default: - if (location) { - ERR("Can't use --location with %s event rules.", - lttng_event_rule_type_str(event_rule_type)); - goto error; - } - - if (event_name) { - ERR("Can't use --event-name with %s event rules.", - lttng_event_rule_type_str( - event_rule_type)); - goto error; - } - } - - /* - * Update *argc and *argv so our caller can keep parsing what follows. - */ - consumed_args = argpar_state_get_ingested_orig_args(state); - LTTNG_ASSERT(consumed_args >= 0); - *argc -= consumed_args; - *argv += consumed_args; - - /* - * Adding a filter to a probe, function or userspace-probe would be - * denied by the kernel tracer as it's not supported at the moment. We - * do an early check here to warn the user. - */ - if (filter) { - switch (event_rule_type) { - case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: - case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: - case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: - case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: - case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: - case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: - break; - default: - ERR("Filter expressions are not supported for %s event rules.", - lttng_event_rule_type_str(event_rule_type)); - goto error; - } - } - - /* - * If --exclude-name/-x was passed, split it into an exclusion list. - * Exclusions are only supported by - * LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT for now. - */ - if (lttng_dynamic_pointer_array_get_count(&exclude_names) > 0) { - if (event_rule_type != LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT) { - ERR("Event name exclusions are not yet implemented for %s event rules.", - lttng_event_rule_type_str(event_rule_type)); - goto error; - } - - if (validate_exclusion_list(name, &exclude_names) != 0) { - /* - * Assume validate_exclusion_list already prints an - * error message. - */ - goto error; - } - } - - if (log_level_str) { - switch (event_rule_type) { - case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: - case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: - case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: - case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: - { - int log_level; - bool log_level_only; - - if (strcmp(log_level_str, "..") == 0) { - /* - * ".." is the same as passing no log level - * option and correspond to the "ANY" case. - */ - break; - } - - if (!parse_log_level_string(log_level_str, event_rule_type, - &log_level, &log_level_only)) { - ERR("Failed to parse log level string `%s`.", - log_level_str); - goto error; - } - - if (log_level_only) { - log_level_rule = lttng_log_level_rule_exactly_create(log_level); - } else { - log_level_rule = lttng_log_level_rule_at_least_as_severe_as_create(log_level); - } - - if (log_level_rule == NULL) { - ERR("Failed to create log level rule object."); - goto error; - } - break; - } - default: - ERR("Log levels are not supported for %s event rules.", - lttng_event_rule_type_str(event_rule_type)); - goto error; - } - } - - /* Finally, create the event rule object. */ - switch (event_rule_type) { - case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: - { - enum lttng_event_rule_status event_rule_status; - - res.er = lttng_event_rule_user_tracepoint_create(); - if (!res.er) { - ERR("Failed to create user_tracepoint event rule."); - goto error; - } - - /* Set pattern. */ - event_rule_status = lttng_event_rule_user_tracepoint_set_name_pattern( - res.er, name); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set user_tracepoint event rule's pattern to '%s'.", - name); - goto error; - } - - /* Set filter. */ - if (filter) { - event_rule_status = lttng_event_rule_user_tracepoint_set_filter( - res.er, filter); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set user_tracepoint event rule's filter to '%s'.", - filter); - goto error; - } - } - - /* Set exclusion list. */ - if (lttng_dynamic_pointer_array_get_count(&exclude_names) > 0) { - int n; - int count = lttng_dynamic_pointer_array_get_count( - &exclude_names); - - for (n = 0; n < count; n++) { - const char *exclude_name = - lttng_dynamic_pointer_array_get_pointer( - &exclude_names, - n); - - event_rule_status = - lttng_event_rule_user_tracepoint_add_name_pattern_exclusion( - res.er, - exclude_name); - if (event_rule_status != - LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set user_tracepoint exclusion list element '%s'", - exclude_name); - goto error; - } - } - } - - if (log_level_rule) { - event_rule_status = - lttng_event_rule_user_tracepoint_set_log_level_rule( - res.er, log_level_rule); - - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set log level on event fule."); - goto error; - } - } - - break; - } - case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: - { - enum lttng_event_rule_status event_rule_status; - - res.er = lttng_event_rule_kernel_tracepoint_create(); - if (!res.er) { - ERR("Failed to create kernel_tracepoint event rule."); - goto error; - } - - /* Set pattern. */ - event_rule_status = lttng_event_rule_kernel_tracepoint_set_name_pattern( - res.er, name); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set kernel_tracepoint event rule's pattern to '%s'.", - name); - goto error; - } - - /* Set filter. */ - if (filter) { - event_rule_status = lttng_event_rule_kernel_tracepoint_set_filter( - res.er, filter); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set kernel_tracepoint event rule's filter to '%s'.", - filter); - goto error; - } - } - break; - } - case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: - { - enum lttng_event_rule_status event_rule_status; - - res.er = lttng_event_rule_jul_logging_create(); - if (!res.er) { - ERR("Failed to create jul_logging event rule."); - goto error; - } - - /* Set pattern. */ - event_rule_status = lttng_event_rule_jul_logging_set_name_pattern( - res.er, name); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set jul_logging event rule's pattern to '%s'.", - name); - goto error; - } - - /* Set filter. */ - if (filter) { - event_rule_status = lttng_event_rule_jul_logging_set_filter( - res.er, filter); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set jul_logging event rule's filter to '%s'.", - filter); - goto error; - } - } - - if (log_level_rule) { - event_rule_status = - lttng_event_rule_jul_logging_set_log_level_rule( - res.er, log_level_rule); - - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set log level on event fule."); - goto error; - } - } - break; - } - case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: - { - enum lttng_event_rule_status event_rule_status; - - res.er = lttng_event_rule_log4j_logging_create(); - if (!res.er) { - ERR("Failed to create jul_logging event rule."); - goto error; - } - - /* Set pattern. */ - event_rule_status = lttng_event_rule_log4j_logging_set_name_pattern( - res.er, name); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set jul_logging event rule's pattern to '%s'.", - name); - goto error; - } - - /* Set filter. */ - if (filter) { - event_rule_status = lttng_event_rule_log4j_logging_set_filter( - res.er, filter); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set jul_logging event rule's filter to '%s'.", - filter); - goto error; - } - } - - if (log_level_rule) { - event_rule_status = - lttng_event_rule_log4j_logging_set_log_level_rule( - res.er, log_level_rule); - - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set log level on event fule."); - goto error; - } - } - break; - } - case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: - { - enum lttng_event_rule_status event_rule_status; - - res.er = lttng_event_rule_python_logging_create(); - if (!res.er) { - ERR("Failed to create jul_logging event rule."); - goto error; - } - - /* Set pattern. */ - event_rule_status = lttng_event_rule_python_logging_set_name_pattern( - res.er, name); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set jul_logging event rule's pattern to '%s'.", - name); - goto error; - } - - /* Set filter. */ - if (filter) { - event_rule_status = lttng_event_rule_python_logging_set_filter( - res.er, filter); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set jul_logging event rule's filter to '%s'.", - filter); - goto error; - } - } - - if (log_level_rule) { - event_rule_status = - lttng_event_rule_python_logging_set_log_level_rule( - res.er, log_level_rule); - - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set log level on event fule."); - goto error; - } - } - break; - } - case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE: - { - int ret; - enum lttng_event_rule_status event_rule_status; - - ret = parse_kernel_probe_opts( - location, &kernel_probe_location); - if (ret) { - ERR("Failed to parse kernel probe location."); - goto error; - } - - LTTNG_ASSERT(kernel_probe_location); - res.er = lttng_event_rule_kernel_kprobe_create(kernel_probe_location); - if (!res.er) { - ERR("Failed to create kprobe event rule."); - goto error; - } - - event_rule_status = - lttng_event_rule_kernel_kprobe_set_event_name( - res.er, event_name); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set kprobe event rule's name to '%s'.", - event_name); - goto error; - } - - break; - } - case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE: - { - int ret; - enum lttng_event_rule_status event_rule_status; - - ret = parse_userspace_probe_opts( - location, &userspace_probe_location); - if (ret) { - ERR("Failed to parse user space probe location."); - goto error; - } - - res.er = lttng_event_rule_kernel_uprobe_create(userspace_probe_location); - if (!res.er) { - ERR("Failed to create userspace probe event rule."); - goto error; - } - - event_rule_status = - lttng_event_rule_kernel_uprobe_set_event_name( - res.er, event_name); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set user space probe event rule's name to '%s'.", - event_name); - goto error; - } - - break; - } - case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: - { - enum lttng_event_rule_status event_rule_status; - enum lttng_event_rule_kernel_syscall_emission_site emission_site; - - if (!parse_syscall_emission_site_from_type( - event_rule_type_str, &emission_site)) { - ERR("Failed to parse syscall type '%s'.", event_rule_type_str); - goto error; - } - - res.er = lttng_event_rule_kernel_syscall_create(emission_site); - if (!res.er) { - ERR("Failed to create syscall event rule."); - goto error; - } - - event_rule_status = lttng_event_rule_kernel_syscall_set_name_pattern( - res.er, name); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set syscall event rule's pattern to '%s'.", - name); - goto error; - } - - if (filter) { - event_rule_status = lttng_event_rule_kernel_syscall_set_filter( - res.er, filter); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set syscall event rule's filter to '%s'.", - filter); - goto error; - } - } - - break; - } - default: - abort(); - goto error; - } - - goto end; - -error: - lttng_event_rule_destroy(res.er); - res.er = NULL; - lttng_dynamic_pointer_array_reset(&res.capture_descriptors); - -end: - if (parser_ctx) { - filter_parser_ctx_free(parser_ctx); - } - - lttng_event_expr_destroy(event_expr); - argpar_item_destroy(item); - free(error); - argpar_state_destroy(state); - free(filter); - free(name); - lttng_dynamic_pointer_array_reset(&exclude_names); - free(log_level_str); - free(location); - free(event_name); - free(event_rule_type_str); - - lttng_kernel_probe_location_destroy(kernel_probe_location); - lttng_userspace_probe_location_destroy(userspace_probe_location); - lttng_log_level_rule_destroy(log_level_rule); - return res; -} - -static -struct lttng_condition *handle_condition_event(int *argc, const char ***argv) -{ - struct parse_event_rule_res res; - struct lttng_condition *c; - size_t i; - - res = parse_event_rule(argc, argv); - if (!res.er) { - c = NULL; - goto error; - } - - c = lttng_condition_event_rule_matches_create(res.er); - lttng_event_rule_destroy(res.er); - res.er = NULL; - if (!c) { - goto error; - } - - for (i = 0; i < lttng_dynamic_pointer_array_get_count(&res.capture_descriptors); - i++) { - enum lttng_condition_status status; - struct lttng_event_expr **expr = - lttng_dynamic_array_get_element( - &res.capture_descriptors.array, i); - - LTTNG_ASSERT(expr); - LTTNG_ASSERT(*expr); - status = lttng_condition_event_rule_matches_append_capture_descriptor( - c, *expr); - if (status != LTTNG_CONDITION_STATUS_OK) { - if (status == LTTNG_CONDITION_STATUS_UNSUPPORTED) { - ERR("The capture feature is unsupported by the event-rule condition type"); - } - - goto error; - } - - /* Ownership of event expression moved to `c` */ - *expr = NULL; - } - - goto end; - -error: - lttng_condition_destroy(c); - c = NULL; - -end: - lttng_dynamic_pointer_array_reset(&res.capture_descriptors); - lttng_event_rule_destroy(res.er); - return c; -} - -struct condition_descr { - const char *name; - struct lttng_condition *(*handler) (int *argc, const char ***argv); -}; - -static const -struct condition_descr condition_descrs[] = { - { "event-rule-matches", handle_condition_event }, -}; - -static -struct lttng_condition *parse_condition(const char *condition_name, int *argc, - const char ***argv) -{ - int i; - struct lttng_condition *cond; - const struct condition_descr *descr = NULL; - - for (i = 0; i < ARRAY_SIZE(condition_descrs); i++) { - if (strcmp(condition_name, condition_descrs[i].name) == 0) { - descr = &condition_descrs[i]; - break; - } - } - - if (!descr) { - ERR("Unknown condition name '%s'", condition_name); - goto error; - } - - cond = descr->handler(argc, argv); - if (!cond) { - /* The handler has already printed an error message. */ - goto error; - } - - goto end; -error: - cond = NULL; -end: - return cond; -} - -static struct lttng_rate_policy *parse_rate_policy(const char *policy_str) -{ - int ret; - size_t num_token = 0; - struct lttng_dynamic_pointer_array tokens; - struct lttng_rate_policy *policy = NULL; - enum lttng_rate_policy_type policy_type; - unsigned long long value; - char *policy_type_str; - char *policy_value_str; - - LTTNG_ASSERT(policy_str); - lttng_dynamic_pointer_array_init(&tokens, NULL); - - /* Rate policy fields are separated by ':'. */ - ret = strutils_split(policy_str, ':', 1, &tokens); - if (ret == 0) { - num_token = lttng_dynamic_pointer_array_get_count(&tokens); - } - - /* - * Early sanity check that the number of parameter is exactly 2. - * i.e : type:value - */ - if (num_token != 2) { - ERR("Rate policy format is invalid."); - goto end; - } - - policy_type_str = lttng_dynamic_pointer_array_get_pointer(&tokens, 0); - policy_value_str = lttng_dynamic_pointer_array_get_pointer(&tokens, 1); - - /* Parse the type. */ - if (strcmp(policy_type_str, "once-after") == 0) { - policy_type = LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N; - } else if (strcmp(policy_type_str, "every") == 0) { - policy_type = LTTNG_RATE_POLICY_TYPE_EVERY_N; - } else { - ERR("Rate policy type `%s` unknown.", policy_type_str); - goto end; - } - - /* Parse the value. */ - if (utils_parse_unsigned_long_long(policy_value_str, &value) != 0) { - ERR("Failed to parse rate policy value `%s` as an integer.", - policy_value_str); - goto end; - } - - if (value == 0) { - ERR("Rate policy value `%s` must be > 0.", policy_value_str); - goto end; - } - - switch (policy_type) { - case LTTNG_RATE_POLICY_TYPE_EVERY_N: - policy = lttng_rate_policy_every_n_create(value); - break; - case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N: - policy = lttng_rate_policy_once_after_n_create(value); - break; - default: - abort(); - } - - if (policy == NULL) { - ERR("Failed to create rate policy `%s`.", policy_str); - } - -end: - lttng_dynamic_pointer_array_reset(&tokens); - return policy; -} - -static const struct argpar_opt_descr notify_action_opt_descrs[] = { - { OPT_RATE_POLICY, '\0', "rate-policy", true }, - ARGPAR_OPT_DESCR_SENTINEL -}; - -static -struct lttng_action *handle_action_notify(int *argc, const char ***argv) -{ - struct lttng_action *action = NULL; - struct argpar_state *state = NULL; - struct argpar_item *item = NULL; - char *error = NULL; - struct lttng_rate_policy *policy = NULL; - - state = argpar_state_create(*argc, *argv, notify_action_opt_descrs); - if (!state) { - ERR("Failed to allocate an argpar state."); - goto error; - } - - while (true) { - enum argpar_state_parse_next_status status; - - ARGPAR_ITEM_DESTROY_AND_RESET(item); - status = argpar_state_parse_next(state, &item, &error); - if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { - ERR("%s", error); - goto error; - } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { - /* Just stop parsing here. */ - break; - } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { - break; - } - - LTTNG_ASSERT(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); - - if (item->type == ARGPAR_ITEM_TYPE_OPT) { - const struct argpar_item_opt *item_opt = - (const struct argpar_item_opt *) item; - - switch (item_opt->descr->id) { - case OPT_RATE_POLICY: - { - policy = parse_rate_policy(item_opt->arg); - if (!policy) { - goto error; - } - break; - } - default: - abort(); - } - } else { - const struct argpar_item_non_opt *item_non_opt; - - LTTNG_ASSERT(item->type == ARGPAR_ITEM_TYPE_NON_OPT); - - item_non_opt = (const struct argpar_item_non_opt *) item; - - switch (item_non_opt->non_opt_index) { - default: - ERR("Unexpected argument `%s`.", - item_non_opt->arg); - goto error; - } - } - } - - *argc -= argpar_state_get_ingested_orig_args(state); - *argv += argpar_state_get_ingested_orig_args(state); - - action = lttng_action_notify_create(); - if (!action) { - ERR("Failed to create notify action"); - goto error; - } - - if (policy) { - enum lttng_action_status status; - status = lttng_action_notify_set_rate_policy(action, policy); - if (status != LTTNG_ACTION_STATUS_OK) { - ERR("Failed to set rate policy"); - goto error; - } - } - - goto end; - -error: - lttng_action_destroy(action); - action = NULL; -end: - free(error); - lttng_rate_policy_destroy(policy); - argpar_state_destroy(state); - argpar_item_destroy(item); - return action; -} - -/* - * Generic handler for a kind of action that takes a session name and an - * optional rate policy. - */ - -static struct lttng_action *handle_action_simple_session_with_policy(int *argc, - const char ***argv, - struct lttng_action *(*create_action_cb)(void), - enum lttng_action_status (*set_session_name_cb)( - struct lttng_action *, const char *), - enum lttng_action_status (*set_rate_policy_cb)( - struct lttng_action *, - const struct lttng_rate_policy *), - const char *action_name) -{ - struct lttng_action *action = NULL; - struct argpar_state *state = NULL; - struct argpar_item *item = NULL; - const char *session_name_arg = NULL; - char *error = NULL; - enum lttng_action_status action_status; - struct lttng_rate_policy *policy = NULL; - - LTTNG_ASSERT(set_session_name_cb); - LTTNG_ASSERT(set_rate_policy_cb); - - const struct argpar_opt_descr rate_policy_opt_descrs[] = { - { OPT_RATE_POLICY, '\0', "rate-policy", true }, - ARGPAR_OPT_DESCR_SENTINEL - }; - - state = argpar_state_create(*argc, *argv, rate_policy_opt_descrs); - if (!state) { - ERR("Failed to allocate an argpar state."); - goto error; - } - - while (true) { - enum argpar_state_parse_next_status status; - - ARGPAR_ITEM_DESTROY_AND_RESET(item); - status = argpar_state_parse_next(state, &item, &error); - if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { - ERR("%s", error); - goto error; - } else if (status == - ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { - /* Just stop parsing here. */ - break; - } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { - break; - } - - LTTNG_ASSERT(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); - if (item->type == ARGPAR_ITEM_TYPE_OPT) { - const struct argpar_item_opt *item_opt = - (const struct argpar_item_opt *) item; - - switch (item_opt->descr->id) { - case OPT_RATE_POLICY: - { - policy = parse_rate_policy(item_opt->arg); - if (!policy) { - goto error; - } - break; - } - default: - abort(); - } - } else { - const struct argpar_item_non_opt *item_non_opt; - item_non_opt = (const struct argpar_item_non_opt *) item; - - switch (item_non_opt->non_opt_index) { - case 0: - session_name_arg = item_non_opt->arg; - break; - default: - ERR("Unexpected argument `%s`.", - item_non_opt->arg); - goto error; - } - } - } - - *argc -= argpar_state_get_ingested_orig_args(state); - *argv += argpar_state_get_ingested_orig_args(state); - - if (!session_name_arg) { - ERR("Missing session name."); - goto error; - } - - action = create_action_cb(); - if (!action) { - ERR("Failed to allocate %s session action.", action_name); - goto error; - } - - action_status = set_session_name_cb(action, session_name_arg); - if (action_status != LTTNG_ACTION_STATUS_OK) { - ERR("Failed to set action %s session's session name to '%s'.", - action_name, session_name_arg); - goto error; - } - - if (policy) { - action_status = set_rate_policy_cb(action, policy); - if (action_status != LTTNG_ACTION_STATUS_OK) { - ERR("Failed to set rate policy"); - goto error; - } - } - - goto end; - -error: - lttng_action_destroy(action); - action = NULL; - argpar_item_destroy(item); -end: - lttng_rate_policy_destroy(policy); - free(error); - argpar_state_destroy(state); - return action; -} - -static -struct lttng_action *handle_action_start_session(int *argc, - const char ***argv) -{ - return handle_action_simple_session_with_policy(argc, argv, - lttng_action_start_session_create, - lttng_action_start_session_set_session_name, - lttng_action_start_session_set_rate_policy, "start"); -} - -static -struct lttng_action *handle_action_stop_session(int *argc, - const char ***argv) -{ - return handle_action_simple_session_with_policy(argc, argv, - lttng_action_stop_session_create, - lttng_action_stop_session_set_session_name, - lttng_action_stop_session_set_rate_policy, "stop"); -} - -static -struct lttng_action *handle_action_rotate_session(int *argc, - const char ***argv) -{ - return handle_action_simple_session_with_policy(argc, argv, - lttng_action_rotate_session_create, - lttng_action_rotate_session_set_session_name, - lttng_action_rotate_session_set_rate_policy, - "rotate"); -} - -static const struct argpar_opt_descr snapshot_action_opt_descrs[] = { - { OPT_NAME, 'n', "name", true }, - { OPT_MAX_SIZE, 'm', "max-size", true }, - { OPT_CTRL_URL, '\0', "ctrl-url", true }, - { OPT_DATA_URL, '\0', "data-url", true }, - { OPT_URL, '\0', "url", true }, - { OPT_PATH, '\0', "path", true }, - { OPT_RATE_POLICY, '\0', "rate-policy", true }, - ARGPAR_OPT_DESCR_SENTINEL -}; - -static -struct lttng_action *handle_action_snapshot_session(int *argc, - const char ***argv) -{ - struct lttng_action *action = NULL; - struct argpar_state *state = NULL; - struct argpar_item *item = NULL; - const char *session_name_arg = NULL; - char *snapshot_name_arg = NULL; - char *ctrl_url_arg = NULL; - char *data_url_arg = NULL; - char *max_size_arg = NULL; - char *url_arg = NULL; - char *path_arg = NULL; - char *error = NULL; - enum lttng_action_status action_status; - struct lttng_snapshot_output *snapshot_output = NULL; - struct lttng_rate_policy *policy = NULL; - int ret; - unsigned int locations_specified = 0; - - state = argpar_state_create(*argc, *argv, snapshot_action_opt_descrs); - if (!state) { - ERR("Failed to allocate an argpar state."); - goto error; - } - - while (true) { - enum argpar_state_parse_next_status status; - - ARGPAR_ITEM_DESTROY_AND_RESET(item); - status = argpar_state_parse_next(state, &item, &error); - if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { - ERR("%s", error); - goto error; - } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { - /* Just stop parsing here. */ - break; - } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { - break; - } - - LTTNG_ASSERT(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); - - if (item->type == ARGPAR_ITEM_TYPE_OPT) { - const struct argpar_item_opt *item_opt = - (const struct argpar_item_opt *) item; - - switch (item_opt->descr->id) { - case OPT_NAME: - if (!assign_string(&snapshot_name_arg, item_opt->arg, "--name/-n")) { - goto error; - } - - break; - case OPT_MAX_SIZE: - if (!assign_string(&max_size_arg, item_opt->arg, "--max-size/-m")) { - goto error; - } - - break; - case OPT_CTRL_URL: - if (!assign_string(&ctrl_url_arg, item_opt->arg, "--ctrl-url")) { - goto error; - } - - break; - case OPT_DATA_URL: - if (!assign_string(&data_url_arg, item_opt->arg, "--data-url")) { - goto error; - } - - break; - case OPT_URL: - if (!assign_string(&url_arg, item_opt->arg, "--url")) { - goto error; - } - - break; - case OPT_PATH: - if (!assign_string(&path_arg, item_opt->arg, "--path")) { - goto error; - } - - break; - case OPT_RATE_POLICY: - { - policy = parse_rate_policy(item_opt->arg); - if (!policy) { - goto error; - } - break; - } - default: - abort(); - } - } else { - const struct argpar_item_non_opt *item_non_opt; - - LTTNG_ASSERT(item->type == ARGPAR_ITEM_TYPE_NON_OPT); - - item_non_opt = (const struct argpar_item_non_opt *) item; - - switch (item_non_opt->non_opt_index) { - case 0: - session_name_arg = item_non_opt->arg; - break; - default: - ERR("Unexpected argument `%s`.", - item_non_opt->arg); - goto error; - } - } - } - - *argc -= argpar_state_get_ingested_orig_args(state); - *argv += argpar_state_get_ingested_orig_args(state); - - if (!session_name_arg) { - ERR("Missing session name."); - goto error; - } - - /* --ctrl-url and --data-url must come in pair. */ - if (ctrl_url_arg && !data_url_arg) { - ERR("--ctrl-url is specified, but --data-url is missing."); - goto error; - } - - if (!ctrl_url_arg && data_url_arg) { - ERR("--data-url is specified, but --ctrl-url is missing."); - goto error; - } - - locations_specified += !!(ctrl_url_arg || data_url_arg); - locations_specified += !!url_arg; - locations_specified += !!path_arg; - - /* --ctrl-url/--data-url, --url and --path are mutually exclusive. */ - if (locations_specified > 1) { - ERR("The --ctrl-url/--data-url, --url, and --path options can't be used together."); - goto error; - } - - /* - * Did the user specify an option that implies using a - * custom/unregistered output? - */ - if (url_arg || ctrl_url_arg || path_arg) { - snapshot_output = lttng_snapshot_output_create(); - if (!snapshot_output) { - ERR("Failed to allocate a snapshot output."); - goto error; - } - } - - action = lttng_action_snapshot_session_create(); - if (!action) { - ERR("Failed to allocate snapshot session action."); - goto error; - } - - action_status = lttng_action_snapshot_session_set_session_name( - action, session_name_arg); - if (action_status != LTTNG_ACTION_STATUS_OK) { - ERR("Failed to set action snapshot session's session name to '%s'.", - session_name_arg); - goto error; - } - - if (snapshot_name_arg) { - if (!snapshot_output) { - ERR("Can't provide a snapshot output name without a snapshot output destination."); - goto error; - } - - ret = lttng_snapshot_output_set_name( - snapshot_name_arg, snapshot_output); - if (ret != 0) { - ERR("Failed to set name of snapshot output."); - goto error; - } - } - - if (max_size_arg) { - uint64_t max_size; - - if (!snapshot_output) { - ERR("Can't provide a snapshot output max size without a snapshot output destination."); - goto error; - } - - ret = utils_parse_size_suffix(max_size_arg, &max_size); - if (ret != 0) { - ERR("Failed to parse `%s` as a size.", max_size_arg); - goto error; - } - - ret = lttng_snapshot_output_set_size(max_size, snapshot_output); - if (ret != 0) { - ERR("Failed to set snapshot output's max size to %" PRIu64 " bytes.", - max_size); - goto error; - } - } - - if (url_arg) { - int num_uris; - struct lttng_uri *uris; - - if (!strstr(url_arg, "://")) { - ERR("Failed to parse '%s' as an URL.", url_arg); - goto error; - } - - num_uris = uri_parse_str_urls(url_arg, NULL, &uris); - if (num_uris < 1) { - ERR("Failed to parse '%s' as an URL.", url_arg); - goto error; - } - - if (uris[0].dtype == LTTNG_DST_PATH) { - ret = lttng_snapshot_output_set_local_path( - uris[0].dst.path, snapshot_output); - free(uris); - if (ret != 0) { - ERR("Failed to assign '%s' as a local destination.", - url_arg); - goto error; - } - } else { - ret = lttng_snapshot_output_set_network_url( - url_arg, snapshot_output); - free(uris); - if (ret != 0) { - ERR("Failed to assign '%s' as a network URL.", - url_arg); - goto error; - } - } - } - - if (path_arg) { - ret = lttng_snapshot_output_set_local_path( - path_arg, snapshot_output); - if (ret != 0) { - ERR("Failed to parse '%s' as a local path.", path_arg); - goto error; - } - } - - if (ctrl_url_arg) { - /* - * Two argument form, network output with separate control and - * data URLs. - */ - ret = lttng_snapshot_output_set_network_urls( - ctrl_url_arg, data_url_arg, snapshot_output); - if (ret != 0) { - ERR("Failed to parse `%s` and `%s` as control and data URLs.", - ctrl_url_arg, data_url_arg); - goto error; - } - } - - if (snapshot_output) { - action_status = lttng_action_snapshot_session_set_output( - action, snapshot_output); - if (action_status != LTTNG_ACTION_STATUS_OK) { - ERR("Failed to set snapshot session action's output."); - goto error; - } - - /* Ownership of `snapshot_output` has been transferred to the action. */ - snapshot_output = NULL; - } - - if (policy) { - enum lttng_action_status status; - status = lttng_action_snapshot_session_set_rate_policy( - action, policy); - if (status != LTTNG_ACTION_STATUS_OK) { - ERR("Failed to set rate policy"); - goto error; - } - } - - goto end; - -error: - lttng_action_destroy(action); - action = NULL; - free(error); -end: - free(snapshot_name_arg); - free(path_arg); - free(url_arg); - free(ctrl_url_arg); - free(data_url_arg); - free(snapshot_output); - free(max_size_arg); - lttng_rate_policy_destroy(policy); - argpar_state_destroy(state); - argpar_item_destroy(item); - return action; -} - -struct action_descr { - const char *name; - struct lttng_action *(*handler) (int *argc, const char ***argv); -}; - -static const -struct action_descr action_descrs[] = { - { "notify", handle_action_notify }, - { "start-session", handle_action_start_session }, - { "stop-session", handle_action_stop_session }, - { "rotate-session", handle_action_rotate_session }, - { "snapshot-session", handle_action_snapshot_session }, -}; - -static -struct lttng_action *parse_action(const char *action_name, int *argc, const char ***argv) -{ - int i; - struct lttng_action *action; - const struct action_descr *descr = NULL; - - for (i = 0; i < ARRAY_SIZE(action_descrs); i++) { - if (strcmp(action_name, action_descrs[i].name) == 0) { - descr = &action_descrs[i]; - break; - } - } - - if (!descr) { - ERR("Unknown action name: %s", action_name); - goto error; - } - - action = descr->handler(argc, argv); - if (!action) { - /* The handler has already printed an error message. */ - goto error; - } - - goto end; -error: - action = NULL; -end: - return action; -} - -static const -struct argpar_opt_descr add_trigger_options[] = { - { OPT_HELP, 'h', "help", false }, - { OPT_LIST_OPTIONS, '\0', "list-options", false }, - { OPT_CONDITION, '\0', "condition", true }, - { OPT_ACTION, '\0', "action", true }, - { OPT_NAME, '\0', "name", true }, - { OPT_OWNER_UID, '\0', "owner-uid", true }, - ARGPAR_OPT_DESCR_SENTINEL, -}; - -static -void lttng_actions_destructor(void *p) -{ - struct lttng_action *action = p; - - lttng_action_destroy(action); -} - -int cmd_add_trigger(int argc, const char **argv) -{ - int ret; - int my_argc = argc - 1; - const char **my_argv = argv + 1; - struct lttng_condition *condition = NULL; - struct lttng_dynamic_pointer_array actions; - struct argpar_state *argpar_state = NULL; - struct argpar_item *argpar_item = NULL; - struct lttng_action *action_list = NULL; - struct lttng_action *action = NULL; - struct lttng_trigger *trigger = NULL; - char *error = NULL; - char *name = NULL; - int i; - char *owner_uid = NULL; - enum lttng_error_code ret_code; - struct mi_writer *mi_writer = NULL; - - lttng_dynamic_pointer_array_init(&actions, lttng_actions_destructor); - - if (lttng_opt_mi) { - mi_writer = mi_lttng_writer_create( - fileno(stdout), lttng_opt_mi); - if (!mi_writer) { - ret = CMD_ERROR; - goto error; - } - - /* Open command element. */ - ret = mi_lttng_writer_command_open(mi_writer, - mi_lttng_element_command_add_trigger); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - /* Open output element. */ - ret = mi_lttng_writer_open_element( - mi_writer, mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - - while (true) { - enum argpar_state_parse_next_status status; - const struct argpar_item_opt *item_opt; - int ingested_args; - - argpar_state_destroy(argpar_state); - argpar_state = argpar_state_create(my_argc, my_argv, - add_trigger_options); - if (!argpar_state) { - ERR("Failed to create argpar state."); - goto error; - } - - ARGPAR_ITEM_DESTROY_AND_RESET(argpar_item); - status = argpar_state_parse_next(argpar_state, &argpar_item, &error); - if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { - ERR("%s", error); - goto error; - } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { - ERR("%s", error); - goto error; - } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { - break; - } - - LTTNG_ASSERT(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); - - if (argpar_item->type == ARGPAR_ITEM_TYPE_NON_OPT) { - const struct argpar_item_non_opt *item_non_opt = - (const struct argpar_item_non_opt *) - argpar_item; - - ERR("Unexpected argument `%s`.", item_non_opt->arg); - goto error; - } - - item_opt = (const struct argpar_item_opt *) argpar_item; - - ingested_args = argpar_state_get_ingested_orig_args( - argpar_state); - - my_argc -= ingested_args; - my_argv += ingested_args; - - switch (item_opt->descr->id) { - case OPT_HELP: - SHOW_HELP(); - ret = 0; - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options_argpar(stdout, add_trigger_options); - ret = 0; - goto end; - case OPT_CONDITION: - { - if (condition) { - ERR("A --condition was already given."); - goto error; - } - - condition = parse_condition(item_opt->arg, &my_argc, &my_argv); - if (!condition) { - /* - * An error message was already printed by - * parse_condition. - */ - goto error; - } - - break; - } - case OPT_ACTION: - { - action = parse_action(item_opt->arg, &my_argc, &my_argv); - if (!action) { - /* - * An error message was already printed by - * parse_condition. - */ - goto error; - } - - ret = lttng_dynamic_pointer_array_add_pointer( - &actions, action); - if (ret) { - ERR("Failed to add pointer to pointer array."); - goto error; - } - - /* Ownership of the action was transferred to the list. */ - action = NULL; - - break; - } - case OPT_NAME: - { - if (!assign_string(&name, item_opt->arg, "--name")) { - goto error; - } - - break; - } - case OPT_OWNER_UID: - { - if (!assign_string(&owner_uid, item_opt->arg, - "--owner-uid")) { - goto error; - } - - break; - } - default: - abort(); - } - } - - if (!condition) { - ERR("Missing --condition."); - goto error; - } - - if (lttng_dynamic_pointer_array_get_count(&actions) == 0) { - ERR("Need at least one --action."); - goto error; - } - - action_list = lttng_action_list_create(); - if (!action_list) { - goto error; - } - - for (i = 0; i < lttng_dynamic_pointer_array_get_count(&actions); i++) { - enum lttng_action_status status; - - action = lttng_dynamic_pointer_array_steal_pointer(&actions, i); - - status = lttng_action_list_add_action(action_list, action); - if (status != LTTNG_ACTION_STATUS_OK) { - goto error; - } - - /* - * The `lttng_action_list_add_action()` takes a reference to - * the action. We can destroy ours. - */ - lttng_action_destroy(action); - action = NULL; - } - - trigger = lttng_trigger_create(condition, action_list); - if (!trigger) { - goto error; - } - - if (owner_uid) { - enum lttng_trigger_status trigger_status; - char *end; - long long uid; - - errno = 0; - uid = strtol(owner_uid, &end, 10); - if (end == owner_uid || *end != '\0' || errno != 0) { - ERR("Failed to parse `%s` as a user id.", owner_uid); - goto error; - } - - trigger_status = lttng_trigger_set_owner_uid(trigger, uid); - if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { - ERR("Failed to set trigger's user identity."); - goto error; - } - } - - if (name) { - ret_code = lttng_register_trigger_with_name(trigger, name); - } else { - ret_code = lttng_register_trigger_with_automatic_name(trigger); - } - - if (ret_code != LTTNG_OK) { - ERR("Failed to register trigger: %s.", - lttng_strerror(-ret_code)); - goto error; - } - - if (lttng_opt_mi) { - ret_code = lttng_trigger_mi_serialize(trigger, mi_writer, NULL); - if (ret_code != LTTNG_OK) { - goto error; - } - } else { - const char *returned_trigger_name; - const enum lttng_trigger_status trigger_status = - lttng_trigger_get_name(trigger, - &returned_trigger_name); - - if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { - WARN("Failed to retrieve the added trigger's name."); - } else { - MSG("Added trigger `%s`.", returned_trigger_name); - } - } - - ret = 0; - - goto end; - -error: - ret = 1; - -end: - /* Mi closing. */ - if (lttng_opt_mi && mi_writer) { - int mi_ret; - - /* Close output element. */ - mi_ret = mi_lttng_writer_close_element(mi_writer); - if (mi_ret) { - ret = 1; - goto cleanup; - } - - mi_ret = mi_lttng_writer_write_element_bool(mi_writer, - mi_lttng_element_command_success, ret ? 0 : 1); - if (mi_ret) { - ret = 1; - goto cleanup; - } - - /* Command element close. */ - mi_ret = mi_lttng_writer_command_close(mi_writer); - if (mi_ret) { - ret = 1; - goto cleanup; - } - } - -cleanup: - argpar_state_destroy(argpar_state); - argpar_item_destroy(argpar_item); - lttng_dynamic_pointer_array_reset(&actions); - lttng_condition_destroy(condition); - lttng_action_destroy(action_list); - lttng_action_destroy(action); - lttng_trigger_destroy(trigger); - free(error); - free(name); - free(owner_uid); - if (mi_writer && mi_lttng_writer_destroy(mi_writer)) { - /* Preserve original error code. */ - ret = ret ? ret : CMD_ERROR; - } - - return ret; -} diff --git a/src/bin/lttng/commands/add_trigger.cpp b/src/bin/lttng/commands/add_trigger.cpp new file mode 100644 index 000000000..939d51080 --- /dev/null +++ b/src/bin/lttng/commands/add_trigger.cpp @@ -0,0 +1,2475 @@ +/* + * Copyright (C) 2021 Simon Marchi + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include + +#include "../command.h" +#include "../loglevel.h" +#include "../uprobe.h" + +#include "common/argpar/argpar.h" +#include "common/dynamic-array.h" +#include "common/mi-lttng.h" +#include "common/string-utils/string-utils.h" +#include "common/utils.h" +#include +/* For lttng_event_rule_type_str(). */ +#include +#include +#include "common/filter/filter-ast.h" +#include "common/filter/filter-ir.h" +#include "common/dynamic-array.h" + +#if (LTTNG_SYMBOL_NAME_LEN == 256) +#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255" +#endif + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP, + OPT_LIST_OPTIONS, + + OPT_CONDITION, + OPT_ACTION, + OPT_ID, + OPT_OWNER_UID, + OPT_RATE_POLICY, + + OPT_NAME, + OPT_FILTER, + OPT_EXCLUDE_NAME, + OPT_EVENT_NAME, + OPT_LOG_LEVEL, + + OPT_TYPE, + OPT_LOCATION, + + OPT_MAX_SIZE, + OPT_DATA_URL, + OPT_CTRL_URL, + OPT_URL, + OPT_PATH, + + OPT_CAPTURE, +}; + +static const struct argpar_opt_descr event_rule_opt_descrs[] = { + { OPT_FILTER, 'f', "filter", true }, + { OPT_NAME, 'n', "name", true }, + { OPT_EXCLUDE_NAME, 'x', "exclude-name", true }, + { OPT_LOG_LEVEL, 'l', "log-level", true }, + { OPT_EVENT_NAME, 'E', "event-name", true }, + + { OPT_TYPE, 't', "type", true }, + { OPT_LOCATION, 'L', "location", true }, + + /* Capture descriptor */ + { OPT_CAPTURE, '\0', "capture", true }, + + ARGPAR_OPT_DESCR_SENTINEL +}; + +static +bool has_syscall_prefix(const char *arg) +{ + bool matches = false; + const char kernel_syscall_type_opt_prefix[] = "kernel:syscall"; + const size_t kernel_syscall_type_opt_prefix_len = + sizeof(kernel_syscall_type_opt_prefix) - 1; + const char syscall_type_opt_prefix[] = "syscall"; + const size_t syscall_type_opt_prefix_len = + sizeof(syscall_type_opt_prefix) - 1; + + if (strncmp(arg, syscall_type_opt_prefix, + syscall_type_opt_prefix_len) == 0) { + matches = true; + } else if (strncmp(arg, kernel_syscall_type_opt_prefix, + kernel_syscall_type_opt_prefix_len) == 0) { + matches = true; + } else { + matches = false; + } + + return matches; +} + +static +bool assign_event_rule_type(enum lttng_event_rule_type *dest, const char *arg) +{ + bool ret; + + if (*dest != LTTNG_EVENT_RULE_TYPE_UNKNOWN) { + ERR("More than one `--type` was specified."); + goto error; + } + + if (strcmp(arg, "user") == 0 || strcmp(arg, "user:tracepoint") == 0) { + *dest = LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT; + } else if (strcmp(arg, "kernel") == 0 || + strcmp(arg, "kernel:tracepoint") == 0) { + *dest = LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT; + } else if (strcmp(arg, "jul") == 0 || strcmp(arg, "jul:logging") == 0) { + *dest = LTTNG_EVENT_RULE_TYPE_JUL_LOGGING; + } else if (strcmp(arg, "log4j") == 0 || + strcmp(arg, "log4j:logging") == 0) { + *dest = LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING; + } else if (strcmp(arg, "python") == 0 || + strcmp(arg, "python:logging") == 0) { + *dest = LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING; + } else if (strcmp(arg, "kprobe") == 0 || + strcmp(arg, "kernel:kprobe") == 0) { + *dest = LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE; + } else if (strcmp(arg, "kernel:uprobe") == 0) { + *dest = LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE; + } else if (has_syscall_prefix(arg)) { + /* + * Matches the following: + * - syscall + * - syscall:entry + * - syscall:exit + * - syscall:entry+exit + * - syscall:* + * - kernel:syscall + * - kernel:syscall:entry + * - kernel:syscall:exit + * - kernel:syscall:entry+exit + * - kernel:syscall:* + * + * Validation for the right side is left to further usage sites. + */ + *dest = LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL; + } else { + ERR("Invalid `--type` value: %s", arg); + goto error; + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +static +bool assign_string(char **dest, const char *src, const char *opt_name) +{ + bool ret; + + if (*dest) { + ERR("Duplicate '%s' given.", opt_name); + goto error; + } + + *dest = strdup(src); + if (!*dest) { + PERROR("Failed to allocate string '%s'.", opt_name); + goto error; + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +static bool parse_syscall_emission_site_from_type(const char *str, + enum lttng_event_rule_kernel_syscall_emission_site *type) +{ + bool ret = false; + const char kernel_prefix[] = "kernel:"; + const size_t kernel_prefix_len = sizeof(kernel_prefix) - 1; + + /* + * If the passed string is of the form "kernel:syscall*", move the + * pointer passed "kernel:". + */ + if (strncmp(str, kernel_prefix, kernel_prefix_len) == 0) { + str = &str[kernel_prefix_len]; + } + + if (strcmp(str, "syscall") == 0 || + strcmp(str, "syscall:entry+exit") == 0) { + *type = LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY_EXIT; + } else if (strcmp(str, "syscall:entry") == 0) { + *type = LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY; + } else if (strcmp(str, "syscall:exit") == 0) { + *type = LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_EXIT; + } else { + goto error; + } + + ret = true; + +error: + return ret; +} + +/* + * Parse `str` as a log level against the passed event rule type. + * + * Return the log level in `*log_level`. Return true in `*log_level_only` if + * the string specifies exactly this log level, false if it specifies at least + * this log level. + * + * Return true if the string was successfully parsed as a log level string. + */ +static bool parse_log_level_string(const char *str, + enum lttng_event_rule_type event_rule_type, + int *log_level, + bool *log_level_only) +{ + bool ret; + + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: + { + enum lttng_loglevel log_level_min, log_level_max; + if (!loglevel_parse_range_string( + str, &log_level_min, &log_level_max)) { + goto error; + } + + /* Only support VAL and VAL.. for now. */ + if (log_level_min != log_level_max && + log_level_max != LTTNG_LOGLEVEL_EMERG) { + goto error; + } + + *log_level = (int) log_level_min; + *log_level_only = log_level_min == log_level_max; + break; + } + case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: + { + enum lttng_loglevel_log4j log_level_min, log_level_max; + if (!loglevel_log4j_parse_range_string( + str, &log_level_min, &log_level_max)) { + goto error; + } + + /* Only support VAL and VAL.. for now. */ + if (log_level_min != log_level_max && + log_level_max != LTTNG_LOGLEVEL_LOG4J_FATAL) { + goto error; + } + + *log_level = (int) log_level_min; + *log_level_only = log_level_min == log_level_max; + break; + } + case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: + { + enum lttng_loglevel_jul log_level_min, log_level_max; + if (!loglevel_jul_parse_range_string( + str, &log_level_min, &log_level_max)) { + goto error; + } + + /* Only support VAL and VAL.. for now. */ + if (log_level_min != log_level_max && + log_level_max != LTTNG_LOGLEVEL_JUL_SEVERE) { + goto error; + } + + *log_level = (int) log_level_min; + *log_level_only = log_level_min == log_level_max; + break; + } + case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: + { + enum lttng_loglevel_python log_level_min, log_level_max; + if (!loglevel_python_parse_range_string( + str, &log_level_min, &log_level_max)) { + goto error; + } + + /* Only support VAL and VAL.. for now. */ + if (log_level_min != log_level_max && + log_level_max != + LTTNG_LOGLEVEL_PYTHON_CRITICAL) { + goto error; + } + + *log_level = (int) log_level_min; + *log_level_only = log_level_min == log_level_max; + break; + } + default: + /* Invalid domain type. */ + abort(); + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +static int parse_kernel_probe_opts(const char *source, + struct lttng_kernel_probe_location **location) +{ + int ret = 0; + int match; + char s_hex[19]; + char name[LTTNG_SYMBOL_NAME_LEN]; + char *symbol_name = NULL; + uint64_t offset; + + /* Check for symbol+offset. */ + match = sscanf(source, + "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API + "[^'+']+%18s", + name, s_hex); + if (match == 2) { + if (*s_hex == '\0') { + ERR("Kernel probe symbol offset is missing."); + goto error; + } + + symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN); + if (!symbol_name) { + PERROR("Failed to copy kernel probe location symbol name."); + goto error; + } + offset = strtoul(s_hex, NULL, 0); + + *location = lttng_kernel_probe_location_symbol_create( + symbol_name, offset); + if (!*location) { + ERR("Failed to create symbol kernel probe location."); + goto error; + } + + goto end; + } + + /* Check for symbol. */ + if (isalpha(name[0]) || name[0] == '_') { + match = sscanf(source, + "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API + "s", + name); + if (match == 1) { + symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN); + if (!symbol_name) { + ERR("Failed to copy kernel probe location symbol name."); + goto error; + } + + *location = lttng_kernel_probe_location_symbol_create( + symbol_name, 0); + if (!*location) { + ERR("Failed to create symbol kernel probe location."); + goto error; + } + + goto end; + } + } + + /* Check for address. */ + match = sscanf(source, "%18s", s_hex); + if (match > 0) { + uint64_t address; + + if (*s_hex == '\0') { + ERR("Invalid kernel probe location address."); + goto error; + } + + address = strtoul(s_hex, NULL, 0); + *location = lttng_kernel_probe_location_address_create(address); + if (!*location) { + ERR("Failed to create symbol kernel probe location."); + goto error; + } + + goto end; + } + +error: + /* No match */ + ret = -1; + *location = NULL; + +end: + free(symbol_name); + return ret; +} + +static +struct lttng_event_expr *ir_op_load_expr_to_event_expr( + const struct ir_load_expression *load_expr, + const char *capture_str) +{ + char *provider_name = NULL; + struct lttng_event_expr *event_expr = NULL; + const struct ir_load_expression_op *load_expr_op = load_expr->child; + const enum ir_load_expression_type load_expr_child_type = + load_expr_op->type; + + switch (load_expr_child_type) { + case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT: + case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT: + { + const char *field_name; + + load_expr_op = load_expr_op->next; + LTTNG_ASSERT(load_expr_op); + LTTNG_ASSERT(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL); + field_name = load_expr_op->u.symbol; + LTTNG_ASSERT(field_name); + + event_expr = load_expr_child_type == IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT ? + lttng_event_expr_event_payload_field_create(field_name) : + lttng_event_expr_channel_context_field_create(field_name); + if (!event_expr) { + ERR("Failed to create %s event expression: field name = `%s`.", + load_expr_child_type == IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT ? + "payload field" : "channel context", + field_name); + goto error; + } + + break; + } + case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT: + { + const char *colon; + const char *type_name; + const char *field_name; + + load_expr_op = load_expr_op->next; + LTTNG_ASSERT(load_expr_op); + LTTNG_ASSERT(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL); + field_name = load_expr_op->u.symbol; + LTTNG_ASSERT(field_name); + + /* + * The field name needs to be of the form PROVIDER:TYPE. We + * split it here. + */ + colon = strchr(field_name, ':'); + if (!colon) { + ERR("Invalid app-specific context field name: missing colon in `%s`.", + field_name); + goto error; + } + + type_name = colon + 1; + if (*type_name == '\0') { + ERR("Invalid app-specific context field name: missing type name after colon in `%s`.", + field_name); + goto error; + } + + provider_name = strndup(field_name, colon - field_name); + if (!provider_name) { + PERROR("Failed to allocate field name string"); + goto error; + } + + event_expr = lttng_event_expr_app_specific_context_field_create( + provider_name, type_name); + if (!event_expr) { + ERR("Failed to create app-specific context field event expression: provider name = `%s`, type name = `%s`", + provider_name, type_name); + goto error; + } + + break; + } + default: + ERR("%s: unexpected load expr type %d.", __func__, + load_expr_op->type); + abort(); + } + + load_expr_op = load_expr_op->next; + + /* There may be a single array index after that. */ + if (load_expr_op->type == IR_LOAD_EXPRESSION_GET_INDEX) { + struct lttng_event_expr *index_event_expr; + const uint64_t index = load_expr_op->u.index; + + index_event_expr = lttng_event_expr_array_field_element_create(event_expr, index); + if (!index_event_expr) { + ERR("Failed to create array field element event expression."); + goto error; + } + + event_expr = index_event_expr; + load_expr_op = load_expr_op->next; + } + + switch (load_expr_op->type) { + case IR_LOAD_EXPRESSION_LOAD_FIELD: + /* + * This is what we expect, IR_LOAD_EXPRESSION_LOAD_FIELD is + * always found at the end of the chain. + */ + break; + case IR_LOAD_EXPRESSION_GET_SYMBOL: + ERR("While parsing expression `%s`: Capturing subfields is not supported.", + capture_str); + goto error; + + default: + ERR("%s: unexpected load expression operator %s.", __func__, + ir_load_expression_type_str(load_expr_op->type)); + abort(); + } + + goto end; + +error: + lttng_event_expr_destroy(event_expr); + event_expr = NULL; + +end: + free(provider_name); + + return event_expr; +} + +static +struct lttng_event_expr *ir_op_load_to_event_expr( + const struct ir_op *ir, const char *capture_str) +{ + struct lttng_event_expr *event_expr = NULL; + + LTTNG_ASSERT(ir->op == IR_OP_LOAD); + + switch (ir->data_type) { + case IR_DATA_EXPRESSION: + { + const struct ir_load_expression *ir_load_expr = + ir->u.load.u.expression; + + event_expr = ir_op_load_expr_to_event_expr( + ir_load_expr, capture_str); + break; + } + default: + ERR("%s: unexpected data type: %s.", __func__, + ir_data_type_str(ir->data_type)); + abort(); + } + + return event_expr; +} + +static +const char *ir_operator_type_human_str(enum ir_op_type op) +{ + const char *name; + + switch (op) { + case IR_OP_BINARY: + name = "Binary"; + break; + case IR_OP_UNARY: + name = "Unary"; + break; + case IR_OP_LOGICAL: + name = "Logical"; + break; + default: + abort(); + } + + return name; +} + +static +struct lttng_event_expr *ir_op_root_to_event_expr(const struct ir_op *ir, + const char *capture_str) +{ + struct lttng_event_expr *event_expr = NULL; + + LTTNG_ASSERT(ir->op == IR_OP_ROOT); + ir = ir->u.root.child; + + switch (ir->op) { + case IR_OP_LOAD: + event_expr = ir_op_load_to_event_expr(ir, capture_str); + break; + case IR_OP_BINARY: + case IR_OP_UNARY: + case IR_OP_LOGICAL: + ERR("While parsing expression `%s`: %s operators are not allowed in capture expressions.", + capture_str, + ir_operator_type_human_str(ir->op)); + break; + default: + ERR("%s: unexpected IR op type: %s.", __func__, + ir_op_type_str(ir->op)); + abort(); + } + + return event_expr; +} + +static +void destroy_event_expr(void *ptr) +{ + lttng_event_expr_destroy((lttng_event_expr *) ptr); +} + +struct parse_event_rule_res { + /* Owned by this. */ + struct lttng_event_rule *er; + + /* Array of `struct lttng_event_expr *` */ + struct lttng_dynamic_pointer_array capture_descriptors; +}; + +static +struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) +{ + enum lttng_event_rule_type event_rule_type = + LTTNG_EVENT_RULE_TYPE_UNKNOWN; + struct argpar_state *state; + struct argpar_item *item = NULL; + char *error = NULL; + int consumed_args = -1; + struct lttng_kernel_probe_location *kernel_probe_location = NULL; + struct lttng_userspace_probe_location *userspace_probe_location = NULL; + struct parse_event_rule_res res = { 0 }; + struct lttng_event_expr *event_expr = NULL; + struct filter_parser_ctx *parser_ctx = NULL; + struct lttng_log_level_rule *log_level_rule = NULL; + + /* Event rule type option */ + char *event_rule_type_str = NULL; + + /* Tracepoint and syscall options. */ + char *name = NULL; + /* Array of strings. */ + struct lttng_dynamic_pointer_array exclude_names; + + /* For userspace / kernel probe and function. */ + char *location = NULL; + char *event_name = NULL; + + /* Filter. */ + char *filter = NULL; + + /* Log level. */ + char *log_level_str = NULL; + + lttng_dynamic_pointer_array_init(&res.capture_descriptors, + destroy_event_expr); + + lttng_dynamic_pointer_array_init(&exclude_names, free); + + state = argpar_state_create(*argc, *argv, event_rule_opt_descrs); + if (!state) { + ERR("Failed to allocate an argpar state."); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + ERR("%s", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + LTTNG_ASSERT(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + const struct argpar_item_opt *item_opt = + (const struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_TYPE: + if (!assign_event_rule_type(&event_rule_type, + item_opt->arg)) { + goto error; + } + + /* Save the string for later use. */ + if (!assign_string(&event_rule_type_str, + item_opt->arg, + "--type/-t")) { + goto error; + } + + break; + case OPT_LOCATION: + if (!assign_string(&location, + item_opt->arg, + "--location/-L")) { + goto error; + } + + break; + case OPT_EVENT_NAME: + if (!assign_string(&event_name, + item_opt->arg, + "--event-name/-E")) { + goto error; + } + + break; + case OPT_FILTER: + if (!assign_string(&filter, item_opt->arg, + "--filter/-f")) { + goto error; + } + + break; + case OPT_NAME: + if (!assign_string(&name, item_opt->arg, + "--name/-n")) { + goto error; + } + + break; + case OPT_EXCLUDE_NAME: + { + int ret; + + ret = lttng_dynamic_pointer_array_add_pointer( + &exclude_names, + strdup(item_opt->arg)); + if (ret != 0) { + ERR("Failed to add pointer to dynamic pointer array."); + goto error; + } + + break; + } + case OPT_LOG_LEVEL: + if (!assign_string(&log_level_str, + item_opt->arg, "--log-level/-l")) { + goto error; + } + + break; + case OPT_CAPTURE: + { + int ret; + const char *capture_str = item_opt->arg; + + ret = filter_parser_ctx_create_from_filter_expression( + capture_str, &parser_ctx); + if (ret) { + ERR("Failed to parse capture expression `%s`.", + capture_str); + goto error; + } + + event_expr = ir_op_root_to_event_expr( + parser_ctx->ir_root, + capture_str); + if (!event_expr) { + /* + * ir_op_root_to_event_expr has printed + * an error message. + */ + goto error; + } + + ret = lttng_dynamic_pointer_array_add_pointer( + &res.capture_descriptors, + event_expr); + if (ret) { + goto error; + } + + /* + * The ownership of event expression was + * transferred to the dynamic array. + */ + event_expr = NULL; + + break; + } + default: + abort(); + } + } else { + const struct argpar_item_non_opt *item_non_opt = + (const struct argpar_item_non_opt *) + item; + + /* Don't accept non-option arguments. */ + ERR("Unexpected argument '%s'", item_non_opt->arg); + goto error; + } + } + + if (event_rule_type == LTTNG_EVENT_RULE_TYPE_UNKNOWN) { + ERR("Event rule requires a --type."); + goto error; + } + + /* + * Option --name is applicable to event rules of type kernel, user, jul, + * log4j,python and syscall. If --name is omitted, it is implicitly + * "*". + */ + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: + case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: + case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: + case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: + case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: + case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: + if (!name) { + name = strdup("*"); + } + break; + + default: + if (name) { + ERR("Can't use --name with %s event rules.", + lttng_event_rule_type_str( + event_rule_type)); + goto error; + } + + if (lttng_dynamic_pointer_array_get_count(&exclude_names) > 0) { + ERR("Can't use --exclude-name/-x with %s event rules.", + lttng_event_rule_type_str( + event_rule_type)); + goto error; + } + } + + /* + * Option --location is only applicable to (and mandatory for) event + * rules of type {k,u}probe and function. + * + * Option --event-name is only applicable to event rules of type probe. + * If omitted, it defaults to the location. + */ + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE: + if (!location) { + ERR("Event rule of type %s requires a --location.", + lttng_event_rule_type_str(event_rule_type)); + goto error; + } + + if (!event_name) { + event_name = strdup(location); + } + + break; + + default: + if (location) { + ERR("Can't use --location with %s event rules.", + lttng_event_rule_type_str(event_rule_type)); + goto error; + } + + if (event_name) { + ERR("Can't use --event-name with %s event rules.", + lttng_event_rule_type_str( + event_rule_type)); + goto error; + } + } + + /* + * Update *argc and *argv so our caller can keep parsing what follows. + */ + consumed_args = argpar_state_get_ingested_orig_args(state); + LTTNG_ASSERT(consumed_args >= 0); + *argc -= consumed_args; + *argv += consumed_args; + + /* + * Adding a filter to a probe, function or userspace-probe would be + * denied by the kernel tracer as it's not supported at the moment. We + * do an early check here to warn the user. + */ + if (filter) { + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: + case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: + case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: + case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: + case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: + case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: + break; + default: + ERR("Filter expressions are not supported for %s event rules.", + lttng_event_rule_type_str(event_rule_type)); + goto error; + } + } + + /* + * If --exclude-name/-x was passed, split it into an exclusion list. + * Exclusions are only supported by + * LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT for now. + */ + if (lttng_dynamic_pointer_array_get_count(&exclude_names) > 0) { + if (event_rule_type != LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT) { + ERR("Event name exclusions are not yet implemented for %s event rules.", + lttng_event_rule_type_str(event_rule_type)); + goto error; + } + + if (validate_exclusion_list(name, &exclude_names) != 0) { + /* + * Assume validate_exclusion_list already prints an + * error message. + */ + goto error; + } + } + + if (log_level_str) { + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: + case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: + case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: + case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: + { + int log_level; + bool log_level_only; + + if (strcmp(log_level_str, "..") == 0) { + /* + * ".." is the same as passing no log level + * option and correspond to the "ANY" case. + */ + break; + } + + if (!parse_log_level_string(log_level_str, event_rule_type, + &log_level, &log_level_only)) { + ERR("Failed to parse log level string `%s`.", + log_level_str); + goto error; + } + + if (log_level_only) { + log_level_rule = lttng_log_level_rule_exactly_create(log_level); + } else { + log_level_rule = lttng_log_level_rule_at_least_as_severe_as_create(log_level); + } + + if (log_level_rule == NULL) { + ERR("Failed to create log level rule object."); + goto error; + } + break; + } + default: + ERR("Log levels are not supported for %s event rules.", + lttng_event_rule_type_str(event_rule_type)); + goto error; + } + } + + /* Finally, create the event rule object. */ + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: + { + enum lttng_event_rule_status event_rule_status; + + res.er = lttng_event_rule_user_tracepoint_create(); + if (!res.er) { + ERR("Failed to create user_tracepoint event rule."); + goto error; + } + + /* Set pattern. */ + event_rule_status = lttng_event_rule_user_tracepoint_set_name_pattern( + res.er, name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set user_tracepoint event rule's pattern to '%s'.", + name); + goto error; + } + + /* Set filter. */ + if (filter) { + event_rule_status = lttng_event_rule_user_tracepoint_set_filter( + res.er, filter); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set user_tracepoint event rule's filter to '%s'.", + filter); + goto error; + } + } + + /* Set exclusion list. */ + if (lttng_dynamic_pointer_array_get_count(&exclude_names) > 0) { + int n; + int count = lttng_dynamic_pointer_array_get_count( + &exclude_names); + + for (n = 0; n < count; n++) { + const char *exclude_name = + (const char *) lttng_dynamic_pointer_array_get_pointer( + &exclude_names, + n); + + event_rule_status = + lttng_event_rule_user_tracepoint_add_name_pattern_exclusion( + res.er, + exclude_name); + if (event_rule_status != + LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set user_tracepoint exclusion list element '%s'", + exclude_name); + goto error; + } + } + } + + if (log_level_rule) { + event_rule_status = + lttng_event_rule_user_tracepoint_set_log_level_rule( + res.er, log_level_rule); + + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set log level on event fule."); + goto error; + } + } + + break; + } + case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: + { + enum lttng_event_rule_status event_rule_status; + + res.er = lttng_event_rule_kernel_tracepoint_create(); + if (!res.er) { + ERR("Failed to create kernel_tracepoint event rule."); + goto error; + } + + /* Set pattern. */ + event_rule_status = lttng_event_rule_kernel_tracepoint_set_name_pattern( + res.er, name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set kernel_tracepoint event rule's pattern to '%s'.", + name); + goto error; + } + + /* Set filter. */ + if (filter) { + event_rule_status = lttng_event_rule_kernel_tracepoint_set_filter( + res.er, filter); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set kernel_tracepoint event rule's filter to '%s'.", + filter); + goto error; + } + } + break; + } + case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: + { + enum lttng_event_rule_status event_rule_status; + + res.er = lttng_event_rule_jul_logging_create(); + if (!res.er) { + ERR("Failed to create jul_logging event rule."); + goto error; + } + + /* Set pattern. */ + event_rule_status = lttng_event_rule_jul_logging_set_name_pattern( + res.er, name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set jul_logging event rule's pattern to '%s'.", + name); + goto error; + } + + /* Set filter. */ + if (filter) { + event_rule_status = lttng_event_rule_jul_logging_set_filter( + res.er, filter); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set jul_logging event rule's filter to '%s'.", + filter); + goto error; + } + } + + if (log_level_rule) { + event_rule_status = + lttng_event_rule_jul_logging_set_log_level_rule( + res.er, log_level_rule); + + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set log level on event fule."); + goto error; + } + } + break; + } + case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: + { + enum lttng_event_rule_status event_rule_status; + + res.er = lttng_event_rule_log4j_logging_create(); + if (!res.er) { + ERR("Failed to create jul_logging event rule."); + goto error; + } + + /* Set pattern. */ + event_rule_status = lttng_event_rule_log4j_logging_set_name_pattern( + res.er, name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set jul_logging event rule's pattern to '%s'.", + name); + goto error; + } + + /* Set filter. */ + if (filter) { + event_rule_status = lttng_event_rule_log4j_logging_set_filter( + res.er, filter); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set jul_logging event rule's filter to '%s'.", + filter); + goto error; + } + } + + if (log_level_rule) { + event_rule_status = + lttng_event_rule_log4j_logging_set_log_level_rule( + res.er, log_level_rule); + + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set log level on event fule."); + goto error; + } + } + break; + } + case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: + { + enum lttng_event_rule_status event_rule_status; + + res.er = lttng_event_rule_python_logging_create(); + if (!res.er) { + ERR("Failed to create jul_logging event rule."); + goto error; + } + + /* Set pattern. */ + event_rule_status = lttng_event_rule_python_logging_set_name_pattern( + res.er, name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set jul_logging event rule's pattern to '%s'.", + name); + goto error; + } + + /* Set filter. */ + if (filter) { + event_rule_status = lttng_event_rule_python_logging_set_filter( + res.er, filter); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set jul_logging event rule's filter to '%s'.", + filter); + goto error; + } + } + + if (log_level_rule) { + event_rule_status = + lttng_event_rule_python_logging_set_log_level_rule( + res.er, log_level_rule); + + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set log level on event fule."); + goto error; + } + } + break; + } + case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE: + { + int ret; + enum lttng_event_rule_status event_rule_status; + + ret = parse_kernel_probe_opts( + location, &kernel_probe_location); + if (ret) { + ERR("Failed to parse kernel probe location."); + goto error; + } + + LTTNG_ASSERT(kernel_probe_location); + res.er = lttng_event_rule_kernel_kprobe_create(kernel_probe_location); + if (!res.er) { + ERR("Failed to create kprobe event rule."); + goto error; + } + + event_rule_status = + lttng_event_rule_kernel_kprobe_set_event_name( + res.er, event_name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set kprobe event rule's name to '%s'.", + event_name); + goto error; + } + + break; + } + case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE: + { + int ret; + enum lttng_event_rule_status event_rule_status; + + ret = parse_userspace_probe_opts( + location, &userspace_probe_location); + if (ret) { + ERR("Failed to parse user space probe location."); + goto error; + } + + res.er = lttng_event_rule_kernel_uprobe_create(userspace_probe_location); + if (!res.er) { + ERR("Failed to create userspace probe event rule."); + goto error; + } + + event_rule_status = + lttng_event_rule_kernel_uprobe_set_event_name( + res.er, event_name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set user space probe event rule's name to '%s'.", + event_name); + goto error; + } + + break; + } + case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: + { + enum lttng_event_rule_status event_rule_status; + enum lttng_event_rule_kernel_syscall_emission_site emission_site; + + if (!parse_syscall_emission_site_from_type( + event_rule_type_str, &emission_site)) { + ERR("Failed to parse syscall type '%s'.", event_rule_type_str); + goto error; + } + + res.er = lttng_event_rule_kernel_syscall_create(emission_site); + if (!res.er) { + ERR("Failed to create syscall event rule."); + goto error; + } + + event_rule_status = lttng_event_rule_kernel_syscall_set_name_pattern( + res.er, name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set syscall event rule's pattern to '%s'.", + name); + goto error; + } + + if (filter) { + event_rule_status = lttng_event_rule_kernel_syscall_set_filter( + res.er, filter); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set syscall event rule's filter to '%s'.", + filter); + goto error; + } + } + + break; + } + default: + abort(); + goto error; + } + + goto end; + +error: + lttng_event_rule_destroy(res.er); + res.er = NULL; + lttng_dynamic_pointer_array_reset(&res.capture_descriptors); + +end: + if (parser_ctx) { + filter_parser_ctx_free(parser_ctx); + } + + lttng_event_expr_destroy(event_expr); + argpar_item_destroy(item); + free(error); + argpar_state_destroy(state); + free(filter); + free(name); + lttng_dynamic_pointer_array_reset(&exclude_names); + free(log_level_str); + free(location); + free(event_name); + free(event_rule_type_str); + + lttng_kernel_probe_location_destroy(kernel_probe_location); + lttng_userspace_probe_location_destroy(userspace_probe_location); + lttng_log_level_rule_destroy(log_level_rule); + return res; +} + +static +struct lttng_condition *handle_condition_event(int *argc, const char ***argv) +{ + struct parse_event_rule_res res; + struct lttng_condition *c; + size_t i; + + res = parse_event_rule(argc, argv); + if (!res.er) { + c = NULL; + goto error; + } + + c = lttng_condition_event_rule_matches_create(res.er); + lttng_event_rule_destroy(res.er); + res.er = NULL; + if (!c) { + goto error; + } + + for (i = 0; i < lttng_dynamic_pointer_array_get_count(&res.capture_descriptors); + i++) { + enum lttng_condition_status status; + struct lttng_event_expr **expr = + (lttng_event_expr **) lttng_dynamic_array_get_element( + &res.capture_descriptors.array, i); + + LTTNG_ASSERT(expr); + LTTNG_ASSERT(*expr); + status = lttng_condition_event_rule_matches_append_capture_descriptor( + c, *expr); + if (status != LTTNG_CONDITION_STATUS_OK) { + if (status == LTTNG_CONDITION_STATUS_UNSUPPORTED) { + ERR("The capture feature is unsupported by the event-rule condition type"); + } + + goto error; + } + + /* Ownership of event expression moved to `c` */ + *expr = NULL; + } + + goto end; + +error: + lttng_condition_destroy(c); + c = NULL; + +end: + lttng_dynamic_pointer_array_reset(&res.capture_descriptors); + lttng_event_rule_destroy(res.er); + return c; +} + +struct condition_descr { + const char *name; + struct lttng_condition *(*handler) (int *argc, const char ***argv); +}; + +static const +struct condition_descr condition_descrs[] = { + { "event-rule-matches", handle_condition_event }, +}; + +static +struct lttng_condition *parse_condition(const char *condition_name, int *argc, + const char ***argv) +{ + int i; + struct lttng_condition *cond; + const struct condition_descr *descr = NULL; + + for (i = 0; i < ARRAY_SIZE(condition_descrs); i++) { + if (strcmp(condition_name, condition_descrs[i].name) == 0) { + descr = &condition_descrs[i]; + break; + } + } + + if (!descr) { + ERR("Unknown condition name '%s'", condition_name); + goto error; + } + + cond = descr->handler(argc, argv); + if (!cond) { + /* The handler has already printed an error message. */ + goto error; + } + + goto end; +error: + cond = NULL; +end: + return cond; +} + +static struct lttng_rate_policy *parse_rate_policy(const char *policy_str) +{ + int ret; + size_t num_token = 0; + struct lttng_dynamic_pointer_array tokens; + struct lttng_rate_policy *policy = NULL; + enum lttng_rate_policy_type policy_type; + unsigned long long value; + char *policy_type_str; + char *policy_value_str; + + LTTNG_ASSERT(policy_str); + lttng_dynamic_pointer_array_init(&tokens, NULL); + + /* Rate policy fields are separated by ':'. */ + ret = strutils_split(policy_str, ':', 1, &tokens); + if (ret == 0) { + num_token = lttng_dynamic_pointer_array_get_count(&tokens); + } + + /* + * Early sanity check that the number of parameter is exactly 2. + * i.e : type:value + */ + if (num_token != 2) { + ERR("Rate policy format is invalid."); + goto end; + } + + policy_type_str = (char *) lttng_dynamic_pointer_array_get_pointer(&tokens, 0); + policy_value_str = (char *) lttng_dynamic_pointer_array_get_pointer(&tokens, 1); + + /* Parse the type. */ + if (strcmp(policy_type_str, "once-after") == 0) { + policy_type = LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N; + } else if (strcmp(policy_type_str, "every") == 0) { + policy_type = LTTNG_RATE_POLICY_TYPE_EVERY_N; + } else { + ERR("Rate policy type `%s` unknown.", policy_type_str); + goto end; + } + + /* Parse the value. */ + if (utils_parse_unsigned_long_long(policy_value_str, &value) != 0) { + ERR("Failed to parse rate policy value `%s` as an integer.", + policy_value_str); + goto end; + } + + if (value == 0) { + ERR("Rate policy value `%s` must be > 0.", policy_value_str); + goto end; + } + + switch (policy_type) { + case LTTNG_RATE_POLICY_TYPE_EVERY_N: + policy = lttng_rate_policy_every_n_create(value); + break; + case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N: + policy = lttng_rate_policy_once_after_n_create(value); + break; + default: + abort(); + } + + if (policy == NULL) { + ERR("Failed to create rate policy `%s`.", policy_str); + } + +end: + lttng_dynamic_pointer_array_reset(&tokens); + return policy; +} + +static const struct argpar_opt_descr notify_action_opt_descrs[] = { + { OPT_RATE_POLICY, '\0', "rate-policy", true }, + ARGPAR_OPT_DESCR_SENTINEL +}; + +static +struct lttng_action *handle_action_notify(int *argc, const char ***argv) +{ + struct lttng_action *action = NULL; + struct argpar_state *state = NULL; + struct argpar_item *item = NULL; + char *error = NULL; + struct lttng_rate_policy *policy = NULL; + + state = argpar_state_create(*argc, *argv, notify_action_opt_descrs); + if (!state) { + ERR("Failed to allocate an argpar state."); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + ERR("%s", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + LTTNG_ASSERT(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + const struct argpar_item_opt *item_opt = + (const struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_RATE_POLICY: + { + policy = parse_rate_policy(item_opt->arg); + if (!policy) { + goto error; + } + break; + } + default: + abort(); + } + } else { + const struct argpar_item_non_opt *item_non_opt; + + LTTNG_ASSERT(item->type == ARGPAR_ITEM_TYPE_NON_OPT); + + item_non_opt = (const struct argpar_item_non_opt *) item; + + switch (item_non_opt->non_opt_index) { + default: + ERR("Unexpected argument `%s`.", + item_non_opt->arg); + goto error; + } + } + } + + *argc -= argpar_state_get_ingested_orig_args(state); + *argv += argpar_state_get_ingested_orig_args(state); + + action = lttng_action_notify_create(); + if (!action) { + ERR("Failed to create notify action"); + goto error; + } + + if (policy) { + enum lttng_action_status status; + status = lttng_action_notify_set_rate_policy(action, policy); + if (status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to set rate policy"); + goto error; + } + } + + goto end; + +error: + lttng_action_destroy(action); + action = NULL; +end: + free(error); + lttng_rate_policy_destroy(policy); + argpar_state_destroy(state); + argpar_item_destroy(item); + return action; +} + +/* + * Generic handler for a kind of action that takes a session name and an + * optional rate policy. + */ + +static struct lttng_action *handle_action_simple_session_with_policy(int *argc, + const char ***argv, + struct lttng_action *(*create_action_cb)(void), + enum lttng_action_status (*set_session_name_cb)( + struct lttng_action *, const char *), + enum lttng_action_status (*set_rate_policy_cb)( + struct lttng_action *, + const struct lttng_rate_policy *), + const char *action_name) +{ + struct lttng_action *action = NULL; + struct argpar_state *state = NULL; + struct argpar_item *item = NULL; + const char *session_name_arg = NULL; + char *error = NULL; + enum lttng_action_status action_status; + struct lttng_rate_policy *policy = NULL; + + LTTNG_ASSERT(set_session_name_cb); + LTTNG_ASSERT(set_rate_policy_cb); + + const struct argpar_opt_descr rate_policy_opt_descrs[] = { + { OPT_RATE_POLICY, '\0', "rate-policy", true }, + ARGPAR_OPT_DESCR_SENTINEL + }; + + state = argpar_state_create(*argc, *argv, rate_policy_opt_descrs); + if (!state) { + ERR("Failed to allocate an argpar state."); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + ERR("%s", error); + goto error; + } else if (status == + ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + LTTNG_ASSERT(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + const struct argpar_item_opt *item_opt = + (const struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_RATE_POLICY: + { + policy = parse_rate_policy(item_opt->arg); + if (!policy) { + goto error; + } + break; + } + default: + abort(); + } + } else { + const struct argpar_item_non_opt *item_non_opt; + item_non_opt = (const struct argpar_item_non_opt *) item; + + switch (item_non_opt->non_opt_index) { + case 0: + session_name_arg = item_non_opt->arg; + break; + default: + ERR("Unexpected argument `%s`.", + item_non_opt->arg); + goto error; + } + } + } + + *argc -= argpar_state_get_ingested_orig_args(state); + *argv += argpar_state_get_ingested_orig_args(state); + + if (!session_name_arg) { + ERR("Missing session name."); + goto error; + } + + action = create_action_cb(); + if (!action) { + ERR("Failed to allocate %s session action.", action_name); + goto error; + } + + action_status = set_session_name_cb(action, session_name_arg); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to set action %s session's session name to '%s'.", + action_name, session_name_arg); + goto error; + } + + if (policy) { + action_status = set_rate_policy_cb(action, policy); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to set rate policy"); + goto error; + } + } + + goto end; + +error: + lttng_action_destroy(action); + action = NULL; + argpar_item_destroy(item); +end: + lttng_rate_policy_destroy(policy); + free(error); + argpar_state_destroy(state); + return action; +} + +static +struct lttng_action *handle_action_start_session(int *argc, + const char ***argv) +{ + return handle_action_simple_session_with_policy(argc, argv, + lttng_action_start_session_create, + lttng_action_start_session_set_session_name, + lttng_action_start_session_set_rate_policy, "start"); +} + +static +struct lttng_action *handle_action_stop_session(int *argc, + const char ***argv) +{ + return handle_action_simple_session_with_policy(argc, argv, + lttng_action_stop_session_create, + lttng_action_stop_session_set_session_name, + lttng_action_stop_session_set_rate_policy, "stop"); +} + +static +struct lttng_action *handle_action_rotate_session(int *argc, + const char ***argv) +{ + return handle_action_simple_session_with_policy(argc, argv, + lttng_action_rotate_session_create, + lttng_action_rotate_session_set_session_name, + lttng_action_rotate_session_set_rate_policy, + "rotate"); +} + +static const struct argpar_opt_descr snapshot_action_opt_descrs[] = { + { OPT_NAME, 'n', "name", true }, + { OPT_MAX_SIZE, 'm', "max-size", true }, + { OPT_CTRL_URL, '\0', "ctrl-url", true }, + { OPT_DATA_URL, '\0', "data-url", true }, + { OPT_URL, '\0', "url", true }, + { OPT_PATH, '\0', "path", true }, + { OPT_RATE_POLICY, '\0', "rate-policy", true }, + ARGPAR_OPT_DESCR_SENTINEL +}; + +static +struct lttng_action *handle_action_snapshot_session(int *argc, + const char ***argv) +{ + struct lttng_action *action = NULL; + struct argpar_state *state = NULL; + struct argpar_item *item = NULL; + const char *session_name_arg = NULL; + char *snapshot_name_arg = NULL; + char *ctrl_url_arg = NULL; + char *data_url_arg = NULL; + char *max_size_arg = NULL; + char *url_arg = NULL; + char *path_arg = NULL; + char *error = NULL; + enum lttng_action_status action_status; + struct lttng_snapshot_output *snapshot_output = NULL; + struct lttng_rate_policy *policy = NULL; + int ret; + unsigned int locations_specified = 0; + + state = argpar_state_create(*argc, *argv, snapshot_action_opt_descrs); + if (!state) { + ERR("Failed to allocate an argpar state."); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + ERR("%s", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + LTTNG_ASSERT(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + const struct argpar_item_opt *item_opt = + (const struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_NAME: + if (!assign_string(&snapshot_name_arg, item_opt->arg, "--name/-n")) { + goto error; + } + + break; + case OPT_MAX_SIZE: + if (!assign_string(&max_size_arg, item_opt->arg, "--max-size/-m")) { + goto error; + } + + break; + case OPT_CTRL_URL: + if (!assign_string(&ctrl_url_arg, item_opt->arg, "--ctrl-url")) { + goto error; + } + + break; + case OPT_DATA_URL: + if (!assign_string(&data_url_arg, item_opt->arg, "--data-url")) { + goto error; + } + + break; + case OPT_URL: + if (!assign_string(&url_arg, item_opt->arg, "--url")) { + goto error; + } + + break; + case OPT_PATH: + if (!assign_string(&path_arg, item_opt->arg, "--path")) { + goto error; + } + + break; + case OPT_RATE_POLICY: + { + policy = parse_rate_policy(item_opt->arg); + if (!policy) { + goto error; + } + break; + } + default: + abort(); + } + } else { + const struct argpar_item_non_opt *item_non_opt; + + LTTNG_ASSERT(item->type == ARGPAR_ITEM_TYPE_NON_OPT); + + item_non_opt = (const struct argpar_item_non_opt *) item; + + switch (item_non_opt->non_opt_index) { + case 0: + session_name_arg = item_non_opt->arg; + break; + default: + ERR("Unexpected argument `%s`.", + item_non_opt->arg); + goto error; + } + } + } + + *argc -= argpar_state_get_ingested_orig_args(state); + *argv += argpar_state_get_ingested_orig_args(state); + + if (!session_name_arg) { + ERR("Missing session name."); + goto error; + } + + /* --ctrl-url and --data-url must come in pair. */ + if (ctrl_url_arg && !data_url_arg) { + ERR("--ctrl-url is specified, but --data-url is missing."); + goto error; + } + + if (!ctrl_url_arg && data_url_arg) { + ERR("--data-url is specified, but --ctrl-url is missing."); + goto error; + } + + locations_specified += !!(ctrl_url_arg || data_url_arg); + locations_specified += !!url_arg; + locations_specified += !!path_arg; + + /* --ctrl-url/--data-url, --url and --path are mutually exclusive. */ + if (locations_specified > 1) { + ERR("The --ctrl-url/--data-url, --url, and --path options can't be used together."); + goto error; + } + + /* + * Did the user specify an option that implies using a + * custom/unregistered output? + */ + if (url_arg || ctrl_url_arg || path_arg) { + snapshot_output = lttng_snapshot_output_create(); + if (!snapshot_output) { + ERR("Failed to allocate a snapshot output."); + goto error; + } + } + + action = lttng_action_snapshot_session_create(); + if (!action) { + ERR("Failed to allocate snapshot session action."); + goto error; + } + + action_status = lttng_action_snapshot_session_set_session_name( + action, session_name_arg); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to set action snapshot session's session name to '%s'.", + session_name_arg); + goto error; + } + + if (snapshot_name_arg) { + if (!snapshot_output) { + ERR("Can't provide a snapshot output name without a snapshot output destination."); + goto error; + } + + ret = lttng_snapshot_output_set_name( + snapshot_name_arg, snapshot_output); + if (ret != 0) { + ERR("Failed to set name of snapshot output."); + goto error; + } + } + + if (max_size_arg) { + uint64_t max_size; + + if (!snapshot_output) { + ERR("Can't provide a snapshot output max size without a snapshot output destination."); + goto error; + } + + ret = utils_parse_size_suffix(max_size_arg, &max_size); + if (ret != 0) { + ERR("Failed to parse `%s` as a size.", max_size_arg); + goto error; + } + + ret = lttng_snapshot_output_set_size(max_size, snapshot_output); + if (ret != 0) { + ERR("Failed to set snapshot output's max size to %" PRIu64 " bytes.", + max_size); + goto error; + } + } + + if (url_arg) { + int num_uris; + struct lttng_uri *uris; + + if (!strstr(url_arg, "://")) { + ERR("Failed to parse '%s' as an URL.", url_arg); + goto error; + } + + num_uris = uri_parse_str_urls(url_arg, NULL, &uris); + if (num_uris < 1) { + ERR("Failed to parse '%s' as an URL.", url_arg); + goto error; + } + + if (uris[0].dtype == LTTNG_DST_PATH) { + ret = lttng_snapshot_output_set_local_path( + uris[0].dst.path, snapshot_output); + free(uris); + if (ret != 0) { + ERR("Failed to assign '%s' as a local destination.", + url_arg); + goto error; + } + } else { + ret = lttng_snapshot_output_set_network_url( + url_arg, snapshot_output); + free(uris); + if (ret != 0) { + ERR("Failed to assign '%s' as a network URL.", + url_arg); + goto error; + } + } + } + + if (path_arg) { + ret = lttng_snapshot_output_set_local_path( + path_arg, snapshot_output); + if (ret != 0) { + ERR("Failed to parse '%s' as a local path.", path_arg); + goto error; + } + } + + if (ctrl_url_arg) { + /* + * Two argument form, network output with separate control and + * data URLs. + */ + ret = lttng_snapshot_output_set_network_urls( + ctrl_url_arg, data_url_arg, snapshot_output); + if (ret != 0) { + ERR("Failed to parse `%s` and `%s` as control and data URLs.", + ctrl_url_arg, data_url_arg); + goto error; + } + } + + if (snapshot_output) { + action_status = lttng_action_snapshot_session_set_output( + action, snapshot_output); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to set snapshot session action's output."); + goto error; + } + + /* Ownership of `snapshot_output` has been transferred to the action. */ + snapshot_output = NULL; + } + + if (policy) { + enum lttng_action_status status; + status = lttng_action_snapshot_session_set_rate_policy( + action, policy); + if (status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to set rate policy"); + goto error; + } + } + + goto end; + +error: + lttng_action_destroy(action); + action = NULL; + free(error); +end: + free(snapshot_name_arg); + free(path_arg); + free(url_arg); + free(ctrl_url_arg); + free(data_url_arg); + free(snapshot_output); + free(max_size_arg); + lttng_rate_policy_destroy(policy); + argpar_state_destroy(state); + argpar_item_destroy(item); + return action; +} + +struct action_descr { + const char *name; + struct lttng_action *(*handler) (int *argc, const char ***argv); +}; + +static const +struct action_descr action_descrs[] = { + { "notify", handle_action_notify }, + { "start-session", handle_action_start_session }, + { "stop-session", handle_action_stop_session }, + { "rotate-session", handle_action_rotate_session }, + { "snapshot-session", handle_action_snapshot_session }, +}; + +static +struct lttng_action *parse_action(const char *action_name, int *argc, const char ***argv) +{ + int i; + struct lttng_action *action; + const struct action_descr *descr = NULL; + + for (i = 0; i < ARRAY_SIZE(action_descrs); i++) { + if (strcmp(action_name, action_descrs[i].name) == 0) { + descr = &action_descrs[i]; + break; + } + } + + if (!descr) { + ERR("Unknown action name: %s", action_name); + goto error; + } + + action = descr->handler(argc, argv); + if (!action) { + /* The handler has already printed an error message. */ + goto error; + } + + goto end; +error: + action = NULL; +end: + return action; +} + +static const +struct argpar_opt_descr add_trigger_options[] = { + { OPT_HELP, 'h', "help", false }, + { OPT_LIST_OPTIONS, '\0', "list-options", false }, + { OPT_CONDITION, '\0', "condition", true }, + { OPT_ACTION, '\0', "action", true }, + { OPT_NAME, '\0', "name", true }, + { OPT_OWNER_UID, '\0', "owner-uid", true }, + ARGPAR_OPT_DESCR_SENTINEL, +}; + +static +void lttng_actions_destructor(void *p) +{ + struct lttng_action *action = (lttng_action *) p; + + lttng_action_destroy(action); +} + +int cmd_add_trigger(int argc, const char **argv) +{ + int ret; + int my_argc = argc - 1; + const char **my_argv = argv + 1; + struct lttng_condition *condition = NULL; + struct lttng_dynamic_pointer_array actions; + struct argpar_state *argpar_state = NULL; + struct argpar_item *argpar_item = NULL; + struct lttng_action *action_list = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + char *error = NULL; + char *name = NULL; + int i; + char *owner_uid = NULL; + enum lttng_error_code ret_code; + struct mi_writer *mi_writer = NULL; + + lttng_dynamic_pointer_array_init(&actions, lttng_actions_destructor); + + if (lttng_opt_mi) { + mi_writer = mi_lttng_writer_create( + fileno(stdout), lttng_opt_mi); + if (!mi_writer) { + ret = CMD_ERROR; + goto error; + } + + /* Open command element. */ + ret = mi_lttng_writer_command_open(mi_writer, + mi_lttng_element_command_add_trigger); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* Open output element. */ + ret = mi_lttng_writer_open_element( + mi_writer, mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + + while (true) { + enum argpar_state_parse_next_status status; + const struct argpar_item_opt *item_opt; + int ingested_args; + + argpar_state_destroy(argpar_state); + argpar_state = argpar_state_create(my_argc, my_argv, + add_trigger_options); + if (!argpar_state) { + ERR("Failed to create argpar state."); + goto error; + } + + ARGPAR_ITEM_DESTROY_AND_RESET(argpar_item); + status = argpar_state_parse_next(argpar_state, &argpar_item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + ERR("%s", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + ERR("%s", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + LTTNG_ASSERT(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (argpar_item->type == ARGPAR_ITEM_TYPE_NON_OPT) { + const struct argpar_item_non_opt *item_non_opt = + (const struct argpar_item_non_opt *) + argpar_item; + + ERR("Unexpected argument `%s`.", item_non_opt->arg); + goto error; + } + + item_opt = (const struct argpar_item_opt *) argpar_item; + + ingested_args = argpar_state_get_ingested_orig_args( + argpar_state); + + my_argc -= ingested_args; + my_argv += ingested_args; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = 0; + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options_argpar(stdout, add_trigger_options); + ret = 0; + goto end; + case OPT_CONDITION: + { + if (condition) { + ERR("A --condition was already given."); + goto error; + } + + condition = parse_condition(item_opt->arg, &my_argc, &my_argv); + if (!condition) { + /* + * An error message was already printed by + * parse_condition. + */ + goto error; + } + + break; + } + case OPT_ACTION: + { + action = parse_action(item_opt->arg, &my_argc, &my_argv); + if (!action) { + /* + * An error message was already printed by + * parse_condition. + */ + goto error; + } + + ret = lttng_dynamic_pointer_array_add_pointer( + &actions, action); + if (ret) { + ERR("Failed to add pointer to pointer array."); + goto error; + } + + /* Ownership of the action was transferred to the list. */ + action = NULL; + + break; + } + case OPT_NAME: + { + if (!assign_string(&name, item_opt->arg, "--name")) { + goto error; + } + + break; + } + case OPT_OWNER_UID: + { + if (!assign_string(&owner_uid, item_opt->arg, + "--owner-uid")) { + goto error; + } + + break; + } + default: + abort(); + } + } + + if (!condition) { + ERR("Missing --condition."); + goto error; + } + + if (lttng_dynamic_pointer_array_get_count(&actions) == 0) { + ERR("Need at least one --action."); + goto error; + } + + action_list = lttng_action_list_create(); + if (!action_list) { + goto error; + } + + for (i = 0; i < lttng_dynamic_pointer_array_get_count(&actions); i++) { + enum lttng_action_status status; + + action = (lttng_action *) lttng_dynamic_pointer_array_steal_pointer(&actions, i); + + status = lttng_action_list_add_action(action_list, action); + if (status != LTTNG_ACTION_STATUS_OK) { + goto error; + } + + /* + * The `lttng_action_list_add_action()` takes a reference to + * the action. We can destroy ours. + */ + lttng_action_destroy(action); + action = NULL; + } + + trigger = lttng_trigger_create(condition, action_list); + if (!trigger) { + goto error; + } + + if (owner_uid) { + enum lttng_trigger_status trigger_status; + char *end; + long long uid; + + errno = 0; + uid = strtol(owner_uid, &end, 10); + if (end == owner_uid || *end != '\0' || errno != 0) { + ERR("Failed to parse `%s` as a user id.", owner_uid); + goto error; + } + + trigger_status = lttng_trigger_set_owner_uid(trigger, uid); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + ERR("Failed to set trigger's user identity."); + goto error; + } + } + + if (name) { + ret_code = lttng_register_trigger_with_name(trigger, name); + } else { + ret_code = lttng_register_trigger_with_automatic_name(trigger); + } + + if (ret_code != LTTNG_OK) { + ERR("Failed to register trigger: %s.", + lttng_strerror(-ret_code)); + goto error; + } + + if (lttng_opt_mi) { + ret_code = lttng_trigger_mi_serialize(trigger, mi_writer, NULL); + if (ret_code != LTTNG_OK) { + goto error; + } + } else { + const char *returned_trigger_name; + const enum lttng_trigger_status trigger_status = + lttng_trigger_get_name(trigger, + &returned_trigger_name); + + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + WARN("Failed to retrieve the added trigger's name."); + } else { + MSG("Added trigger `%s`.", returned_trigger_name); + } + } + + ret = 0; + + goto end; + +error: + ret = 1; + +end: + /* Mi closing. */ + if (lttng_opt_mi && mi_writer) { + int mi_ret; + + /* Close output element. */ + mi_ret = mi_lttng_writer_close_element(mi_writer); + if (mi_ret) { + ret = 1; + goto cleanup; + } + + mi_ret = mi_lttng_writer_write_element_bool(mi_writer, + mi_lttng_element_command_success, ret ? 0 : 1); + if (mi_ret) { + ret = 1; + goto cleanup; + } + + /* Command element close. */ + mi_ret = mi_lttng_writer_command_close(mi_writer); + if (mi_ret) { + ret = 1; + goto cleanup; + } + } + +cleanup: + argpar_state_destroy(argpar_state); + argpar_item_destroy(argpar_item); + lttng_dynamic_pointer_array_reset(&actions); + lttng_condition_destroy(condition); + lttng_action_destroy(action_list); + lttng_action_destroy(action); + lttng_trigger_destroy(trigger); + free(error); + free(name); + free(owner_uid); + if (mi_writer && mi_lttng_writer_destroy(mi_writer)) { + /* Preserve original error code. */ + ret = ret ? ret : CMD_ERROR; + } + + return ret; +} diff --git a/src/bin/lttng/commands/clear.c b/src/bin/lttng/commands/clear.c deleted file mode 100644 index 61879d61d..000000000 --- a/src/bin/lttng/commands/clear.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright (C) 2019 Jonathan Rajotte - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../command.h" - -#include -#include -#include - -static int opt_clear_all; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -/* Mi writer */ -static struct mi_writer *writer; - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"all", 'a', POPT_ARG_VAL, &opt_clear_all, 1, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * clear session - */ -static int clear_session(struct lttng_session *session) -{ - enum lttng_clear_handle_status status = - LTTNG_CLEAR_HANDLE_STATUS_OK; - struct lttng_clear_handle *handle = NULL; - enum lttng_error_code ret_code; - bool printed_wait_msg = false; - char *session_name = NULL; - int ret; - - ret = lttng_clear_session(session->name, &handle); - if (ret < 0) { - ERR("%s", lttng_strerror(ret)); - goto error; - } - - do { - status = lttng_clear_handle_wait_for_completion(handle, - DEFAULT_DATA_AVAILABILITY_WAIT_TIME_US / USEC_PER_MSEC); - switch (status) { - case LTTNG_CLEAR_HANDLE_STATUS_TIMEOUT: - if (!printed_wait_msg) { - _MSG("Waiting for clear of session \"%s\"", - session->name); - printed_wait_msg = true; - } - _MSG("."); - fflush(stdout); - break; - case LTTNG_CLEAR_HANDLE_STATUS_COMPLETED: - break; - default: - ERR("Failed to wait for the completion of clear for session \"%s\"", - session->name); - ret = -1; - goto error; - } - } while (status == LTTNG_CLEAR_HANDLE_STATUS_TIMEOUT); - - status = lttng_clear_handle_get_result(handle, &ret_code); - if (status != LTTNG_CLEAR_HANDLE_STATUS_OK) { - ERR("Failed to get the result of session clear"); - ret = -1; - goto error; - } - if (ret_code != LTTNG_OK) { - ret = -ret_code; - goto error; - } - - MSG("%sSession \"%s\" cleared", printed_wait_msg ? "\n" : "", - session->name); - printed_wait_msg = false; - - if (lttng_opt_mi) { - ret = mi_lttng_session(writer, session, 0); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - - ret = CMD_SUCCESS; -error: - if (printed_wait_msg) { - MSG(""); - } - lttng_clear_handle_destroy(handle); - free(session_name); - return ret; -} - -/* - * clear all sessions - * - * Call clear_session for each registered sessions - */ -static int clear_all_sessions(struct lttng_session *sessions, int count) -{ - int i, ret = CMD_SUCCESS; - - if (count == 0) { - MSG("No session found, nothing to do."); - } else if (count < 0) { - ERR("%s", lttng_strerror(ret)); - goto error; - } - - for (i = 0; i < count; i++) { - ret = clear_session(&sessions[i]); - if (ret < 0) { - goto error; - } - } -error: - return ret; -} - -/* - * The 'clear ' first level command - */ -int cmd_clear(int argc, const char **argv) -{ - int opt; - int ret = CMD_SUCCESS , i, command_ret = CMD_SUCCESS, success = 1; - static poptContext pc; - char *session_name = NULL; - const char *leftover = NULL; - bool free_session_name = false; - struct lttng_session *sessions = NULL; - int count; - int found; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - break; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - break; - default: - ret = CMD_UNDEFINED; - break; - } - goto end; - } - - /* Mi preparation */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_clear); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* For validation and semantic purpose we open a sessions element */ - ret = mi_lttng_sessions_open(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - if (!opt_clear_all) { - session_name = (char *) poptGetArg(pc); - if (!session_name) { - /* No session name specified, lookup default */ - session_name = get_session_name(); - if (session_name == NULL) { - command_ret = CMD_ERROR; - success = 0; - goto mi_closing; - } - free_session_name = true; - } - } else { - session_name = NULL; - } - - leftover = poptGetArg(pc); - if (leftover) { - ERR("Unknown argument: %s", leftover); - ret = CMD_ERROR; - success = 0; - goto mi_closing; - } - - /* Recuperate all sessions for further operation */ - count = lttng_list_sessions(&sessions); - if (count < 0) { - ERR("%s", lttng_strerror(count)); - command_ret = CMD_ERROR; - success = 0; - goto mi_closing; - } - - /* Ignore session name in case all sessions are to be cleaned */ - if (opt_clear_all) { - command_ret = clear_all_sessions(sessions, count); - if (command_ret) { - ERR("%s", lttng_strerror(command_ret)); - success = 0; - } - } else { - /* Find the corresponding lttng_session struct */ - found = 0; - for (i = 0; i < count; i++) { - if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) { - found = 1; - command_ret = clear_session(&sessions[i]); - if (command_ret) { - ERR("%s", lttng_strerror(command_ret)); - success = 0; - } - } - } - - if (!found) { - ERR("Session name %s not found", session_name); - command_ret = LTTNG_ERR_SESS_NOT_FOUND; - success = 0; - goto mi_closing; - } - } - -mi_closing: - /* Mi closing */ - if (lttng_opt_mi) { - /* Close sessions and output element element */ - ret = mi_lttng_close_multi_element(writer, 2); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } -end: - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; - } - - free(sessions); - if (free_session_name) { - free(session_name); - } - - /* Overwrite ret if an error occurred during clear_session/all */ - ret = command_ret ? command_ret : ret; - - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/clear.cpp b/src/bin/lttng/commands/clear.cpp new file mode 100644 index 000000000..61879d61d --- /dev/null +++ b/src/bin/lttng/commands/clear.cpp @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../command.h" + +#include +#include +#include + +static int opt_clear_all; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +/* Mi writer */ +static struct mi_writer *writer; + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"all", 'a', POPT_ARG_VAL, &opt_clear_all, 1, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * clear session + */ +static int clear_session(struct lttng_session *session) +{ + enum lttng_clear_handle_status status = + LTTNG_CLEAR_HANDLE_STATUS_OK; + struct lttng_clear_handle *handle = NULL; + enum lttng_error_code ret_code; + bool printed_wait_msg = false; + char *session_name = NULL; + int ret; + + ret = lttng_clear_session(session->name, &handle); + if (ret < 0) { + ERR("%s", lttng_strerror(ret)); + goto error; + } + + do { + status = lttng_clear_handle_wait_for_completion(handle, + DEFAULT_DATA_AVAILABILITY_WAIT_TIME_US / USEC_PER_MSEC); + switch (status) { + case LTTNG_CLEAR_HANDLE_STATUS_TIMEOUT: + if (!printed_wait_msg) { + _MSG("Waiting for clear of session \"%s\"", + session->name); + printed_wait_msg = true; + } + _MSG("."); + fflush(stdout); + break; + case LTTNG_CLEAR_HANDLE_STATUS_COMPLETED: + break; + default: + ERR("Failed to wait for the completion of clear for session \"%s\"", + session->name); + ret = -1; + goto error; + } + } while (status == LTTNG_CLEAR_HANDLE_STATUS_TIMEOUT); + + status = lttng_clear_handle_get_result(handle, &ret_code); + if (status != LTTNG_CLEAR_HANDLE_STATUS_OK) { + ERR("Failed to get the result of session clear"); + ret = -1; + goto error; + } + if (ret_code != LTTNG_OK) { + ret = -ret_code; + goto error; + } + + MSG("%sSession \"%s\" cleared", printed_wait_msg ? "\n" : "", + session->name); + printed_wait_msg = false; + + if (lttng_opt_mi) { + ret = mi_lttng_session(writer, session, 0); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + + ret = CMD_SUCCESS; +error: + if (printed_wait_msg) { + MSG(""); + } + lttng_clear_handle_destroy(handle); + free(session_name); + return ret; +} + +/* + * clear all sessions + * + * Call clear_session for each registered sessions + */ +static int clear_all_sessions(struct lttng_session *sessions, int count) +{ + int i, ret = CMD_SUCCESS; + + if (count == 0) { + MSG("No session found, nothing to do."); + } else if (count < 0) { + ERR("%s", lttng_strerror(ret)); + goto error; + } + + for (i = 0; i < count; i++) { + ret = clear_session(&sessions[i]); + if (ret < 0) { + goto error; + } + } +error: + return ret; +} + +/* + * The 'clear ' first level command + */ +int cmd_clear(int argc, const char **argv) +{ + int opt; + int ret = CMD_SUCCESS , i, command_ret = CMD_SUCCESS, success = 1; + static poptContext pc; + char *session_name = NULL; + const char *leftover = NULL; + bool free_session_name = false; + struct lttng_session *sessions = NULL; + int count; + int found; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + break; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + break; + default: + ret = CMD_UNDEFINED; + break; + } + goto end; + } + + /* Mi preparation */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_clear); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* For validation and semantic purpose we open a sessions element */ + ret = mi_lttng_sessions_open(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + if (!opt_clear_all) { + session_name = (char *) poptGetArg(pc); + if (!session_name) { + /* No session name specified, lookup default */ + session_name = get_session_name(); + if (session_name == NULL) { + command_ret = CMD_ERROR; + success = 0; + goto mi_closing; + } + free_session_name = true; + } + } else { + session_name = NULL; + } + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + ret = CMD_ERROR; + success = 0; + goto mi_closing; + } + + /* Recuperate all sessions for further operation */ + count = lttng_list_sessions(&sessions); + if (count < 0) { + ERR("%s", lttng_strerror(count)); + command_ret = CMD_ERROR; + success = 0; + goto mi_closing; + } + + /* Ignore session name in case all sessions are to be cleaned */ + if (opt_clear_all) { + command_ret = clear_all_sessions(sessions, count); + if (command_ret) { + ERR("%s", lttng_strerror(command_ret)); + success = 0; + } + } else { + /* Find the corresponding lttng_session struct */ + found = 0; + for (i = 0; i < count; i++) { + if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) { + found = 1; + command_ret = clear_session(&sessions[i]); + if (command_ret) { + ERR("%s", lttng_strerror(command_ret)); + success = 0; + } + } + } + + if (!found) { + ERR("Session name %s not found", session_name); + command_ret = LTTNG_ERR_SESS_NOT_FOUND; + success = 0; + goto mi_closing; + } + } + +mi_closing: + /* Mi closing */ + if (lttng_opt_mi) { + /* Close sessions and output element element */ + ret = mi_lttng_close_multi_element(writer, 2); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } +end: + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; + } + + free(sessions); + if (free_session_name) { + free(session_name); + } + + /* Overwrite ret if an error occurred during clear_session/all */ + ret = command_ret ? command_ret : ret; + + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/create.c b/src/bin/lttng/commands/create.c deleted file mode 100644 index b266c10a9..000000000 --- a/src/bin/lttng/commands/create.c +++ /dev/null @@ -1,807 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * Copyright (C) 2019 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "../command.h" -#include "../utils.h" - -#include -#include -#include -#include -#include - -static char *opt_output_path; -static char *opt_session_name; -static char *opt_url; -static char *opt_ctrl_url; -static char *opt_data_url; -static char *opt_shm_path; -static int opt_no_consumer; -static int opt_no_output; -static int opt_snapshot; -static uint32_t opt_live_timer; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, - OPT_LIVE_TIMER, -}; - -enum output_type { - OUTPUT_NONE, - OUTPUT_LOCAL, - OUTPUT_NETWORK, - OUTPUT_UNSPECIFIED, -}; - -static struct mi_writer *writer; -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL}, - {"output", 'o', POPT_ARG_STRING, &opt_output_path, 0, NULL, NULL}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {"set-url", 'U', POPT_ARG_STRING, &opt_url, 0, 0, 0}, - {"ctrl-url", 'C', POPT_ARG_STRING, &opt_ctrl_url, 0, 0, 0}, - {"data-url", 'D', POPT_ARG_STRING, &opt_data_url, 0, 0, 0}, - {"no-output", 0, POPT_ARG_VAL, &opt_no_output, 1, 0, 0}, - {"no-consumer", 0, POPT_ARG_VAL, &opt_no_consumer, 1, 0, 0}, - {"snapshot", 0, POPT_ARG_VAL, &opt_snapshot, 1, 0, 0}, - {"live", 0, POPT_ARG_INT | POPT_ARGFLAG_OPTIONAL, 0, OPT_LIVE_TIMER, 0, 0}, - {"shm-path", 0, POPT_ARG_STRING, &opt_shm_path, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * Retrieve the created session and mi output it based on provided argument - * This is currently a summary of what was pretty printed and is subject to - * enhancements. - */ -static int mi_created_session(const char *session_name) -{ - int ret, i, count, found; - struct lttng_session *sessions; - - /* session_name should not be null */ - LTTNG_ASSERT(session_name); - LTTNG_ASSERT(writer); - - count = lttng_list_sessions(&sessions); - if (count < 0) { - ret = count; - ERR("%s", lttng_strerror(ret)); - goto error; - } - - if (count == 0) { - ERR("Error session creation failed: session %s not found", session_name); - ret = -LTTNG_ERR_SESS_NOT_FOUND; - goto end; - } - - found = 0; - for (i = 0; i < count; i++) { - if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) { - found = 1; - ret = mi_lttng_session(writer, &sessions[i], 0); - if (ret) { - goto error; - } - break; - } - } - - if (!found) { - ret = -LTTNG_ERR_SESS_NOT_FOUND; - } else { - ret = CMD_SUCCESS; - } - -error: - free(sessions); -end: - return ret; -} - -static -struct lttng_session_descriptor *create_session_descriptor(void) -{ - ssize_t uri_count; - enum output_type output_type; - struct lttng_uri *uris = NULL; - struct lttng_session_descriptor *descriptor = NULL; - const char *uri_str1 = NULL, *uri_str2 = NULL; - char local_output_path[LTTNG_PATH_MAX] = {}; - - if (opt_no_output) { - output_type = OUTPUT_NONE; - } else if (opt_output_path) { - char *expanded_output_path; - int ret; - - output_type = OUTPUT_LOCAL; - expanded_output_path = utils_expand_path(opt_output_path); - if (!expanded_output_path) { - ERR("Failed to expand output path."); - goto end; - } - ret = lttng_strncpy(local_output_path, expanded_output_path, - sizeof(local_output_path)); - free(expanded_output_path); - if (ret) { - ERR("Output path exceeds the maximal supported length (%zu bytes)", - sizeof(local_output_path)); - goto end; - } - } else if (opt_url || opt_ctrl_url) { - int ret; - - uri_str1 = opt_ctrl_url ? opt_ctrl_url : opt_url; - uri_str2 = opt_data_url; - - uri_count = uri_parse_str_urls(uri_str1, uri_str2, &uris); - if (uri_count != 1 && uri_count != 2) { - ERR("Unrecognized URL format."); - goto end; - } - - switch (uri_count) { - case 1: - output_type = OUTPUT_LOCAL; - if (uris[0].dtype != LTTNG_DST_PATH) { - ERR("Unrecognized URL format."); - goto end; - } - ret = lttng_strncpy(local_output_path, uris[0].dst.path, - sizeof(local_output_path)); - if (ret) { - ERR("Output path exceeds the maximal supported length (%zu bytes)", - sizeof(local_output_path)); - } - break; - case 2: - output_type = OUTPUT_NETWORK; - break; - default: - /* Already checked. */ - abort(); - } - } else { - output_type = OUTPUT_UNSPECIFIED; - } - - if (opt_snapshot) { - /* Snapshot session. */ - switch (output_type) { - case OUTPUT_UNSPECIFIED: - case OUTPUT_LOCAL: - descriptor = lttng_session_descriptor_snapshot_local_create( - opt_session_name, - output_type == OUTPUT_LOCAL ? - local_output_path : NULL); - break; - case OUTPUT_NONE: - descriptor = lttng_session_descriptor_snapshot_create( - opt_session_name); - break; - case OUTPUT_NETWORK: - descriptor = lttng_session_descriptor_snapshot_network_create( - opt_session_name, uri_str1, uri_str2); - break; - default: - abort(); - } - } else if (opt_live_timer) { - /* Live session. */ - if (output_type != OUTPUT_UNSPECIFIED && - output_type != OUTPUT_NETWORK) { - ERR("Unsupported output type specified for live session."); - goto end; - } - descriptor = lttng_session_descriptor_live_network_create( - opt_session_name, uri_str1, uri_str2, - opt_live_timer); - } else { - /* Regular session. */ - switch (output_type) { - case OUTPUT_UNSPECIFIED: - case OUTPUT_LOCAL: - descriptor = lttng_session_descriptor_local_create( - opt_session_name, - output_type == OUTPUT_LOCAL ? - local_output_path : NULL); - break; - case OUTPUT_NONE: - descriptor = lttng_session_descriptor_create( - opt_session_name); - break; - case OUTPUT_NETWORK: - descriptor = lttng_session_descriptor_network_create( - opt_session_name, uri_str1, uri_str2); - break; - default: - abort(); - } - } - if (!descriptor) { - ERR("Failed to initialize session creation command."); - } else { - /* - * Auto-launch the relay daemon when a live session - * is created using default URLs. - */ - if (!opt_url && !opt_ctrl_url && !opt_data_url && - opt_live_timer && !check_relayd()) { - int ret; - const char *pathname = opt_relayd_path ? : - INSTALL_BIN_PATH "/lttng-relayd"; - - ret = spawn_relayd(pathname, 0); - if (ret < 0) { - lttng_session_descriptor_destroy(descriptor); - descriptor = NULL; - } - } - } -end: - free(uris); - return descriptor; -} - -/* - * Create a tracing session. - * If no name is specified, a default name is generated. - * - * Returns one of the CMD_* result constants. - */ -static int create_session(void) -{ - int ret, i; - char shm_path[LTTNG_PATH_MAX] = {}; - struct lttng_session_descriptor *session_descriptor = NULL; - enum lttng_session_descriptor_status descriptor_status; - enum lttng_error_code ret_code; - struct lttng_session *sessions = NULL; - const struct lttng_session *created_session = NULL; - const char *created_session_name; - - /* Validate options. */ - if (opt_session_name) { - if (strlen(opt_session_name) > NAME_MAX) { - ERR("Session name too long. Length must be lower or equal to %d", - NAME_MAX); - ret = CMD_ERROR; - goto error; - } - /* - * Check if the session name begins with "auto-" or is exactly "auto". - * Both are reserved for the default session name. See bug #449 to - * understand why we need to check both here. - */ - if ((strncmp(opt_session_name, DEFAULT_SESSION_NAME "-", - strlen(DEFAULT_SESSION_NAME) + 1) == 0) || - (strncmp(opt_session_name, DEFAULT_SESSION_NAME, - strlen(DEFAULT_SESSION_NAME)) == 0 && - strlen(opt_session_name) == strlen(DEFAULT_SESSION_NAME))) { - ERR("%s is a reserved keyword for default session(s)", - DEFAULT_SESSION_NAME); - ret = CMD_ERROR; - goto error; - } - } - - if (opt_snapshot && opt_live_timer) { - ERR("Snapshot and live modes are mutually exclusive."); - ret = CMD_ERROR; - goto error; - } - - if ((!opt_ctrl_url && opt_data_url) || (opt_ctrl_url && !opt_data_url)) { - ERR("Both control and data URLs must be specified."); - ret = CMD_ERROR; - goto error; - } - - session_descriptor = create_session_descriptor(); - if (!session_descriptor) { - ret = CMD_ERROR; - goto error; - } - ret_code = lttng_create_session_ext(session_descriptor); - if (ret_code != LTTNG_OK) { - ERR("%s", lttng_strerror(-ret_code)); - ret = CMD_ERROR; - goto error; - } - - descriptor_status = lttng_session_descriptor_get_session_name( - session_descriptor, &created_session_name); - if (descriptor_status != LTTNG_SESSION_DESCRIPTOR_STATUS_OK) { - ERR("Failed to obtain created session name"); - ret = CMD_ERROR; - goto error; - } - - ret = lttng_list_sessions(&sessions); - if (ret < 0) { - ERR("Failed to fetch properties of created session: %s", - lttng_strerror(ret)); - ret = CMD_ERROR; - goto error; - } - for (i = 0; i < ret; i++) { - if (!strcmp(created_session_name, sessions[i].name)) { - created_session = &sessions[i]; - break; - } - } - if (!created_session) { - ERR("Failed to fetch properties of created session"); - ret = CMD_ERROR; - goto error; - } - - if (opt_shm_path) { - char datetime_suffix[17] = {}; - - /* - * An auto-generated session name already includes the creation - * timestamp. - */ - if (opt_session_name) { - uint64_t creation_time; - struct tm *timeinfo; - time_t creation_time_t; - size_t strftime_ret; - - ret_code = lttng_session_get_creation_time( - created_session, - &creation_time); - if (ret_code != LTTNG_OK) { - ERR("%s", lttng_strerror(-ret_code)); - ret = CMD_ERROR; - goto error; - } - creation_time_t = (time_t) creation_time; - timeinfo = localtime(&creation_time_t); - if (!timeinfo) { - PERROR("Failed to interpret session creation time"); - ret = CMD_ERROR; - goto error; - } - strftime_ret = strftime(datetime_suffix, - sizeof(datetime_suffix), - "-%Y%m%d-%H%M%S", timeinfo); - if (strftime_ret == 0) { - ERR("Failed to format session creation time."); - ret = CMD_ERROR; - goto error; - } - } - - ret = snprintf(shm_path, sizeof(shm_path), - "%s/%s%s", opt_shm_path, created_session_name, - datetime_suffix); - if (ret < 0 || ret >= sizeof(shm_path)) { - ERR("Failed to format the shared memory path."); - ret = CMD_ERROR; - goto error; - } - ret = lttng_set_session_shm_path(created_session_name, - shm_path); - if (ret < 0) { - lttng_destroy_session(created_session_name); - ret = CMD_ERROR; - goto error; - } - } - - if (opt_snapshot) { - MSG("Snapshot session %s created.", created_session_name); - } else if (opt_live_timer) { - MSG("Live session %s created.", created_session_name); - } else { - MSG("Session %s created.", created_session_name); - } - - if (*created_session->path && !opt_snapshot) { - MSG("Traces will be output to %s", created_session->path); - - if (opt_live_timer) { - MSG("Live timer interval set to %u %s", opt_live_timer, - USEC_UNIT); - } - } else if (opt_snapshot) { - struct lttng_snapshot_output_list *list; - struct lttng_snapshot_output *iter; - char snapshot_url[LTTNG_PATH_MAX] = {}; - - ret = lttng_snapshot_list_output(created_session_name, &list); - if (ret < 0) { - ERR("Failed to list snapshot outputs."); - ret = CMD_ERROR; - goto error; - } - - while ((iter = lttng_snapshot_output_list_get_next(list))) { - const char *url = NULL; - - url = lttng_snapshot_output_get_ctrl_url( - iter); - ret = lttng_strncpy(snapshot_url, url, - sizeof(snapshot_url)); - if (ret) { - snapshot_url[0] = '\0'; - ERR("Failed to retrieve snapshot output destination"); - } - break; - } - lttng_snapshot_output_list_destroy(list); - - if (*snapshot_url) { - MSG("Default snapshot output set to %s", - snapshot_url); - } - MSG("Every channel enabled for this session will be set to mmap output and default to overwrite mode."); - } - if (opt_shm_path) { - MSG("Shared memory path set to %s", shm_path); - } - - /* Mi output */ - if (lttng_opt_mi) { - ret = mi_created_session(created_session_name); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - - /* Init lttng session config */ - ret = config_init(created_session_name); - if (ret < 0) { - ret = CMD_ERROR; - goto error; - } - - ret = CMD_SUCCESS; -error: - lttng_session_descriptor_destroy(session_descriptor); - free(sessions); - return ret; -} - -/* - * spawn_sessiond - * - * Spawn a session daemon by forking and execv. - */ -static int spawn_sessiond(const char *pathname) -{ - int ret = 0; - pid_t pid; - - MSG("Spawning a session daemon"); - pid = fork(); - if (pid == 0) { - /* - * Spawn session daemon in daemon mode. - */ - execlp(pathname, "lttng-sessiond", - "--daemonize", NULL); - /* execlp only returns if error happened */ - if (errno == ENOENT) { - ERR("No session daemon found. Use --sessiond-path."); - } else { - PERROR("execlp"); - } - kill(getppid(), SIGTERM); /* wake parent */ - exit(EXIT_FAILURE); - } else if (pid > 0) { - /* - * In daemon mode (--daemonize), sessiond only exits when - * it's ready to accept commands. - */ - for (;;) { - int status; - pid_t wait_pid_ret = waitpid(pid, &status, 0); - - if (wait_pid_ret < 0) { - if (errno == EINTR) { - continue; - } - PERROR("waitpid"); - ret = -errno; - goto end; - } - - if (WIFSIGNALED(status)) { - ERR("Session daemon was killed by signal %d", - WTERMSIG(status)); - ret = -1; - goto end; - } else if (WIFEXITED(status)) { - DBG("Session daemon terminated normally (exit status: %d)", - WEXITSTATUS(status)); - - if (WEXITSTATUS(status) != 0) { - ERR("Session daemon terminated with an error (exit status: %d)", - WEXITSTATUS(status)); - ret = -1; - goto end; - } - break; - } - } - - goto end; - } else { - PERROR("fork"); - ret = -1; - goto end; - } - -end: - return ret; -} - -/* - * launch_sessiond - * - * Check if the session daemon is available using - * the liblttngctl API for the check. If not, try to - * spawn a daemon. - */ -static int launch_sessiond(void) -{ - int ret; - const char *pathname = NULL; - - ret = lttng_session_daemon_alive(); - if (ret) { - /* Sessiond is alive, not an error */ - ret = 0; - goto end; - } - - /* Try command line option path */ - pathname = opt_sessiond_path; - - /* Try LTTNG_SESSIOND_PATH env variable */ - if (pathname == NULL) { - pathname = getenv(DEFAULT_SESSIOND_PATH_ENV); - } - - /* Try with configured path */ - if (pathname == NULL) { - if (CONFIG_SESSIOND_BIN[0] != '\0') { - pathname = CONFIG_SESSIOND_BIN; - } - } - - /* Try the default path */ - if (pathname == NULL) { - pathname = INSTALL_BIN_PATH "/lttng-sessiond"; - } - - DBG("Session daemon binary path: %s", pathname); - - /* Check existence and permissions */ - ret = access(pathname, F_OK | X_OK); - if (ret < 0) { - ERR("No such file or access denied: %s", pathname); - goto end; - } - - ret = spawn_sessiond(pathname); -end: - if (ret) { - ERR("Problem occurred while launching session daemon (%s)", - pathname); - } - return ret; -} - -static -int validate_url_option_combination(void) -{ - int ret = 0; - int used_count = 0; - - used_count += !!opt_url; - used_count += !!opt_output_path; - used_count += (opt_data_url || opt_ctrl_url); - if (used_count > 1) { - ERR("Only one of the --set-url, --ctrl-url/data-url, or --output options may be used at once."); - ret = -1; - } - - return ret; -} - -/* - * The 'create ' first level command - * - * Returns one of the CMD_* result constants. - */ -int cmd_create(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; - char *opt_arg = NULL; - const char *leftover = NULL; - static poptContext pc; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - case OPT_LIVE_TIMER: - { - uint64_t v; - - errno = 0; - opt_arg = poptGetOptArg(pc); - if (!opt_arg) { - /* Set up default values. */ - opt_live_timer = (uint32_t) DEFAULT_LTTNG_LIVE_TIMER; - DBG("Session live timer interval set to default value %d", - opt_live_timer); - break; - } - - if (utils_parse_time_suffix(opt_arg, &v) < 0) { - ERR("Wrong value for --live parameter: %s", opt_arg); - ret = CMD_ERROR; - goto end; - } - - if (v != (uint32_t) v) { - ERR("32-bit overflow in --live parameter: %s", opt_arg); - ret = CMD_ERROR; - goto end; - } - - if (v == 0) { - ERR("Live timer interval must be greater than zero"); - ret = CMD_ERROR; - goto end; - } - - opt_live_timer = (uint32_t) v; - DBG("Session live timer interval set to %d", opt_live_timer); - break; - } - default: - ret = CMD_UNDEFINED; - goto end; - } - } - - if (opt_no_consumer) { - MSG("The option --no-consumer is obsolete. Use --no-output now."); - ret = CMD_WARNING; - goto end; - } - - ret = validate_url_option_combination(); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Spawn a session daemon if needed */ - if (!opt_no_sessiond) { - ret = launch_sessiond(); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - /* MI initialization */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_create); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - opt_session_name = (char*) poptGetArg(pc); - - leftover = poptGetArg(pc); - if (leftover) { - ERR("Unknown argument: %s", leftover); - ret = CMD_ERROR; - goto end; - } - - command_ret = create_session(); - if (command_ret) { - success = 0; - } - - if (lttng_opt_mi) { - /* Close output element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - -end: - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; - } - - /* Overwrite ret if an error occurred in create_session() */ - ret = command_ret ? command_ret : ret; - - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/create.cpp b/src/bin/lttng/commands/create.cpp new file mode 100644 index 000000000..b266c10a9 --- /dev/null +++ b/src/bin/lttng/commands/create.cpp @@ -0,0 +1,807 @@ +/* + * Copyright (C) 2011 David Goulet + * Copyright (C) 2019 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../command.h" +#include "../utils.h" + +#include +#include +#include +#include +#include + +static char *opt_output_path; +static char *opt_session_name; +static char *opt_url; +static char *opt_ctrl_url; +static char *opt_data_url; +static char *opt_shm_path; +static int opt_no_consumer; +static int opt_no_output; +static int opt_snapshot; +static uint32_t opt_live_timer; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, + OPT_LIVE_TIMER, +}; + +enum output_type { + OUTPUT_NONE, + OUTPUT_LOCAL, + OUTPUT_NETWORK, + OUTPUT_UNSPECIFIED, +}; + +static struct mi_writer *writer; +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL}, + {"output", 'o', POPT_ARG_STRING, &opt_output_path, 0, NULL, NULL}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {"set-url", 'U', POPT_ARG_STRING, &opt_url, 0, 0, 0}, + {"ctrl-url", 'C', POPT_ARG_STRING, &opt_ctrl_url, 0, 0, 0}, + {"data-url", 'D', POPT_ARG_STRING, &opt_data_url, 0, 0, 0}, + {"no-output", 0, POPT_ARG_VAL, &opt_no_output, 1, 0, 0}, + {"no-consumer", 0, POPT_ARG_VAL, &opt_no_consumer, 1, 0, 0}, + {"snapshot", 0, POPT_ARG_VAL, &opt_snapshot, 1, 0, 0}, + {"live", 0, POPT_ARG_INT | POPT_ARGFLAG_OPTIONAL, 0, OPT_LIVE_TIMER, 0, 0}, + {"shm-path", 0, POPT_ARG_STRING, &opt_shm_path, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * Retrieve the created session and mi output it based on provided argument + * This is currently a summary of what was pretty printed and is subject to + * enhancements. + */ +static int mi_created_session(const char *session_name) +{ + int ret, i, count, found; + struct lttng_session *sessions; + + /* session_name should not be null */ + LTTNG_ASSERT(session_name); + LTTNG_ASSERT(writer); + + count = lttng_list_sessions(&sessions); + if (count < 0) { + ret = count; + ERR("%s", lttng_strerror(ret)); + goto error; + } + + if (count == 0) { + ERR("Error session creation failed: session %s not found", session_name); + ret = -LTTNG_ERR_SESS_NOT_FOUND; + goto end; + } + + found = 0; + for (i = 0; i < count; i++) { + if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) { + found = 1; + ret = mi_lttng_session(writer, &sessions[i], 0); + if (ret) { + goto error; + } + break; + } + } + + if (!found) { + ret = -LTTNG_ERR_SESS_NOT_FOUND; + } else { + ret = CMD_SUCCESS; + } + +error: + free(sessions); +end: + return ret; +} + +static +struct lttng_session_descriptor *create_session_descriptor(void) +{ + ssize_t uri_count; + enum output_type output_type; + struct lttng_uri *uris = NULL; + struct lttng_session_descriptor *descriptor = NULL; + const char *uri_str1 = NULL, *uri_str2 = NULL; + char local_output_path[LTTNG_PATH_MAX] = {}; + + if (opt_no_output) { + output_type = OUTPUT_NONE; + } else if (opt_output_path) { + char *expanded_output_path; + int ret; + + output_type = OUTPUT_LOCAL; + expanded_output_path = utils_expand_path(opt_output_path); + if (!expanded_output_path) { + ERR("Failed to expand output path."); + goto end; + } + ret = lttng_strncpy(local_output_path, expanded_output_path, + sizeof(local_output_path)); + free(expanded_output_path); + if (ret) { + ERR("Output path exceeds the maximal supported length (%zu bytes)", + sizeof(local_output_path)); + goto end; + } + } else if (opt_url || opt_ctrl_url) { + int ret; + + uri_str1 = opt_ctrl_url ? opt_ctrl_url : opt_url; + uri_str2 = opt_data_url; + + uri_count = uri_parse_str_urls(uri_str1, uri_str2, &uris); + if (uri_count != 1 && uri_count != 2) { + ERR("Unrecognized URL format."); + goto end; + } + + switch (uri_count) { + case 1: + output_type = OUTPUT_LOCAL; + if (uris[0].dtype != LTTNG_DST_PATH) { + ERR("Unrecognized URL format."); + goto end; + } + ret = lttng_strncpy(local_output_path, uris[0].dst.path, + sizeof(local_output_path)); + if (ret) { + ERR("Output path exceeds the maximal supported length (%zu bytes)", + sizeof(local_output_path)); + } + break; + case 2: + output_type = OUTPUT_NETWORK; + break; + default: + /* Already checked. */ + abort(); + } + } else { + output_type = OUTPUT_UNSPECIFIED; + } + + if (opt_snapshot) { + /* Snapshot session. */ + switch (output_type) { + case OUTPUT_UNSPECIFIED: + case OUTPUT_LOCAL: + descriptor = lttng_session_descriptor_snapshot_local_create( + opt_session_name, + output_type == OUTPUT_LOCAL ? + local_output_path : NULL); + break; + case OUTPUT_NONE: + descriptor = lttng_session_descriptor_snapshot_create( + opt_session_name); + break; + case OUTPUT_NETWORK: + descriptor = lttng_session_descriptor_snapshot_network_create( + opt_session_name, uri_str1, uri_str2); + break; + default: + abort(); + } + } else if (opt_live_timer) { + /* Live session. */ + if (output_type != OUTPUT_UNSPECIFIED && + output_type != OUTPUT_NETWORK) { + ERR("Unsupported output type specified for live session."); + goto end; + } + descriptor = lttng_session_descriptor_live_network_create( + opt_session_name, uri_str1, uri_str2, + opt_live_timer); + } else { + /* Regular session. */ + switch (output_type) { + case OUTPUT_UNSPECIFIED: + case OUTPUT_LOCAL: + descriptor = lttng_session_descriptor_local_create( + opt_session_name, + output_type == OUTPUT_LOCAL ? + local_output_path : NULL); + break; + case OUTPUT_NONE: + descriptor = lttng_session_descriptor_create( + opt_session_name); + break; + case OUTPUT_NETWORK: + descriptor = lttng_session_descriptor_network_create( + opt_session_name, uri_str1, uri_str2); + break; + default: + abort(); + } + } + if (!descriptor) { + ERR("Failed to initialize session creation command."); + } else { + /* + * Auto-launch the relay daemon when a live session + * is created using default URLs. + */ + if (!opt_url && !opt_ctrl_url && !opt_data_url && + opt_live_timer && !check_relayd()) { + int ret; + const char *pathname = opt_relayd_path ? : + INSTALL_BIN_PATH "/lttng-relayd"; + + ret = spawn_relayd(pathname, 0); + if (ret < 0) { + lttng_session_descriptor_destroy(descriptor); + descriptor = NULL; + } + } + } +end: + free(uris); + return descriptor; +} + +/* + * Create a tracing session. + * If no name is specified, a default name is generated. + * + * Returns one of the CMD_* result constants. + */ +static int create_session(void) +{ + int ret, i; + char shm_path[LTTNG_PATH_MAX] = {}; + struct lttng_session_descriptor *session_descriptor = NULL; + enum lttng_session_descriptor_status descriptor_status; + enum lttng_error_code ret_code; + struct lttng_session *sessions = NULL; + const struct lttng_session *created_session = NULL; + const char *created_session_name; + + /* Validate options. */ + if (opt_session_name) { + if (strlen(opt_session_name) > NAME_MAX) { + ERR("Session name too long. Length must be lower or equal to %d", + NAME_MAX); + ret = CMD_ERROR; + goto error; + } + /* + * Check if the session name begins with "auto-" or is exactly "auto". + * Both are reserved for the default session name. See bug #449 to + * understand why we need to check both here. + */ + if ((strncmp(opt_session_name, DEFAULT_SESSION_NAME "-", + strlen(DEFAULT_SESSION_NAME) + 1) == 0) || + (strncmp(opt_session_name, DEFAULT_SESSION_NAME, + strlen(DEFAULT_SESSION_NAME)) == 0 && + strlen(opt_session_name) == strlen(DEFAULT_SESSION_NAME))) { + ERR("%s is a reserved keyword for default session(s)", + DEFAULT_SESSION_NAME); + ret = CMD_ERROR; + goto error; + } + } + + if (opt_snapshot && opt_live_timer) { + ERR("Snapshot and live modes are mutually exclusive."); + ret = CMD_ERROR; + goto error; + } + + if ((!opt_ctrl_url && opt_data_url) || (opt_ctrl_url && !opt_data_url)) { + ERR("Both control and data URLs must be specified."); + ret = CMD_ERROR; + goto error; + } + + session_descriptor = create_session_descriptor(); + if (!session_descriptor) { + ret = CMD_ERROR; + goto error; + } + ret_code = lttng_create_session_ext(session_descriptor); + if (ret_code != LTTNG_OK) { + ERR("%s", lttng_strerror(-ret_code)); + ret = CMD_ERROR; + goto error; + } + + descriptor_status = lttng_session_descriptor_get_session_name( + session_descriptor, &created_session_name); + if (descriptor_status != LTTNG_SESSION_DESCRIPTOR_STATUS_OK) { + ERR("Failed to obtain created session name"); + ret = CMD_ERROR; + goto error; + } + + ret = lttng_list_sessions(&sessions); + if (ret < 0) { + ERR("Failed to fetch properties of created session: %s", + lttng_strerror(ret)); + ret = CMD_ERROR; + goto error; + } + for (i = 0; i < ret; i++) { + if (!strcmp(created_session_name, sessions[i].name)) { + created_session = &sessions[i]; + break; + } + } + if (!created_session) { + ERR("Failed to fetch properties of created session"); + ret = CMD_ERROR; + goto error; + } + + if (opt_shm_path) { + char datetime_suffix[17] = {}; + + /* + * An auto-generated session name already includes the creation + * timestamp. + */ + if (opt_session_name) { + uint64_t creation_time; + struct tm *timeinfo; + time_t creation_time_t; + size_t strftime_ret; + + ret_code = lttng_session_get_creation_time( + created_session, + &creation_time); + if (ret_code != LTTNG_OK) { + ERR("%s", lttng_strerror(-ret_code)); + ret = CMD_ERROR; + goto error; + } + creation_time_t = (time_t) creation_time; + timeinfo = localtime(&creation_time_t); + if (!timeinfo) { + PERROR("Failed to interpret session creation time"); + ret = CMD_ERROR; + goto error; + } + strftime_ret = strftime(datetime_suffix, + sizeof(datetime_suffix), + "-%Y%m%d-%H%M%S", timeinfo); + if (strftime_ret == 0) { + ERR("Failed to format session creation time."); + ret = CMD_ERROR; + goto error; + } + } + + ret = snprintf(shm_path, sizeof(shm_path), + "%s/%s%s", opt_shm_path, created_session_name, + datetime_suffix); + if (ret < 0 || ret >= sizeof(shm_path)) { + ERR("Failed to format the shared memory path."); + ret = CMD_ERROR; + goto error; + } + ret = lttng_set_session_shm_path(created_session_name, + shm_path); + if (ret < 0) { + lttng_destroy_session(created_session_name); + ret = CMD_ERROR; + goto error; + } + } + + if (opt_snapshot) { + MSG("Snapshot session %s created.", created_session_name); + } else if (opt_live_timer) { + MSG("Live session %s created.", created_session_name); + } else { + MSG("Session %s created.", created_session_name); + } + + if (*created_session->path && !opt_snapshot) { + MSG("Traces will be output to %s", created_session->path); + + if (opt_live_timer) { + MSG("Live timer interval set to %u %s", opt_live_timer, + USEC_UNIT); + } + } else if (opt_snapshot) { + struct lttng_snapshot_output_list *list; + struct lttng_snapshot_output *iter; + char snapshot_url[LTTNG_PATH_MAX] = {}; + + ret = lttng_snapshot_list_output(created_session_name, &list); + if (ret < 0) { + ERR("Failed to list snapshot outputs."); + ret = CMD_ERROR; + goto error; + } + + while ((iter = lttng_snapshot_output_list_get_next(list))) { + const char *url = NULL; + + url = lttng_snapshot_output_get_ctrl_url( + iter); + ret = lttng_strncpy(snapshot_url, url, + sizeof(snapshot_url)); + if (ret) { + snapshot_url[0] = '\0'; + ERR("Failed to retrieve snapshot output destination"); + } + break; + } + lttng_snapshot_output_list_destroy(list); + + if (*snapshot_url) { + MSG("Default snapshot output set to %s", + snapshot_url); + } + MSG("Every channel enabled for this session will be set to mmap output and default to overwrite mode."); + } + if (opt_shm_path) { + MSG("Shared memory path set to %s", shm_path); + } + + /* Mi output */ + if (lttng_opt_mi) { + ret = mi_created_session(created_session_name); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + + /* Init lttng session config */ + ret = config_init(created_session_name); + if (ret < 0) { + ret = CMD_ERROR; + goto error; + } + + ret = CMD_SUCCESS; +error: + lttng_session_descriptor_destroy(session_descriptor); + free(sessions); + return ret; +} + +/* + * spawn_sessiond + * + * Spawn a session daemon by forking and execv. + */ +static int spawn_sessiond(const char *pathname) +{ + int ret = 0; + pid_t pid; + + MSG("Spawning a session daemon"); + pid = fork(); + if (pid == 0) { + /* + * Spawn session daemon in daemon mode. + */ + execlp(pathname, "lttng-sessiond", + "--daemonize", NULL); + /* execlp only returns if error happened */ + if (errno == ENOENT) { + ERR("No session daemon found. Use --sessiond-path."); + } else { + PERROR("execlp"); + } + kill(getppid(), SIGTERM); /* wake parent */ + exit(EXIT_FAILURE); + } else if (pid > 0) { + /* + * In daemon mode (--daemonize), sessiond only exits when + * it's ready to accept commands. + */ + for (;;) { + int status; + pid_t wait_pid_ret = waitpid(pid, &status, 0); + + if (wait_pid_ret < 0) { + if (errno == EINTR) { + continue; + } + PERROR("waitpid"); + ret = -errno; + goto end; + } + + if (WIFSIGNALED(status)) { + ERR("Session daemon was killed by signal %d", + WTERMSIG(status)); + ret = -1; + goto end; + } else if (WIFEXITED(status)) { + DBG("Session daemon terminated normally (exit status: %d)", + WEXITSTATUS(status)); + + if (WEXITSTATUS(status) != 0) { + ERR("Session daemon terminated with an error (exit status: %d)", + WEXITSTATUS(status)); + ret = -1; + goto end; + } + break; + } + } + + goto end; + } else { + PERROR("fork"); + ret = -1; + goto end; + } + +end: + return ret; +} + +/* + * launch_sessiond + * + * Check if the session daemon is available using + * the liblttngctl API for the check. If not, try to + * spawn a daemon. + */ +static int launch_sessiond(void) +{ + int ret; + const char *pathname = NULL; + + ret = lttng_session_daemon_alive(); + if (ret) { + /* Sessiond is alive, not an error */ + ret = 0; + goto end; + } + + /* Try command line option path */ + pathname = opt_sessiond_path; + + /* Try LTTNG_SESSIOND_PATH env variable */ + if (pathname == NULL) { + pathname = getenv(DEFAULT_SESSIOND_PATH_ENV); + } + + /* Try with configured path */ + if (pathname == NULL) { + if (CONFIG_SESSIOND_BIN[0] != '\0') { + pathname = CONFIG_SESSIOND_BIN; + } + } + + /* Try the default path */ + if (pathname == NULL) { + pathname = INSTALL_BIN_PATH "/lttng-sessiond"; + } + + DBG("Session daemon binary path: %s", pathname); + + /* Check existence and permissions */ + ret = access(pathname, F_OK | X_OK); + if (ret < 0) { + ERR("No such file or access denied: %s", pathname); + goto end; + } + + ret = spawn_sessiond(pathname); +end: + if (ret) { + ERR("Problem occurred while launching session daemon (%s)", + pathname); + } + return ret; +} + +static +int validate_url_option_combination(void) +{ + int ret = 0; + int used_count = 0; + + used_count += !!opt_url; + used_count += !!opt_output_path; + used_count += (opt_data_url || opt_ctrl_url); + if (used_count > 1) { + ERR("Only one of the --set-url, --ctrl-url/data-url, or --output options may be used at once."); + ret = -1; + } + + return ret; +} + +/* + * The 'create ' first level command + * + * Returns one of the CMD_* result constants. + */ +int cmd_create(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; + char *opt_arg = NULL; + const char *leftover = NULL; + static poptContext pc; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + case OPT_LIVE_TIMER: + { + uint64_t v; + + errno = 0; + opt_arg = poptGetOptArg(pc); + if (!opt_arg) { + /* Set up default values. */ + opt_live_timer = (uint32_t) DEFAULT_LTTNG_LIVE_TIMER; + DBG("Session live timer interval set to default value %d", + opt_live_timer); + break; + } + + if (utils_parse_time_suffix(opt_arg, &v) < 0) { + ERR("Wrong value for --live parameter: %s", opt_arg); + ret = CMD_ERROR; + goto end; + } + + if (v != (uint32_t) v) { + ERR("32-bit overflow in --live parameter: %s", opt_arg); + ret = CMD_ERROR; + goto end; + } + + if (v == 0) { + ERR("Live timer interval must be greater than zero"); + ret = CMD_ERROR; + goto end; + } + + opt_live_timer = (uint32_t) v; + DBG("Session live timer interval set to %d", opt_live_timer); + break; + } + default: + ret = CMD_UNDEFINED; + goto end; + } + } + + if (opt_no_consumer) { + MSG("The option --no-consumer is obsolete. Use --no-output now."); + ret = CMD_WARNING; + goto end; + } + + ret = validate_url_option_combination(); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Spawn a session daemon if needed */ + if (!opt_no_sessiond) { + ret = launch_sessiond(); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + /* MI initialization */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_create); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + opt_session_name = (char*) poptGetArg(pc); + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + ret = CMD_ERROR; + goto end; + } + + command_ret = create_session(); + if (command_ret) { + success = 0; + } + + if (lttng_opt_mi) { + /* Close output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + +end: + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; + } + + /* Overwrite ret if an error occurred in create_session() */ + ret = command_ret ? command_ret : ret; + + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/destroy.c b/src/bin/lttng/commands/destroy.c deleted file mode 100644 index 78bbbaaad..000000000 --- a/src/bin/lttng/commands/destroy.c +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../command.h" - -#include -#include -#include - -static char *opt_session_name; -static int opt_destroy_all; -static int opt_no_wait; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -/* Mi writer */ -static struct mi_writer *writer; - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"all", 'a', POPT_ARG_VAL, &opt_destroy_all, 1, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {"no-wait", 'n', POPT_ARG_VAL, &opt_no_wait, 1, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * destroy_session - * - * Unregister the provided session to the session daemon. On success, removes - * the default configuration. - */ -static int destroy_session(struct lttng_session *session) -{ - int ret; - char *session_name = NULL; - bool session_was_already_stopped; - enum lttng_error_code ret_code; - struct lttng_destruction_handle *handle = NULL; - enum lttng_destruction_handle_status status; - bool newline_needed = false, printed_destroy_msg = false; - enum lttng_rotation_state rotation_state; - char *stats_str = NULL; - - ret = lttng_stop_tracing_no_wait(session->name); - if (ret < 0 && ret != -LTTNG_ERR_TRACE_ALREADY_STOPPED) { - ERR("%s", lttng_strerror(ret)); - } - - session_was_already_stopped = ret == -LTTNG_ERR_TRACE_ALREADY_STOPPED; - if (!opt_no_wait) { - do { - ret = lttng_data_pending(session->name); - if (ret < 0) { - /* Return the data available call error. */ - goto error; - } - - /* - * Data sleep time before retrying (in usec). Don't - * sleep if the call returned value indicates - * availability. - */ - if (ret) { - if (!printed_destroy_msg) { - _MSG("Destroying session %s", - session->name); - newline_needed = true; - printed_destroy_msg = true; - fflush(stdout); - } - - usleep(DEFAULT_DATA_AVAILABILITY_WAIT_TIME_US); - _MSG("."); - fflush(stdout); - } - } while (ret != 0); - } - - if (!session_was_already_stopped) { - /* - * Don't print the event and packet loss warnings since the user - * already saw them when stopping the trace. - */ - ret = get_session_stats_str(session->name, &stats_str); - if (ret < 0) { - goto error; - } - } - - ret_code = lttng_destroy_session_ext(session->name, &handle); - if (ret_code != LTTNG_OK) { - ret = -ret_code; - goto error; - } - - if (opt_no_wait) { - goto skip_wait_rotation; - } - - do { - status = lttng_destruction_handle_wait_for_completion( - handle, DEFAULT_DATA_AVAILABILITY_WAIT_TIME_US / - USEC_PER_MSEC); - switch (status) { - case LTTNG_DESTRUCTION_HANDLE_STATUS_TIMEOUT: - if (!printed_destroy_msg) { - _MSG("Destroying session %s", session->name); - newline_needed = true; - printed_destroy_msg = true; - } - _MSG("."); - fflush(stdout); - break; - case LTTNG_DESTRUCTION_HANDLE_STATUS_COMPLETED: - break; - default: - ERR("%sFailed to wait for the completion of the destruction of session \"%s\"", - newline_needed ? "\n" : "", - session->name); - newline_needed = false; - ret = -1; - goto error; - } - } while (status == LTTNG_DESTRUCTION_HANDLE_STATUS_TIMEOUT); - - status = lttng_destruction_handle_get_result(handle, &ret_code); - if (status != LTTNG_DESTRUCTION_HANDLE_STATUS_OK) { - ERR("%sFailed to get the result of session destruction", - newline_needed ? "\n" : ""); - ret = -1; - newline_needed = false; - goto error; - } - if (ret_code != LTTNG_OK) { - ret = -ret_code; - goto error; - } - - status = lttng_destruction_handle_get_rotation_state( - handle, &rotation_state); - if (status != LTTNG_DESTRUCTION_HANDLE_STATUS_OK) { - ERR("%sFailed to get rotation state from destruction handle", - newline_needed ? "\n" : ""); - newline_needed = false; - goto skip_wait_rotation; - } - - switch (rotation_state) { - case LTTNG_ROTATION_STATE_NO_ROTATION: - break; - case LTTNG_ROTATION_STATE_COMPLETED: - { - const struct lttng_trace_archive_location *location; - - status = lttng_destruction_handle_get_archive_location( - handle, &location); - if (status == LTTNG_DESTRUCTION_HANDLE_STATUS_OK) { - ret = print_trace_archive_location( - location, session->name); - if (ret) { - ERR("%sFailed to print the location of trace archive", - newline_needed ? "\n" : ""); - newline_needed = false; - goto skip_wait_rotation; - } - break; - } - /* fall-through. */ - } - default: - ERR("%sFailed to get the location of the rotation performed during the session's destruction", - newline_needed ? "\n" : ""); - newline_needed = false; - goto skip_wait_rotation; - } -skip_wait_rotation: - MSG("%sSession %s destroyed", newline_needed ? "\n" : "", - session->name); - newline_needed = false; - if (stats_str) { - MSG("%s", stats_str); - } - - session_name = get_session_name_quiet(); - if (session_name && !strncmp(session->name, session_name, NAME_MAX)) { - config_destroy_default(); - } - - if (lttng_opt_mi) { - ret = mi_lttng_session(writer, session, 0); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - - ret = CMD_SUCCESS; -error: - if (newline_needed) { - MSG(""); - } - lttng_destruction_handle_destroy(handle); - free(session_name); - free(stats_str); - return ret; -} - -/* - * destroy_all_sessions - * - * Call destroy_sessions for each registered sessions - */ -static int destroy_all_sessions(struct lttng_session *sessions, int count) -{ - int i; - bool error_occurred = false; - - LTTNG_ASSERT(count >= 0); - if (count == 0) { - MSG("No session found, nothing to do."); - } - - for (i = 0; i < count; i++) { - int ret = destroy_session(&sessions[i]); - - if (ret < 0) { - ERR("%s during the destruction of session \"%s\"", - lttng_strerror(ret), - sessions[i].name); - /* Continue to next session. */ - error_occurred = true; - } - } - - return error_occurred ? CMD_ERROR : CMD_SUCCESS; -} - -/* - * The 'destroy ' first level command - */ -int cmd_destroy(int argc, const char **argv) -{ - int opt; - int ret = CMD_SUCCESS , i, command_ret = CMD_SUCCESS, success = 1; - static poptContext pc; - char *session_name = NULL; - const char *leftover = NULL; - - struct lttng_session *sessions = NULL; - int count; - int found; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - break; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - break; - default: - ret = CMD_UNDEFINED; - break; - } - goto end; - } - - /* Mi preparation */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_destroy); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* For validation and semantic purpose we open a sessions element */ - ret = mi_lttng_sessions_open(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - /* Recuperate all sessions for further operation */ - count = lttng_list_sessions(&sessions); - if (count < 0) { - ERR("%s", lttng_strerror(count)); - command_ret = CMD_ERROR; - success = 0; - goto mi_closing; - } - - /* Ignore session name in case all sessions are to be destroyed */ - if (opt_destroy_all) { - command_ret = destroy_all_sessions(sessions, count); - if (command_ret) { - success = 0; - } - } else { - opt_session_name = (char *) poptGetArg(pc); - - if (!opt_session_name) { - /* No session name specified, lookup default */ - session_name = get_session_name(); - if (session_name == NULL) { - command_ret = CMD_ERROR; - success = 0; - goto mi_closing; - } - } else { - session_name = opt_session_name; - } - - /* Find the corresponding lttng_session struct */ - found = 0; - for (i = 0; i < count; i++) { - if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) { - found = 1; - command_ret = destroy_session(&sessions[i]); - if (command_ret) { - success = 0; - ERR("%s during the destruction of session \"%s\"", - lttng_strerror(command_ret), - sessions[i].name); - } - } - } - - if (!found) { - ERR("Session name %s not found", session_name); - command_ret = LTTNG_ERR_SESS_NOT_FOUND; - success = 0; - goto mi_closing; - } - } - - leftover = poptGetArg(pc); - if (leftover) { - ERR("Unknown argument: %s", leftover); - ret = CMD_ERROR; - success = 0; - goto mi_closing; - } - -mi_closing: - /* Mi closing */ - if (lttng_opt_mi) { - /* Close sessions and output element element */ - ret = mi_lttng_close_multi_element(writer, 2); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } -end: - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; - } - - if (opt_session_name == NULL) { - free(session_name); - } - - free(sessions); - - /* Overwrite ret if an error occurred during destroy_session/all */ - ret = command_ret ? command_ret : ret; - - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/destroy.cpp b/src/bin/lttng/commands/destroy.cpp new file mode 100644 index 000000000..78bbbaaad --- /dev/null +++ b/src/bin/lttng/commands/destroy.cpp @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2011 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../command.h" + +#include +#include +#include + +static char *opt_session_name; +static int opt_destroy_all; +static int opt_no_wait; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +/* Mi writer */ +static struct mi_writer *writer; + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"all", 'a', POPT_ARG_VAL, &opt_destroy_all, 1, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {"no-wait", 'n', POPT_ARG_VAL, &opt_no_wait, 1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * destroy_session + * + * Unregister the provided session to the session daemon. On success, removes + * the default configuration. + */ +static int destroy_session(struct lttng_session *session) +{ + int ret; + char *session_name = NULL; + bool session_was_already_stopped; + enum lttng_error_code ret_code; + struct lttng_destruction_handle *handle = NULL; + enum lttng_destruction_handle_status status; + bool newline_needed = false, printed_destroy_msg = false; + enum lttng_rotation_state rotation_state; + char *stats_str = NULL; + + ret = lttng_stop_tracing_no_wait(session->name); + if (ret < 0 && ret != -LTTNG_ERR_TRACE_ALREADY_STOPPED) { + ERR("%s", lttng_strerror(ret)); + } + + session_was_already_stopped = ret == -LTTNG_ERR_TRACE_ALREADY_STOPPED; + if (!opt_no_wait) { + do { + ret = lttng_data_pending(session->name); + if (ret < 0) { + /* Return the data available call error. */ + goto error; + } + + /* + * Data sleep time before retrying (in usec). Don't + * sleep if the call returned value indicates + * availability. + */ + if (ret) { + if (!printed_destroy_msg) { + _MSG("Destroying session %s", + session->name); + newline_needed = true; + printed_destroy_msg = true; + fflush(stdout); + } + + usleep(DEFAULT_DATA_AVAILABILITY_WAIT_TIME_US); + _MSG("."); + fflush(stdout); + } + } while (ret != 0); + } + + if (!session_was_already_stopped) { + /* + * Don't print the event and packet loss warnings since the user + * already saw them when stopping the trace. + */ + ret = get_session_stats_str(session->name, &stats_str); + if (ret < 0) { + goto error; + } + } + + ret_code = lttng_destroy_session_ext(session->name, &handle); + if (ret_code != LTTNG_OK) { + ret = -ret_code; + goto error; + } + + if (opt_no_wait) { + goto skip_wait_rotation; + } + + do { + status = lttng_destruction_handle_wait_for_completion( + handle, DEFAULT_DATA_AVAILABILITY_WAIT_TIME_US / + USEC_PER_MSEC); + switch (status) { + case LTTNG_DESTRUCTION_HANDLE_STATUS_TIMEOUT: + if (!printed_destroy_msg) { + _MSG("Destroying session %s", session->name); + newline_needed = true; + printed_destroy_msg = true; + } + _MSG("."); + fflush(stdout); + break; + case LTTNG_DESTRUCTION_HANDLE_STATUS_COMPLETED: + break; + default: + ERR("%sFailed to wait for the completion of the destruction of session \"%s\"", + newline_needed ? "\n" : "", + session->name); + newline_needed = false; + ret = -1; + goto error; + } + } while (status == LTTNG_DESTRUCTION_HANDLE_STATUS_TIMEOUT); + + status = lttng_destruction_handle_get_result(handle, &ret_code); + if (status != LTTNG_DESTRUCTION_HANDLE_STATUS_OK) { + ERR("%sFailed to get the result of session destruction", + newline_needed ? "\n" : ""); + ret = -1; + newline_needed = false; + goto error; + } + if (ret_code != LTTNG_OK) { + ret = -ret_code; + goto error; + } + + status = lttng_destruction_handle_get_rotation_state( + handle, &rotation_state); + if (status != LTTNG_DESTRUCTION_HANDLE_STATUS_OK) { + ERR("%sFailed to get rotation state from destruction handle", + newline_needed ? "\n" : ""); + newline_needed = false; + goto skip_wait_rotation; + } + + switch (rotation_state) { + case LTTNG_ROTATION_STATE_NO_ROTATION: + break; + case LTTNG_ROTATION_STATE_COMPLETED: + { + const struct lttng_trace_archive_location *location; + + status = lttng_destruction_handle_get_archive_location( + handle, &location); + if (status == LTTNG_DESTRUCTION_HANDLE_STATUS_OK) { + ret = print_trace_archive_location( + location, session->name); + if (ret) { + ERR("%sFailed to print the location of trace archive", + newline_needed ? "\n" : ""); + newline_needed = false; + goto skip_wait_rotation; + } + break; + } + /* fall-through. */ + } + default: + ERR("%sFailed to get the location of the rotation performed during the session's destruction", + newline_needed ? "\n" : ""); + newline_needed = false; + goto skip_wait_rotation; + } +skip_wait_rotation: + MSG("%sSession %s destroyed", newline_needed ? "\n" : "", + session->name); + newline_needed = false; + if (stats_str) { + MSG("%s", stats_str); + } + + session_name = get_session_name_quiet(); + if (session_name && !strncmp(session->name, session_name, NAME_MAX)) { + config_destroy_default(); + } + + if (lttng_opt_mi) { + ret = mi_lttng_session(writer, session, 0); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + + ret = CMD_SUCCESS; +error: + if (newline_needed) { + MSG(""); + } + lttng_destruction_handle_destroy(handle); + free(session_name); + free(stats_str); + return ret; +} + +/* + * destroy_all_sessions + * + * Call destroy_sessions for each registered sessions + */ +static int destroy_all_sessions(struct lttng_session *sessions, int count) +{ + int i; + bool error_occurred = false; + + LTTNG_ASSERT(count >= 0); + if (count == 0) { + MSG("No session found, nothing to do."); + } + + for (i = 0; i < count; i++) { + int ret = destroy_session(&sessions[i]); + + if (ret < 0) { + ERR("%s during the destruction of session \"%s\"", + lttng_strerror(ret), + sessions[i].name); + /* Continue to next session. */ + error_occurred = true; + } + } + + return error_occurred ? CMD_ERROR : CMD_SUCCESS; +} + +/* + * The 'destroy ' first level command + */ +int cmd_destroy(int argc, const char **argv) +{ + int opt; + int ret = CMD_SUCCESS , i, command_ret = CMD_SUCCESS, success = 1; + static poptContext pc; + char *session_name = NULL; + const char *leftover = NULL; + + struct lttng_session *sessions = NULL; + int count; + int found; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + break; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + break; + default: + ret = CMD_UNDEFINED; + break; + } + goto end; + } + + /* Mi preparation */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_destroy); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* For validation and semantic purpose we open a sessions element */ + ret = mi_lttng_sessions_open(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + /* Recuperate all sessions for further operation */ + count = lttng_list_sessions(&sessions); + if (count < 0) { + ERR("%s", lttng_strerror(count)); + command_ret = CMD_ERROR; + success = 0; + goto mi_closing; + } + + /* Ignore session name in case all sessions are to be destroyed */ + if (opt_destroy_all) { + command_ret = destroy_all_sessions(sessions, count); + if (command_ret) { + success = 0; + } + } else { + opt_session_name = (char *) poptGetArg(pc); + + if (!opt_session_name) { + /* No session name specified, lookup default */ + session_name = get_session_name(); + if (session_name == NULL) { + command_ret = CMD_ERROR; + success = 0; + goto mi_closing; + } + } else { + session_name = opt_session_name; + } + + /* Find the corresponding lttng_session struct */ + found = 0; + for (i = 0; i < count; i++) { + if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) { + found = 1; + command_ret = destroy_session(&sessions[i]); + if (command_ret) { + success = 0; + ERR("%s during the destruction of session \"%s\"", + lttng_strerror(command_ret), + sessions[i].name); + } + } + } + + if (!found) { + ERR("Session name %s not found", session_name); + command_ret = LTTNG_ERR_SESS_NOT_FOUND; + success = 0; + goto mi_closing; + } + } + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + ret = CMD_ERROR; + success = 0; + goto mi_closing; + } + +mi_closing: + /* Mi closing */ + if (lttng_opt_mi) { + /* Close sessions and output element element */ + ret = mi_lttng_close_multi_element(writer, 2); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } +end: + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; + } + + if (opt_session_name == NULL) { + free(session_name); + } + + free(sessions); + + /* Overwrite ret if an error occurred during destroy_session/all */ + ret = command_ret ? command_ret : ret; + + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/disable_channels.c b/src/bin/lttng/commands/disable_channels.c deleted file mode 100644 index cf9f20bb6..000000000 --- a/src/bin/lttng/commands/disable_channels.c +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "../command.h" - -static char *opt_channels; -static int opt_kernel; -static char *opt_session_name; -static int opt_userspace; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_USERSPACE, - OPT_LIST_OPTIONS, -}; - -static struct lttng_handle *handle; -static struct mi_writer *writer; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, - {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, - {"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {0, 0, 0, 0, 0, 0, 0} -}; - -static int mi_partial_channel_print(char *channel_name, unsigned int enabled, - int success) -{ - int ret; - - LTTNG_ASSERT(writer); - LTTNG_ASSERT(channel_name); - - /* Open channel element */ - ret = mi_lttng_writer_open_element(writer, config_element_channel); - if (ret) { - goto end; - } - - /* Name */ - ret = mi_lttng_writer_write_element_string(writer, config_element_name, - channel_name); - if (ret) { - goto end; - } - - /* Enabled ? */ - ret = mi_lttng_writer_write_element_bool(writer, config_element_enabled, - enabled); - if (ret) { - goto end; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_success, success); - if (ret) { - goto end; - } - - /* Closing channel element */ - ret = mi_lttng_writer_close_element(writer); - -end: - return ret; -} - -/* - * Disabling channel using the lttng API. - */ -static int disable_channels(char *session_name) -{ - int ret = CMD_SUCCESS, warn = 0, success; - - /* Normal case for disable channed is enabled = 0 */ - unsigned int enabled = 0; - char *channel_name; - struct lttng_domain dom; - - memset(&dom, 0, sizeof(dom)); - - /* Create lttng domain */ - if (opt_kernel) { - dom.type = LTTNG_DOMAIN_KERNEL; - } else if (opt_userspace) { - dom.type = LTTNG_DOMAIN_UST; - } else { - /* Checked by the caller. */ - abort(); - } - - handle = lttng_create_handle(session_name, &dom); - if (handle == NULL) { - ret = -1; - goto error; - } - - /* Prepare MI */ - if (lttng_opt_mi) { - /* open a channels element */ - ret = mi_lttng_writer_open_element(writer, config_element_channels); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - } - - /* Strip channel list */ - channel_name = strtok(opt_channels, ","); - while (channel_name != NULL) { - DBG("Disabling channel %s", channel_name); - - ret = lttng_disable_channel(handle, channel_name); - if (ret < 0) { - ERR("Channel %s: %s (session %s)", channel_name, - lttng_strerror(ret), session_name); - warn = 1; - - /* - * Mi: - * We assume that if an error occurred the channel is still active. - * This might not be the case but is a good assumption. - * The client should look at the stderr stream - * for more informations. - */ - enabled = 1; - success = 0; - - } else { - MSG("%s channel %s disabled for session %s", - lttng_domain_type_str(dom.type), - channel_name, session_name); - enabled = 0; - success = 1; - } - - /* Print the channel */ - if (lttng_opt_mi) { - ret = mi_partial_channel_print(channel_name, enabled, success); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - - /* Next channel */ - channel_name = strtok(NULL, ","); - } - - ret = CMD_SUCCESS; - - /* Close Mi */ - if (lttng_opt_mi) { - /* Close channels element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - -error: - /* Bypass the warning if a more important error happened */ - if (!ret && warn) { - ret = CMD_WARNING; - } - - lttng_destroy_handle(handle); - - return ret; -} - -/* - * cmd_disable_channels - * - * Disable channel to trace session - */ -int cmd_disable_channels(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; - static poptContext pc; - char *session_name = NULL; - const char *leftover = NULL; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_USERSPACE: - opt_userspace = 1; - break; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - default: - ret = CMD_UNDEFINED; - goto end; - } - } - - ret = print_missing_or_multiple_domains( - opt_kernel + opt_userspace, false); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - opt_channels = (char*) poptGetArg(pc); - if (opt_channels == NULL) { - ERR("Missing channel name(s).\n"); - ret = CMD_ERROR; - goto end; - } - - leftover = poptGetArg(pc); - if (leftover) { - ERR("Unknown argument: %s", leftover); - ret = CMD_ERROR; - goto end; - } - - if (!opt_session_name) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = CMD_ERROR; - goto end; - } - } else { - session_name = opt_session_name; - } - - /* Mi check */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_disable_channel); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - command_ret = disable_channels(session_name); - if (command_ret) { - success = 0; - } - - /* Mi closing */ - if (lttng_opt_mi) { - /* Close output element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_success, success); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - -end: - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - ret = ret ? ret : LTTNG_ERR_MI_IO_FAIL; - } - - if (!opt_session_name && session_name) { - free(session_name); - } - - /* Overwrite ret if an error occurred in disable_channels */ - ret = command_ret ? command_ret : ret; - - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/disable_channels.cpp b/src/bin/lttng/commands/disable_channels.cpp new file mode 100644 index 000000000..cf9f20bb6 --- /dev/null +++ b/src/bin/lttng/commands/disable_channels.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2011 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../command.h" + +static char *opt_channels; +static int opt_kernel; +static char *opt_session_name; +static int opt_userspace; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_USERSPACE, + OPT_LIST_OPTIONS, +}; + +static struct lttng_handle *handle; +static struct mi_writer *writer; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, + {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, + {"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {0, 0, 0, 0, 0, 0, 0} +}; + +static int mi_partial_channel_print(char *channel_name, unsigned int enabled, + int success) +{ + int ret; + + LTTNG_ASSERT(writer); + LTTNG_ASSERT(channel_name); + + /* Open channel element */ + ret = mi_lttng_writer_open_element(writer, config_element_channel); + if (ret) { + goto end; + } + + /* Name */ + ret = mi_lttng_writer_write_element_string(writer, config_element_name, + channel_name); + if (ret) { + goto end; + } + + /* Enabled ? */ + ret = mi_lttng_writer_write_element_bool(writer, config_element_enabled, + enabled); + if (ret) { + goto end; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_success, success); + if (ret) { + goto end; + } + + /* Closing channel element */ + ret = mi_lttng_writer_close_element(writer); + +end: + return ret; +} + +/* + * Disabling channel using the lttng API. + */ +static int disable_channels(char *session_name) +{ + int ret = CMD_SUCCESS, warn = 0, success; + + /* Normal case for disable channed is enabled = 0 */ + unsigned int enabled = 0; + char *channel_name; + struct lttng_domain dom; + + memset(&dom, 0, sizeof(dom)); + + /* Create lttng domain */ + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + } else if (opt_userspace) { + dom.type = LTTNG_DOMAIN_UST; + } else { + /* Checked by the caller. */ + abort(); + } + + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = -1; + goto error; + } + + /* Prepare MI */ + if (lttng_opt_mi) { + /* open a channels element */ + ret = mi_lttng_writer_open_element(writer, config_element_channels); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + } + + /* Strip channel list */ + channel_name = strtok(opt_channels, ","); + while (channel_name != NULL) { + DBG("Disabling channel %s", channel_name); + + ret = lttng_disable_channel(handle, channel_name); + if (ret < 0) { + ERR("Channel %s: %s (session %s)", channel_name, + lttng_strerror(ret), session_name); + warn = 1; + + /* + * Mi: + * We assume that if an error occurred the channel is still active. + * This might not be the case but is a good assumption. + * The client should look at the stderr stream + * for more informations. + */ + enabled = 1; + success = 0; + + } else { + MSG("%s channel %s disabled for session %s", + lttng_domain_type_str(dom.type), + channel_name, session_name); + enabled = 0; + success = 1; + } + + /* Print the channel */ + if (lttng_opt_mi) { + ret = mi_partial_channel_print(channel_name, enabled, success); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + + /* Next channel */ + channel_name = strtok(NULL, ","); + } + + ret = CMD_SUCCESS; + + /* Close Mi */ + if (lttng_opt_mi) { + /* Close channels element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + +error: + /* Bypass the warning if a more important error happened */ + if (!ret && warn) { + ret = CMD_WARNING; + } + + lttng_destroy_handle(handle); + + return ret; +} + +/* + * cmd_disable_channels + * + * Disable channel to trace session + */ +int cmd_disable_channels(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; + static poptContext pc; + char *session_name = NULL; + const char *leftover = NULL; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_USERSPACE: + opt_userspace = 1; + break; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + default: + ret = CMD_UNDEFINED; + goto end; + } + } + + ret = print_missing_or_multiple_domains( + opt_kernel + opt_userspace, false); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + opt_channels = (char*) poptGetArg(pc); + if (opt_channels == NULL) { + ERR("Missing channel name(s).\n"); + ret = CMD_ERROR; + goto end; + } + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + ret = CMD_ERROR; + goto end; + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = CMD_ERROR; + goto end; + } + } else { + session_name = opt_session_name; + } + + /* Mi check */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_disable_channel); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + command_ret = disable_channels(session_name); + if (command_ret) { + success = 0; + } + + /* Mi closing */ + if (lttng_opt_mi) { + /* Close output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + +end: + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : LTTNG_ERR_MI_IO_FAIL; + } + + if (!opt_session_name && session_name) { + free(session_name); + } + + /* Overwrite ret if an error occurred in disable_channels */ + ret = command_ret ? command_ret : ret; + + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/disable_events.c b/src/bin/lttng/commands/disable_events.c deleted file mode 100644 index 687da1f92..000000000 --- a/src/bin/lttng/commands/disable_events.c +++ /dev/null @@ -1,481 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "../command.h" - -static char *opt_event_list; -static int opt_kernel; -static char *opt_channel_name; -static char *opt_session_name; -static int opt_userspace; -static int opt_disable_all; -static int opt_jul; -static int opt_log4j; -static int opt_python; -static int opt_event_type; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_TYPE_SYSCALL, - OPT_TYPE_TRACEPOINT, - OPT_TYPE_PROBE, - OPT_TYPE_FUNCTION, - OPT_TYPE_ALL, - OPT_LIST_OPTIONS, -}; - -static struct lttng_handle *handle; -static struct mi_writer *writer; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, - {"all-events", 'a', POPT_ARG_VAL, &opt_disable_all, 1, 0, 0}, - {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0}, - {"jul", 'j', POPT_ARG_VAL, &opt_jul, 1, 0, 0}, - {"log4j", 'l', POPT_ARG_VAL, &opt_log4j, 1, 0, 0}, - {"python", 'p', POPT_ARG_VAL, &opt_python, 1, 0, 0}, - {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, - {"userspace", 'u', POPT_ARG_VAL, &opt_userspace, 1, 0, 0}, - {"syscall", 0, POPT_ARG_NONE, 0, OPT_TYPE_SYSCALL, 0, 0}, - {"probe", 0, POPT_ARG_NONE, 0, OPT_TYPE_PROBE, 0, 0}, - {"tracepoint", 0, POPT_ARG_NONE, 0, OPT_TYPE_TRACEPOINT, 0, 0}, - {"function", 0, POPT_ARG_NONE, 0, OPT_TYPE_FUNCTION, 0, 0}, - {"all", 0, POPT_ARG_NONE, 0, OPT_TYPE_ALL, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {0, 0, 0, 0, 0, 0, 0} -}; - -static -const char *print_channel_name(const char *name) -{ - return name ? : DEFAULT_CHANNEL_NAME; -} - -static -const char *print_raw_channel_name(const char *name) -{ - return name ? : ""; -} - -static -const char *print_event_type(const enum lttng_event_type ev_type) -{ - switch (ev_type) { - case LTTNG_EVENT_ALL: - return "any"; - case LTTNG_EVENT_TRACEPOINT: - return "tracepoint"; - case LTTNG_EVENT_PROBE: - return "probe"; - case LTTNG_EVENT_FUNCTION: - return "function"; - case LTTNG_EVENT_FUNCTION_ENTRY: - return "function entry"; - case LTTNG_EVENT_SYSCALL: - return "syscall"; - default: - return ""; - } -} - -/* Mi print a partial event. - * enabled is 0 or 1 - * success is 0 or 1 - */ -static int mi_print_event(const char *event_name, int enabled, int success) -{ - int ret; - - LTTNG_ASSERT(writer); - LTTNG_ASSERT(event_name); - - /* Open event element */ - ret = mi_lttng_writer_open_element(writer, config_element_event); - if (ret) { - goto end; - } - - /* Print the name of event */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_name, event_name); - if (ret) { - goto end; - } - - /* Print enabled ? */ - ret = mi_lttng_writer_write_element_bool(writer, - config_element_enabled, enabled); - if (ret) { - goto end; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - goto end; - } - - /* Close event element */ - ret = mi_lttng_writer_close_element(writer); -end: - return ret; -} - -/* - * disable_events - * - * Disabling event using the lttng API. - */ -static int disable_events(char *session_name) -{ - int ret = CMD_SUCCESS, warn = 0, command_ret = CMD_SUCCESS; - int enabled = 1, success = 1; - char *event_name, *channel_name = NULL; - struct lttng_domain dom; - struct lttng_event event; - - memset(&dom, 0, sizeof(dom)); - - /* Create lttng domain */ - if (opt_kernel) { - dom.type = LTTNG_DOMAIN_KERNEL; - } else if (opt_userspace) { - dom.type = LTTNG_DOMAIN_UST; - } else if (opt_jul) { - dom.type = LTTNG_DOMAIN_JUL; - } else if (opt_log4j) { - dom.type = LTTNG_DOMAIN_LOG4J; - } else if (opt_python) { - dom.type = LTTNG_DOMAIN_PYTHON; - } else { - /* Checked by the caller. */ - abort(); - } - - channel_name = opt_channel_name; - - handle = lttng_create_handle(session_name, &dom); - if (handle == NULL) { - ret = -1; - goto error; - } - - /* Mi print the channel and open the events element */ - if (lttng_opt_mi) { - ret = mi_lttng_writer_open_element(writer, config_element_channel); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - ret = mi_lttng_writer_write_element_string(writer, - config_element_name, print_channel_name(channel_name)); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open events element */ - ret = mi_lttng_writer_open_element(writer, config_element_events); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - memset(&event, 0, sizeof(event)); - /* Set default loglevel to any/unknown */ - event.loglevel = -1; - - /* opt_event_type contain the event type to disable at this point */ - event.type = opt_event_type; - - if (opt_disable_all) { - command_ret = lttng_disable_event_ext(handle, &event, channel_name, NULL); - if (command_ret < 0) { - ERR("%s", lttng_strerror(command_ret)); - enabled = 1; - success = 0; - - } else { - enabled = 0; - success = 1; - MSG("All %s events of type %s are disabled in channel %s", - lttng_domain_type_str(dom.type), - print_event_type(opt_event_type), - print_channel_name(channel_name)); - } - - if (lttng_opt_mi) { - ret = mi_print_event("*", enabled, success); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - } else { - /* Strip event list */ - event_name = strtok(opt_event_list, ","); - while (event_name != NULL) { - DBG("Disabling event %s", event_name); - - strncpy(event.name, event_name, sizeof(event.name)); - event.name[sizeof(event.name) - 1] = '\0'; - command_ret = lttng_disable_event_ext(handle, &event, channel_name, NULL); - if (command_ret < 0) { - ERR("%s of type %s : %s (channel %s, session %s)", - event_name, - print_event_type(opt_event_type), - lttng_strerror(command_ret), - command_ret == -LTTNG_ERR_NEED_CHANNEL_NAME - ? print_raw_channel_name(channel_name) - : print_channel_name(channel_name), - session_name); - warn = 1; - success = 0; - /* - * If an error occurred we assume that the event is still - * enabled. - */ - enabled = 1; - } else { - MSG("%s %s of type %s disabled in channel %s for session %s", - lttng_domain_type_str(dom.type), - event_name, - print_event_type(opt_event_type), - print_channel_name(channel_name), - session_name); - success = 1; - enabled = 0; - } - - if (lttng_opt_mi) { - ret = mi_print_event(event_name, enabled, success); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - - /* Next event */ - event_name = strtok(NULL, ","); - } - } - -end: - if (lttng_opt_mi) { - /* Close events element and channel element */ - ret = mi_lttng_close_multi_element(writer, 2); - if (ret) { - ret = CMD_ERROR; - } - } -error: - /* if there is already an error preserve it */ - if (warn && !ret) { - ret = CMD_WARNING; - } - - /* Overwrite ret if an error occurred */ - ret = command_ret ? command_ret : ret; - - lttng_destroy_handle(handle); - return ret; -} - -/* - * cmd_disable_events - * - * Disable event to trace session - */ -int cmd_disable_events(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; - static poptContext pc; - char *session_name = NULL; - const char *leftover = NULL; - int event_type = -1; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - /* Default event type */ - opt_event_type = LTTNG_EVENT_ALL; - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_TYPE_SYSCALL: - opt_event_type = LTTNG_EVENT_SYSCALL; - break; - case OPT_TYPE_TRACEPOINT: - opt_event_type = LTTNG_EVENT_TRACEPOINT; - break; - case OPT_TYPE_PROBE: - opt_event_type = LTTNG_EVENT_PROBE; - break; - case OPT_TYPE_FUNCTION: - opt_event_type = LTTNG_EVENT_FUNCTION; - break; - case OPT_TYPE_ALL: - opt_event_type = LTTNG_EVENT_ALL; - break; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - default: - ret = CMD_UNDEFINED; - goto end; - } - - /* Validate event type. Multiple event type are not supported. */ - if (event_type == -1) { - event_type = opt_event_type; - } else { - if (event_type != opt_event_type) { - ERR("Multiple event type not supported."); - ret = CMD_ERROR; - goto end; - } - } - } - - ret = print_missing_or_multiple_domains( - opt_kernel + opt_userspace + opt_jul + opt_log4j + - opt_python, - true); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Ust and agent only support ALL event type */ - if ((opt_userspace || opt_jul || opt_log4j || opt_python) - && opt_event_type != LTTNG_EVENT_ALL) { - ERR("Disabling userspace and agent (-j | -l | -p) event(s) based on instrumentation type is not supported.\n"); - ret = CMD_ERROR; - goto end; - } - - opt_event_list = (char*) poptGetArg(pc); - if (opt_event_list == NULL && opt_disable_all == 0) { - ERR("Missing event name(s).\n"); - ret = CMD_ERROR; - goto end; - } - - leftover = poptGetArg(pc); - if (leftover) { - ERR("Unknown argument: %s", leftover); - ret = CMD_ERROR; - goto end; - } - - if (!opt_session_name) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = CMD_ERROR; - goto end; - } - } else { - session_name = opt_session_name; - } - - /* Mi check */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_disable_event); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - command_ret = disable_events(session_name); - if (command_ret) { - success = 0; - } - - /* Mi closing */ - if (lttng_opt_mi) { - /* Close output element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - -end: - if (!opt_session_name && session_name) { - free(session_name); - } - - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - ret = ret ? ret : LTTNG_ERR_MI_IO_FAIL; - } - - /* Overwrite ret if an error occurred in disable_events */ - ret = command_ret ? command_ret : ret; - - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/disable_events.cpp b/src/bin/lttng/commands/disable_events.cpp new file mode 100644 index 000000000..5ce36e50a --- /dev/null +++ b/src/bin/lttng/commands/disable_events.cpp @@ -0,0 +1,481 @@ +/* + * Copyright (C) 2011 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../command.h" + +static char *opt_event_list; +static int opt_kernel; +static char *opt_channel_name; +static char *opt_session_name; +static int opt_userspace; +static int opt_disable_all; +static int opt_jul; +static int opt_log4j; +static int opt_python; +static int opt_event_type; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_TYPE_SYSCALL, + OPT_TYPE_TRACEPOINT, + OPT_TYPE_PROBE, + OPT_TYPE_FUNCTION, + OPT_TYPE_ALL, + OPT_LIST_OPTIONS, +}; + +static struct lttng_handle *handle; +static struct mi_writer *writer; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, + {"all-events", 'a', POPT_ARG_VAL, &opt_disable_all, 1, 0, 0}, + {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0}, + {"jul", 'j', POPT_ARG_VAL, &opt_jul, 1, 0, 0}, + {"log4j", 'l', POPT_ARG_VAL, &opt_log4j, 1, 0, 0}, + {"python", 'p', POPT_ARG_VAL, &opt_python, 1, 0, 0}, + {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, + {"userspace", 'u', POPT_ARG_VAL, &opt_userspace, 1, 0, 0}, + {"syscall", 0, POPT_ARG_NONE, 0, OPT_TYPE_SYSCALL, 0, 0}, + {"probe", 0, POPT_ARG_NONE, 0, OPT_TYPE_PROBE, 0, 0}, + {"tracepoint", 0, POPT_ARG_NONE, 0, OPT_TYPE_TRACEPOINT, 0, 0}, + {"function", 0, POPT_ARG_NONE, 0, OPT_TYPE_FUNCTION, 0, 0}, + {"all", 0, POPT_ARG_NONE, 0, OPT_TYPE_ALL, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {0, 0, 0, 0, 0, 0, 0} +}; + +static +const char *print_channel_name(const char *name) +{ + return name ? : DEFAULT_CHANNEL_NAME; +} + +static +const char *print_raw_channel_name(const char *name) +{ + return name ? : ""; +} + +static +const char *print_event_type(const enum lttng_event_type ev_type) +{ + switch (ev_type) { + case LTTNG_EVENT_ALL: + return "any"; + case LTTNG_EVENT_TRACEPOINT: + return "tracepoint"; + case LTTNG_EVENT_PROBE: + return "probe"; + case LTTNG_EVENT_FUNCTION: + return "function"; + case LTTNG_EVENT_FUNCTION_ENTRY: + return "function entry"; + case LTTNG_EVENT_SYSCALL: + return "syscall"; + default: + return ""; + } +} + +/* Mi print a partial event. + * enabled is 0 or 1 + * success is 0 or 1 + */ +static int mi_print_event(const char *event_name, int enabled, int success) +{ + int ret; + + LTTNG_ASSERT(writer); + LTTNG_ASSERT(event_name); + + /* Open event element */ + ret = mi_lttng_writer_open_element(writer, config_element_event); + if (ret) { + goto end; + } + + /* Print the name of event */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_name, event_name); + if (ret) { + goto end; + } + + /* Print enabled ? */ + ret = mi_lttng_writer_write_element_bool(writer, + config_element_enabled, enabled); + if (ret) { + goto end; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + goto end; + } + + /* Close event element */ + ret = mi_lttng_writer_close_element(writer); +end: + return ret; +} + +/* + * disable_events + * + * Disabling event using the lttng API. + */ +static int disable_events(char *session_name) +{ + int ret = CMD_SUCCESS, warn = 0, command_ret = CMD_SUCCESS; + int enabled = 1, success = 1; + char *event_name, *channel_name = NULL; + struct lttng_domain dom; + struct lttng_event event; + + memset(&dom, 0, sizeof(dom)); + + /* Create lttng domain */ + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + } else if (opt_userspace) { + dom.type = LTTNG_DOMAIN_UST; + } else if (opt_jul) { + dom.type = LTTNG_DOMAIN_JUL; + } else if (opt_log4j) { + dom.type = LTTNG_DOMAIN_LOG4J; + } else if (opt_python) { + dom.type = LTTNG_DOMAIN_PYTHON; + } else { + /* Checked by the caller. */ + abort(); + } + + channel_name = opt_channel_name; + + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = -1; + goto error; + } + + /* Mi print the channel and open the events element */ + if (lttng_opt_mi) { + ret = mi_lttng_writer_open_element(writer, config_element_channel); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + ret = mi_lttng_writer_write_element_string(writer, + config_element_name, print_channel_name(channel_name)); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open events element */ + ret = mi_lttng_writer_open_element(writer, config_element_events); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + memset(&event, 0, sizeof(event)); + /* Set default loglevel to any/unknown */ + event.loglevel = -1; + + /* opt_event_type contain the event type to disable at this point */ + event.type = (lttng_event_type) opt_event_type; + + if (opt_disable_all) { + command_ret = lttng_disable_event_ext(handle, &event, channel_name, NULL); + if (command_ret < 0) { + ERR("%s", lttng_strerror(command_ret)); + enabled = 1; + success = 0; + + } else { + enabled = 0; + success = 1; + MSG("All %s events of type %s are disabled in channel %s", + lttng_domain_type_str(dom.type), + print_event_type((lttng_event_type) opt_event_type), + print_channel_name(channel_name)); + } + + if (lttng_opt_mi) { + ret = mi_print_event("*", enabled, success); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + } else { + /* Strip event list */ + event_name = strtok(opt_event_list, ","); + while (event_name != NULL) { + DBG("Disabling event %s", event_name); + + strncpy(event.name, event_name, sizeof(event.name)); + event.name[sizeof(event.name) - 1] = '\0'; + command_ret = lttng_disable_event_ext(handle, &event, channel_name, NULL); + if (command_ret < 0) { + ERR("%s of type %s : %s (channel %s, session %s)", + event_name, + print_event_type((lttng_event_type) opt_event_type), + lttng_strerror(command_ret), + command_ret == -LTTNG_ERR_NEED_CHANNEL_NAME + ? print_raw_channel_name(channel_name) + : print_channel_name(channel_name), + session_name); + warn = 1; + success = 0; + /* + * If an error occurred we assume that the event is still + * enabled. + */ + enabled = 1; + } else { + MSG("%s %s of type %s disabled in channel %s for session %s", + lttng_domain_type_str(dom.type), + event_name, + print_event_type((lttng_event_type) opt_event_type), + print_channel_name(channel_name), + session_name); + success = 1; + enabled = 0; + } + + if (lttng_opt_mi) { + ret = mi_print_event(event_name, enabled, success); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + + /* Next event */ + event_name = strtok(NULL, ","); + } + } + +end: + if (lttng_opt_mi) { + /* Close events element and channel element */ + ret = mi_lttng_close_multi_element(writer, 2); + if (ret) { + ret = CMD_ERROR; + } + } +error: + /* if there is already an error preserve it */ + if (warn && !ret) { + ret = CMD_WARNING; + } + + /* Overwrite ret if an error occurred */ + ret = command_ret ? command_ret : ret; + + lttng_destroy_handle(handle); + return ret; +} + +/* + * cmd_disable_events + * + * Disable event to trace session + */ +int cmd_disable_events(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; + static poptContext pc; + char *session_name = NULL; + const char *leftover = NULL; + int event_type = -1; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + /* Default event type */ + opt_event_type = LTTNG_EVENT_ALL; + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_TYPE_SYSCALL: + opt_event_type = LTTNG_EVENT_SYSCALL; + break; + case OPT_TYPE_TRACEPOINT: + opt_event_type = LTTNG_EVENT_TRACEPOINT; + break; + case OPT_TYPE_PROBE: + opt_event_type = LTTNG_EVENT_PROBE; + break; + case OPT_TYPE_FUNCTION: + opt_event_type = LTTNG_EVENT_FUNCTION; + break; + case OPT_TYPE_ALL: + opt_event_type = LTTNG_EVENT_ALL; + break; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + default: + ret = CMD_UNDEFINED; + goto end; + } + + /* Validate event type. Multiple event type are not supported. */ + if (event_type == -1) { + event_type = opt_event_type; + } else { + if (event_type != opt_event_type) { + ERR("Multiple event type not supported."); + ret = CMD_ERROR; + goto end; + } + } + } + + ret = print_missing_or_multiple_domains( + opt_kernel + opt_userspace + opt_jul + opt_log4j + + opt_python, + true); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Ust and agent only support ALL event type */ + if ((opt_userspace || opt_jul || opt_log4j || opt_python) + && opt_event_type != LTTNG_EVENT_ALL) { + ERR("Disabling userspace and agent (-j | -l | -p) event(s) based on instrumentation type is not supported.\n"); + ret = CMD_ERROR; + goto end; + } + + opt_event_list = (char*) poptGetArg(pc); + if (opt_event_list == NULL && opt_disable_all == 0) { + ERR("Missing event name(s).\n"); + ret = CMD_ERROR; + goto end; + } + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + ret = CMD_ERROR; + goto end; + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = CMD_ERROR; + goto end; + } + } else { + session_name = opt_session_name; + } + + /* Mi check */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_disable_event); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + command_ret = disable_events(session_name); + if (command_ret) { + success = 0; + } + + /* Mi closing */ + if (lttng_opt_mi) { + /* Close output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + +end: + if (!opt_session_name && session_name) { + free(session_name); + } + + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : LTTNG_ERR_MI_IO_FAIL; + } + + /* Overwrite ret if an error occurred in disable_events */ + ret = command_ret ? command_ret : ret; + + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/disable_rotation.c b/src/bin/lttng/commands/disable_rotation.c deleted file mode 100644 index ff5749c97..000000000 --- a/src/bin/lttng/commands/disable_rotation.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (C) 2017 Julien Desfossez - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "../command.h" -#include - -static char *opt_session_name; -static struct mi_writer *writer; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, - OPT_TIMER, - OPT_SIZE, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, - {"timer", 0, POPT_ARG_NONE, 0, OPT_TIMER, 0, 0}, - {"size", 0, POPT_ARG_NONE, 0, OPT_SIZE, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -static const char *schedule_type_str[] = { - [LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC] = "periodic", - [LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD] = "size-based", -}; - -static const struct lttng_rotation_schedule *get_schedule( - const char *session_name, - const struct lttng_rotation_schedules *schedules, - enum lttng_rotation_schedule_type schedule_type) -{ - unsigned int count, i; - enum lttng_rotation_status status; - const struct lttng_rotation_schedule *ret = NULL; - - status = lttng_rotation_schedules_get_count(schedules, &count); - if (status != LTTNG_ROTATION_STATUS_OK) { - ERR("Unable to determine the number of rotation schedules of session %s", - session_name); - goto end; - } - - for (i = 0; i < count; i++) { - const struct lttng_rotation_schedule *schedule = NULL; - - schedule = lttng_rotation_schedules_get_at_index(schedules, i); - if (!schedule) { - ERR("Unable to retrieve rotation schedule at index %u", - i); - goto end; - } - - if (lttng_rotation_schedule_get_type(schedule) == - schedule_type) { - ret = schedule; - break; - } - } - - if (!ret) { - ERR("No %s rotation schedule active on session %s", - schedule_type_str[schedule_type], session_name); - } -end: - return ret; -} - -static struct lttng_rotation_schedule *create_empty_schedule( - enum lttng_rotation_schedule_type type) -{ - struct lttng_rotation_schedule *schedule = NULL; - - switch (type) { - case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC: - schedule = lttng_rotation_schedule_periodic_create(); - break; - case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD: - schedule = lttng_rotation_schedule_size_threshold_create(); - break; - default: - abort(); - } - return schedule; -} - -static enum cmd_error_code remove_schedule(const char *session_name, - enum lttng_rotation_schedule_type schedule_type) -{ - enum cmd_error_code cmd_ret; - int ret; - const struct lttng_rotation_schedule *schedule = NULL; - struct lttng_rotation_schedules *schedules = NULL; - enum lttng_rotation_status status; - const char *schedule_type_name; - struct lttng_rotation_schedule *empty_schedule = NULL; - - switch (schedule_type) { - case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC: - case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD: - break; - default: - ERR("Unknown schedule type"); - abort(); - } - - schedule_type_name = schedule_type_str[schedule_type]; - - ret = lttng_session_list_rotation_schedules(session_name, &schedules); - if (ret != LTTNG_OK) { - ERR("Failed to list rotation schedules of session %s", - session_name); - goto error; - } - - schedule = get_schedule(session_name, schedules, schedule_type); - if (!schedule) { - cmd_ret = CMD_ERROR; - /* - * get_schedule() logs its own errors. - * A temporaty schedule is created to serialize an MI rotation - * schedule descriptor of the appropriate type that has no - * attributes set. - */ - empty_schedule = create_empty_schedule(schedule_type); - if (!empty_schedule) { - goto error; - } - goto skip_removal; - } - - status = lttng_session_remove_rotation_schedule(session_name, schedule); - switch (status) { - case LTTNG_ROTATION_STATUS_OK: - MSG("Disabled %s rotation on session %s", - schedule_type_name, session_name); - cmd_ret = CMD_SUCCESS; - break; - case LTTNG_ROTATION_STATUS_SCHEDULE_NOT_SET: - ERR("No %s rotation schedule set on session %s", - schedule_type_name, - session_name); - cmd_ret = CMD_ERROR; - break; - case LTTNG_ROTATION_STATUS_ERROR: - case LTTNG_ROTATION_STATUS_INVALID: - default: - ERR("Failed to disable %s rotation schedule on session %s", - schedule_type_name, session_name); - cmd_ret = CMD_ERROR; - break; - } - -skip_removal: - if (lttng_opt_mi) { - ret = mi_lttng_rotation_schedule_result(writer, - schedule ? schedule : empty_schedule, - cmd_ret == CMD_SUCCESS); - if (ret < 0) { - goto error; - } - } - -end: - lttng_rotation_schedules_destroy(schedules); - lttng_rotation_schedule_destroy(empty_schedule); - return cmd_ret; -error: - cmd_ret = CMD_ERROR; - goto end; -} - -/* - * cmd_disable_rotation - * - * The 'disable-rotation ' first level command - */ -int cmd_disable_rotation(int argc, const char **argv) -{ - int popt_ret, opt, ret = 0; - enum cmd_error_code cmd_ret = CMD_SUCCESS; - static poptContext pc; - char *session_name = NULL; - bool free_session_name = false; - bool periodic_rotation = false, size_rotation = false; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - popt_ret = poptReadDefaultConfig(pc, 0); - if (popt_ret) { - cmd_ret = CMD_ERROR; - ERR("poptReadDefaultConfig"); - goto end; - } - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - case OPT_TIMER: - periodic_rotation = true; - break; - case OPT_SIZE: - size_rotation = true; - break; - default: - cmd_ret = CMD_UNDEFINED; - goto end; - } - } - - if (opt_session_name == NULL) { - session_name = get_session_name(); - if (session_name == NULL) { - goto error; - } - free_session_name = true; - } else { - session_name = opt_session_name; - } - - /* Mi check */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - goto error; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_disable_rotation); - if (ret) { - goto error; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - goto error; - } - } - - if (!periodic_rotation && !size_rotation) { - ERR("No session rotation schedule type provided."); - cmd_ret = CMD_ERROR; - goto close_command; - } - - if (lttng_opt_mi) { - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_rotation_schedule_results); - if (ret) { - goto error; - } - - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_session_name, - session_name); - if (ret) { - goto error; - } - } - - if (periodic_rotation) { - /* - * Continue processing even on error as multiple schedules can - * be specified at once. - */ - cmd_ret = remove_schedule(session_name, - LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC); - } - - if (size_rotation) { - enum cmd_error_code tmp_ret; - - /* Don't overwrite cmd_ret if it already indicates an error. */ - tmp_ret = remove_schedule(session_name, - LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD); - cmd_ret = cmd_ret ? cmd_ret : tmp_ret; - } - - if (lttng_opt_mi) { - /* Close rotation schedule results element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto error; - } - } - -close_command: - /* Mi closing */ - if (lttng_opt_mi) { - /* Close output element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto error; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, - cmd_ret == CMD_SUCCESS); - if (ret) { - goto error; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - goto error; - } - } - -end: - (void) mi_lttng_writer_destroy(writer); - poptFreeContext(pc); - if (free_session_name) { - free(session_name); - } - return cmd_ret; -error: - cmd_ret = CMD_ERROR; - goto end; -} diff --git a/src/bin/lttng/commands/disable_rotation.cpp b/src/bin/lttng/commands/disable_rotation.cpp new file mode 100644 index 000000000..8229f63e0 --- /dev/null +++ b/src/bin/lttng/commands/disable_rotation.cpp @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2017 Julien Desfossez + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../command.h" +#include + +static char *opt_session_name; +static struct mi_writer *writer; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, + OPT_TIMER, + OPT_SIZE, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, + {"timer", 0, POPT_ARG_NONE, 0, OPT_TIMER, 0, 0}, + {"size", 0, POPT_ARG_NONE, 0, OPT_SIZE, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +static const char *schedule_type_str[] = { + "periodic", + "size-based", +}; + +static const struct lttng_rotation_schedule *get_schedule( + const char *session_name, + const struct lttng_rotation_schedules *schedules, + enum lttng_rotation_schedule_type schedule_type) +{ + unsigned int count, i; + enum lttng_rotation_status status; + const struct lttng_rotation_schedule *ret = NULL; + + status = lttng_rotation_schedules_get_count(schedules, &count); + if (status != LTTNG_ROTATION_STATUS_OK) { + ERR("Unable to determine the number of rotation schedules of session %s", + session_name); + goto end; + } + + for (i = 0; i < count; i++) { + const struct lttng_rotation_schedule *schedule = NULL; + + schedule = lttng_rotation_schedules_get_at_index(schedules, i); + if (!schedule) { + ERR("Unable to retrieve rotation schedule at index %u", + i); + goto end; + } + + if (lttng_rotation_schedule_get_type(schedule) == + schedule_type) { + ret = schedule; + break; + } + } + + if (!ret) { + ERR("No %s rotation schedule active on session %s", + schedule_type_str[schedule_type], session_name); + } +end: + return ret; +} + +static struct lttng_rotation_schedule *create_empty_schedule( + enum lttng_rotation_schedule_type type) +{ + struct lttng_rotation_schedule *schedule = NULL; + + switch (type) { + case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC: + schedule = lttng_rotation_schedule_periodic_create(); + break; + case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD: + schedule = lttng_rotation_schedule_size_threshold_create(); + break; + default: + abort(); + } + return schedule; +} + +static enum cmd_error_code remove_schedule(const char *session_name, + enum lttng_rotation_schedule_type schedule_type) +{ + enum cmd_error_code cmd_ret; + int ret; + const struct lttng_rotation_schedule *schedule = NULL; + struct lttng_rotation_schedules *schedules = NULL; + enum lttng_rotation_status status; + const char *schedule_type_name; + struct lttng_rotation_schedule *empty_schedule = NULL; + + switch (schedule_type) { + case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC: + case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD: + break; + default: + ERR("Unknown schedule type"); + abort(); + } + + schedule_type_name = schedule_type_str[schedule_type]; + + ret = lttng_session_list_rotation_schedules(session_name, &schedules); + if (ret != LTTNG_OK) { + ERR("Failed to list rotation schedules of session %s", + session_name); + goto error; + } + + schedule = get_schedule(session_name, schedules, schedule_type); + if (!schedule) { + cmd_ret = CMD_ERROR; + /* + * get_schedule() logs its own errors. + * A temporaty schedule is created to serialize an MI rotation + * schedule descriptor of the appropriate type that has no + * attributes set. + */ + empty_schedule = create_empty_schedule(schedule_type); + if (!empty_schedule) { + goto error; + } + goto skip_removal; + } + + status = lttng_session_remove_rotation_schedule(session_name, schedule); + switch (status) { + case LTTNG_ROTATION_STATUS_OK: + MSG("Disabled %s rotation on session %s", + schedule_type_name, session_name); + cmd_ret = CMD_SUCCESS; + break; + case LTTNG_ROTATION_STATUS_SCHEDULE_NOT_SET: + ERR("No %s rotation schedule set on session %s", + schedule_type_name, + session_name); + cmd_ret = CMD_ERROR; + break; + case LTTNG_ROTATION_STATUS_ERROR: + case LTTNG_ROTATION_STATUS_INVALID: + default: + ERR("Failed to disable %s rotation schedule on session %s", + schedule_type_name, session_name); + cmd_ret = CMD_ERROR; + break; + } + +skip_removal: + if (lttng_opt_mi) { + ret = mi_lttng_rotation_schedule_result(writer, + schedule ? schedule : empty_schedule, + cmd_ret == CMD_SUCCESS); + if (ret < 0) { + goto error; + } + } + +end: + lttng_rotation_schedules_destroy(schedules); + lttng_rotation_schedule_destroy(empty_schedule); + return cmd_ret; +error: + cmd_ret = CMD_ERROR; + goto end; +} + +/* + * cmd_disable_rotation + * + * The 'disable-rotation ' first level command + */ +int cmd_disable_rotation(int argc, const char **argv) +{ + int popt_ret, opt, ret = 0; + enum cmd_error_code cmd_ret = CMD_SUCCESS; + static poptContext pc; + char *session_name = NULL; + bool free_session_name = false; + bool periodic_rotation = false, size_rotation = false; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + popt_ret = poptReadDefaultConfig(pc, 0); + if (popt_ret) { + cmd_ret = CMD_ERROR; + ERR("poptReadDefaultConfig"); + goto end; + } + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + case OPT_TIMER: + periodic_rotation = true; + break; + case OPT_SIZE: + size_rotation = true; + break; + default: + cmd_ret = CMD_UNDEFINED; + goto end; + } + } + + if (opt_session_name == NULL) { + session_name = get_session_name(); + if (session_name == NULL) { + goto error; + } + free_session_name = true; + } else { + session_name = opt_session_name; + } + + /* Mi check */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + goto error; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_disable_rotation); + if (ret) { + goto error; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + goto error; + } + } + + if (!periodic_rotation && !size_rotation) { + ERR("No session rotation schedule type provided."); + cmd_ret = CMD_ERROR; + goto close_command; + } + + if (lttng_opt_mi) { + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_rotation_schedule_results); + if (ret) { + goto error; + } + + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_session_name, + session_name); + if (ret) { + goto error; + } + } + + if (periodic_rotation) { + /* + * Continue processing even on error as multiple schedules can + * be specified at once. + */ + cmd_ret = remove_schedule(session_name, + LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC); + } + + if (size_rotation) { + enum cmd_error_code tmp_ret; + + /* Don't overwrite cmd_ret if it already indicates an error. */ + tmp_ret = remove_schedule(session_name, + LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD); + cmd_ret = cmd_ret ? cmd_ret : tmp_ret; + } + + if (lttng_opt_mi) { + /* Close rotation schedule results element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto error; + } + } + +close_command: + /* Mi closing */ + if (lttng_opt_mi) { + /* Close output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto error; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, + cmd_ret == CMD_SUCCESS); + if (ret) { + goto error; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + goto error; + } + } + +end: + (void) mi_lttng_writer_destroy(writer); + poptFreeContext(pc); + if (free_session_name) { + free(session_name); + } + return cmd_ret; +error: + cmd_ret = CMD_ERROR; + goto end; +} diff --git a/src/bin/lttng/commands/enable_channels.c b/src/bin/lttng/commands/enable_channels.c deleted file mode 100644 index 194ec541e..000000000 --- a/src/bin/lttng/commands/enable_channels.c +++ /dev/null @@ -1,759 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "../command.h" -#include "../utils.h" - - -static struct lttng_channel chan_opts; -static char *opt_channels; -static int opt_kernel; -static char *opt_session_name; -static int opt_userspace; -static char *opt_output; -static int opt_buffer_uid; -static int opt_buffer_pid; -static int opt_buffer_global; -static struct { - bool set; - uint64_t interval; -} opt_monitor_timer; -static struct { - bool set; - int64_t value; -} opt_blocking_timeout; - -static struct mi_writer *writer; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_DISCARD, - OPT_OVERWRITE, - OPT_SUBBUF_SIZE, - OPT_NUM_SUBBUF, - OPT_SWITCH_TIMER, - OPT_MONITOR_TIMER, - OPT_READ_TIMER, - OPT_USERSPACE, - OPT_LIST_OPTIONS, - OPT_TRACEFILE_SIZE, - OPT_TRACEFILE_COUNT, - OPT_BLOCKING_TIMEOUT, -}; - -static struct lttng_handle *handle; - -const char *output_mmap = "mmap"; -const char *output_splice = "splice"; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, - {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, - {"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0}, - {"discard", 0, POPT_ARG_NONE, 0, OPT_DISCARD, 0, 0}, - {"overwrite", 0, POPT_ARG_NONE, 0, OPT_OVERWRITE, 0, 0}, - {"subbuf-size", 0, POPT_ARG_STRING, 0, OPT_SUBBUF_SIZE, 0, 0}, - {"num-subbuf", 0, POPT_ARG_INT, 0, OPT_NUM_SUBBUF, 0, 0}, - {"switch-timer", 0, POPT_ARG_INT, 0, OPT_SWITCH_TIMER, 0, 0}, - {"monitor-timer", 0, POPT_ARG_INT, 0, OPT_MONITOR_TIMER, 0, 0}, - {"read-timer", 0, POPT_ARG_INT, 0, OPT_READ_TIMER, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {"output", 0, POPT_ARG_STRING, &opt_output, 0, 0, 0}, - {"buffers-uid", 0, POPT_ARG_VAL, &opt_buffer_uid, 1, 0, 0}, - {"buffers-pid", 0, POPT_ARG_VAL, &opt_buffer_pid, 1, 0, 0}, - {"buffers-global", 0, POPT_ARG_VAL, &opt_buffer_global, 1, 0, 0}, - {"tracefile-size", 'C', POPT_ARG_INT, 0, OPT_TRACEFILE_SIZE, 0, 0}, - {"tracefile-count", 'W', POPT_ARG_INT, 0, OPT_TRACEFILE_COUNT, 0, 0}, - {"blocking-timeout", 0, POPT_ARG_INT, 0, OPT_BLOCKING_TIMEOUT, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * Set default attributes depending on those already defined from the command - * line. - */ -static void set_default_attr(struct lttng_domain *dom) -{ - struct lttng_channel_attr default_attr; - - memset(&default_attr, 0, sizeof(default_attr)); - - /* Set attributes */ - lttng_channel_set_default_attr(dom, &default_attr); - - if (chan_opts.attr.overwrite == -1) { - chan_opts.attr.overwrite = default_attr.overwrite; - } - if (chan_opts.attr.subbuf_size == -1) { - chan_opts.attr.subbuf_size = default_attr.subbuf_size; - } - if (chan_opts.attr.num_subbuf == -1) { - chan_opts.attr.num_subbuf = default_attr.num_subbuf; - } - if (chan_opts.attr.switch_timer_interval == -1) { - chan_opts.attr.switch_timer_interval = default_attr.switch_timer_interval; - } - if (chan_opts.attr.read_timer_interval == -1) { - chan_opts.attr.read_timer_interval = default_attr.read_timer_interval; - } - if ((int) chan_opts.attr.output == -1) { - chan_opts.attr.output = default_attr.output; - } - if (chan_opts.attr.tracefile_count == -1) { - chan_opts.attr.tracefile_count = default_attr.tracefile_count; - } - if (chan_opts.attr.tracefile_size == -1) { - chan_opts.attr.tracefile_size = default_attr.tracefile_size; - } -} - -/* - * Adding channel using the lttng API. - */ -static int enable_channel(char *session_name) -{ - struct lttng_channel *channel = NULL; - int ret = CMD_SUCCESS, warn = 0, error = 0, success = 0; - char *channel_name; - struct lttng_domain dom; - - memset(&dom, 0, sizeof(dom)); - - /* Validate options. */ - if (opt_kernel) { - if (opt_blocking_timeout.set) { - ERR("Retry timeout option not supported for kernel domain (-k)"); - ret = CMD_ERROR; - goto error; - } - } - - /* Create lttng domain */ - if (opt_kernel) { - dom.type = LTTNG_DOMAIN_KERNEL; - dom.buf_type = LTTNG_BUFFER_GLOBAL; - if (opt_buffer_uid || opt_buffer_pid) { - ERR("Buffer type not supported for domain -k"); - ret = CMD_ERROR; - goto error; - } - } else if (opt_userspace) { - dom.type = LTTNG_DOMAIN_UST; - if (opt_buffer_pid) { - dom.buf_type = LTTNG_BUFFER_PER_PID; - } else { - if (opt_buffer_global) { - ERR("Buffer type not supported for domain -u"); - ret = CMD_ERROR; - goto error; - } - dom.buf_type = LTTNG_BUFFER_PER_UID; - } - } else { - /* Checked by the caller. */ - abort(); - } - - set_default_attr(&dom); - - if (chan_opts.attr.tracefile_size == 0 && chan_opts.attr.tracefile_count) { - ERR("Missing option --tracefile-size. " - "A file count without a size won't do anything."); - ret = CMD_ERROR; - goto error; - } - - if ((chan_opts.attr.tracefile_size > 0) && - (chan_opts.attr.tracefile_size < chan_opts.attr.subbuf_size)) { - WARN("Tracefile size rounded up from (%" PRIu64 ") to subbuffer size (%" PRIu64 ")", - chan_opts.attr.tracefile_size, chan_opts.attr.subbuf_size); - chan_opts.attr.tracefile_size = chan_opts.attr.subbuf_size; - } - - /* Setting channel output */ - if (opt_output) { - if (!strncmp(output_mmap, opt_output, strlen(output_mmap))) { - chan_opts.attr.output = LTTNG_EVENT_MMAP; - } else if (!strncmp(output_splice, opt_output, strlen(output_splice))) { - chan_opts.attr.output = LTTNG_EVENT_SPLICE; - } else { - ERR("Unknown output type %s. Possible values are: %s, %s\n", - opt_output, output_mmap, output_splice); - ret = CMD_ERROR; - goto error; - } - } - - handle = lttng_create_handle(session_name, &dom); - if (handle == NULL) { - ret = -1; - goto error; - } - - /* Mi open channels element */ - if (lttng_opt_mi) { - LTTNG_ASSERT(writer); - ret = mi_lttng_channels_open(writer); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - - /* Strip channel list (format: chan1,chan2,...) */ - channel_name = strtok(opt_channels, ","); - while (channel_name != NULL) { - void *extended_ptr; - - /* Validate channel name's length */ - if (strlen(channel_name) >= sizeof(chan_opts.name)) { - ERR("Channel name is too long (max. %zu characters)", - sizeof(chan_opts.name) - 1); - error = 1; - goto skip_enable; - } - - /* - * A dynamically-allocated channel is used in order to allow - * the configuration of extended attributes (post-2.9). - */ - channel = lttng_channel_create(&dom); - if (!channel) { - ERR("Unable to create channel object"); - error = 1; - goto error; - } - - /* Copy channel name */ - strcpy(channel->name, channel_name); - channel->enabled = 1; - extended_ptr = channel->attr.extended.ptr; - memcpy(&channel->attr, &chan_opts.attr, sizeof(chan_opts.attr)); - channel->attr.extended.ptr = extended_ptr; - if (opt_monitor_timer.set) { - ret = lttng_channel_set_monitor_timer_interval(channel, - opt_monitor_timer.interval); - if (ret) { - ERR("Failed to set the channel's monitor timer interval"); - error = 1; - goto error; - } - } - if (opt_blocking_timeout.set) { - ret = lttng_channel_set_blocking_timeout(channel, - opt_blocking_timeout.value); - if (ret) { - ERR("Failed to set the channel's blocking timeout"); - error = 1; - goto error; - } - } - - DBG("Enabling channel %s", channel_name); - - ret = lttng_enable_channel(handle, channel); - if (ret < 0) { - success = 0; - switch (-ret) { - case LTTNG_ERR_KERN_CHAN_EXIST: - case LTTNG_ERR_UST_CHAN_EXIST: - case LTTNG_ERR_CHAN_EXIST: - WARN("Channel %s: %s (session %s)", channel_name, - lttng_strerror(ret), session_name); - warn = 1; - break; - case LTTNG_ERR_INVALID_CHANNEL_NAME: - ERR("Invalid channel name: \"%s\". " - "Channel names may not start with '.', and " - "may not contain '/'.", channel_name); - error = 1; - break; - default: - ERR("Channel %s: %s (session %s)", channel_name, - lttng_strerror(ret), session_name); - error = 1; - break; - } - } else { - MSG("%s channel %s enabled for session %s", - lttng_domain_type_str(dom.type), - channel_name, session_name); - success = 1; - } - -skip_enable: - if (lttng_opt_mi) { - /* Mi print the channel element and leave it open */ - ret = mi_lttng_channel(writer, channel, 1); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - /* Individual Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - /* Close channel element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - - /* Next channel */ - channel_name = strtok(NULL, ","); - lttng_channel_destroy(channel); - channel = NULL; - } - - if (lttng_opt_mi) { - /* Close channels element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - - ret = CMD_SUCCESS; - -error: - if (channel) { - lttng_channel_destroy(channel); - } - /* If more important error happen bypass the warning */ - if (!ret && warn) { - ret = CMD_WARNING; - } - /* If more important error happen bypass the warning */ - if (!ret && error) { - ret = CMD_ERROR; - } - - lttng_destroy_handle(handle); - - return ret; -} - -/* - * Default value for channel configuration. - */ -static void init_channel_config(void) -{ - /* - * Put -1 everywhere so we can identify those set by the command line and - * those needed to be set by the default values. - */ - memset(&chan_opts.attr, -1, sizeof(chan_opts.attr)); - chan_opts.attr.extended.ptr = NULL; -} - -/* - * Add channel to trace session - */ -int cmd_enable_channels(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; - static poptContext pc; - char *session_name = NULL; - char *opt_arg = NULL; - const char *leftover = NULL; - - init_channel_config(); - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_DISCARD: - chan_opts.attr.overwrite = 0; - DBG("Channel set to discard"); - break; - case OPT_OVERWRITE: - chan_opts.attr.overwrite = 1; - DBG("Channel set to overwrite"); - break; - case OPT_SUBBUF_SIZE: - { - uint64_t rounded_size; - int order; - - /* Parse the size */ - opt_arg = poptGetOptArg(pc); - if (utils_parse_size_suffix(opt_arg, &chan_opts.attr.subbuf_size) < 0 || !chan_opts.attr.subbuf_size) { - ERR("Wrong value in --subbuf-size parameter: %s", opt_arg); - ret = CMD_ERROR; - goto end; - } - - order = get_count_order_u64(chan_opts.attr.subbuf_size); - LTTNG_ASSERT(order >= 0); - rounded_size = 1ULL << order; - if (rounded_size < chan_opts.attr.subbuf_size) { - ERR("The subbuf size (%" PRIu64 ") is rounded and overflows!", - chan_opts.attr.subbuf_size); - ret = CMD_ERROR; - goto end; - } - - if (rounded_size != chan_opts.attr.subbuf_size) { - WARN("The subbuf size (%" PRIu64 ") is rounded to the next power of 2 (%" PRIu64 ")", - chan_opts.attr.subbuf_size, rounded_size); - chan_opts.attr.subbuf_size = rounded_size; - } - - /* Should now be power of 2 */ - LTTNG_ASSERT(!((chan_opts.attr.subbuf_size - 1) & chan_opts.attr.subbuf_size)); - - DBG("Channel subbuf size set to %" PRIu64, chan_opts.attr.subbuf_size); - break; - } - case OPT_NUM_SUBBUF: - { - uint64_t rounded_size; - int order; - - errno = 0; - opt_arg = poptGetOptArg(pc); - chan_opts.attr.num_subbuf = strtoull(opt_arg, NULL, 0); - if (errno != 0 || !chan_opts.attr.num_subbuf || !isdigit(opt_arg[0])) { - ERR("Wrong value in --num-subbuf parameter: %s", opt_arg); - ret = CMD_ERROR; - goto end; - } - - order = get_count_order_u64(chan_opts.attr.num_subbuf); - LTTNG_ASSERT(order >= 0); - rounded_size = 1ULL << order; - if (rounded_size < chan_opts.attr.num_subbuf) { - ERR("The number of subbuffers (%" PRIu64 ") is rounded and overflows!", - chan_opts.attr.num_subbuf); - ret = CMD_ERROR; - goto end; - } - - if (rounded_size != chan_opts.attr.num_subbuf) { - WARN("The number of subbuffers (%" PRIu64 ") is rounded to the next power of 2 (%" PRIu64 ")", - chan_opts.attr.num_subbuf, rounded_size); - chan_opts.attr.num_subbuf = rounded_size; - } - - /* Should now be power of 2 */ - LTTNG_ASSERT(!((chan_opts.attr.num_subbuf - 1) & chan_opts.attr.num_subbuf)); - - DBG("Channel subbuf num set to %" PRIu64, chan_opts.attr.num_subbuf); - break; - } - case OPT_SWITCH_TIMER: - { - uint64_t v; - - errno = 0; - opt_arg = poptGetOptArg(pc); - - if (utils_parse_time_suffix(opt_arg, &v) < 0) { - ERR("Wrong value for --switch-timer parameter: %s", opt_arg); - ret = CMD_ERROR; - goto end; - } - - if (v != (uint32_t) v) { - ERR("32-bit overflow in --switch-timer parameter: %s", opt_arg); - ret = CMD_ERROR; - goto end; - } - chan_opts.attr.switch_timer_interval = (uint32_t) v; - DBG("Channel switch timer interval set to %d %s", - chan_opts.attr.switch_timer_interval, - USEC_UNIT); - break; - } - case OPT_READ_TIMER: - { - uint64_t v; - - errno = 0; - opt_arg = poptGetOptArg(pc); - - if (utils_parse_time_suffix(opt_arg, &v) < 0) { - ERR("Wrong value for --read-timer parameter: %s", opt_arg); - ret = CMD_ERROR; - goto end; - } - - if (v != (uint32_t) v) { - ERR("32-bit overflow in --read-timer parameter: %s", opt_arg); - ret = CMD_ERROR; - goto end; - } - chan_opts.attr.read_timer_interval = (uint32_t) v; - DBG("Channel read timer interval set to %d %s", - chan_opts.attr.read_timer_interval, - USEC_UNIT); - break; - } - case OPT_MONITOR_TIMER: - { - uint64_t v; - - errno = 0; - opt_arg = poptGetOptArg(pc); - - if (utils_parse_time_suffix(opt_arg, &v) < 0) { - ERR("Wrong value for --monitor-timer parameter: %s", opt_arg); - ret = CMD_ERROR; - goto end; - } - opt_monitor_timer.interval = (uint64_t) v; - opt_monitor_timer.set = true; - DBG("Channel monitor timer interval set to %" PRIu64 " %s", - opt_monitor_timer.interval, - USEC_UNIT); - break; - } - case OPT_BLOCKING_TIMEOUT: - { - uint64_t v; - long long v_msec; - - errno = 0; - opt_arg = poptGetOptArg(pc); - - if (strcmp(opt_arg, "inf") == 0) { - opt_blocking_timeout.value = (int64_t) -1; - opt_blocking_timeout.set = true; - DBG("Channel blocking timeout set to infinity"); - break; - } - - if (utils_parse_time_suffix(opt_arg, &v) < 0) { - ERR("Wrong value for --blocking-timeout parameter: %s", opt_arg); - ret = CMD_ERROR; - goto end; - } - - /* - * While LTTng-UST and LTTng-tools will accept a - * blocking timeout expressed in µs, the current - * tracer implementation relies on poll() which - * takes an "int timeout" parameter expressed in - * msec. - * - * Since the error reporting from the tracer is - * not precise, we perform this check here to - * provide a helpful error message in case of - * overflow. - * - * The setter (liblttng-ctl) also performs an - * equivalent check. - */ - v_msec = v / 1000; - if (v_msec != (int32_t) v_msec) { - ERR("32-bit milliseconds overflow in --blocking-timeout parameter: %s", opt_arg); - ret = CMD_ERROR; - goto end; - } - - opt_blocking_timeout.value = (int64_t) v; - opt_blocking_timeout.set = true; - DBG("Channel blocking timeout set to %" PRId64 " %s%s", - opt_blocking_timeout.value, - USEC_UNIT, - opt_blocking_timeout.value == 0 ? - " (non-blocking)" : ""); - break; - } - case OPT_USERSPACE: - opt_userspace = 1; - break; - case OPT_TRACEFILE_SIZE: - opt_arg = poptGetOptArg(pc); - if (utils_parse_size_suffix(opt_arg, &chan_opts.attr.tracefile_size) < 0) { - ERR("Wrong value in --tracefile-size parameter: %s", opt_arg); - ret = CMD_ERROR; - goto end; - } - DBG("Maximum tracefile size set to %" PRIu64, - chan_opts.attr.tracefile_size); - break; - case OPT_TRACEFILE_COUNT: - { - unsigned long v; - - errno = 0; - opt_arg = poptGetOptArg(pc); - v = strtoul(opt_arg, NULL, 0); - if (errno != 0 || !isdigit(opt_arg[0])) { - ERR("Wrong value in --tracefile-count parameter: %s", opt_arg); - ret = CMD_ERROR; - goto end; - } - if (v != (uint32_t) v) { - ERR("32-bit overflow in --tracefile-count parameter: %s", opt_arg); - ret = CMD_ERROR; - goto end; - } - chan_opts.attr.tracefile_count = (uint32_t) v; - DBG("Maximum tracefile count set to %" PRIu64, - chan_opts.attr.tracefile_count); - break; - } - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - default: - ret = CMD_UNDEFINED; - goto end; - } - } - - ret = print_missing_or_multiple_domains( - opt_kernel + opt_userspace, false); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - if (chan_opts.attr.overwrite == 1 && opt_blocking_timeout.set && - opt_blocking_timeout.value != 0) { - ERR("You cannot specify --overwrite and --blocking-timeout=N, " - "where N is different than 0"); - ret = CMD_ERROR; - goto end; - } - - /* Mi check */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_enable_channels); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - opt_channels = (char*) poptGetArg(pc); - if (opt_channels == NULL) { - ERR("Missing channel name.\n"); - ret = CMD_ERROR; - success = 0; - goto mi_closing; - } - - leftover = poptGetArg(pc); - if (leftover) { - ERR("Unknown argument: %s", leftover); - ret = CMD_ERROR; - success = 0; - goto mi_closing; - } - - if (!opt_session_name) { - session_name = get_session_name(); - if (session_name == NULL) { - command_ret = CMD_ERROR; - success = 0; - goto mi_closing; - } - } else { - session_name = opt_session_name; - } - - command_ret = enable_channel(session_name); - if (command_ret) { - success = 0; - } - -mi_closing: - /* Mi closing */ - if (lttng_opt_mi) { - /* Close output element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - goto end; - } - } - -end: - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - ret = ret ? ret : LTTNG_ERR_MI_IO_FAIL; - } - - if (!opt_session_name && session_name) { - free(session_name); - } - - /* Overwrite ret if an error occurred when enable_channel */ - ret = command_ret ? command_ret : ret; - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/enable_channels.cpp b/src/bin/lttng/commands/enable_channels.cpp new file mode 100644 index 000000000..194ec541e --- /dev/null +++ b/src/bin/lttng/commands/enable_channels.cpp @@ -0,0 +1,759 @@ +/* + * Copyright (C) 2011 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "../command.h" +#include "../utils.h" + + +static struct lttng_channel chan_opts; +static char *opt_channels; +static int opt_kernel; +static char *opt_session_name; +static int opt_userspace; +static char *opt_output; +static int opt_buffer_uid; +static int opt_buffer_pid; +static int opt_buffer_global; +static struct { + bool set; + uint64_t interval; +} opt_monitor_timer; +static struct { + bool set; + int64_t value; +} opt_blocking_timeout; + +static struct mi_writer *writer; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_DISCARD, + OPT_OVERWRITE, + OPT_SUBBUF_SIZE, + OPT_NUM_SUBBUF, + OPT_SWITCH_TIMER, + OPT_MONITOR_TIMER, + OPT_READ_TIMER, + OPT_USERSPACE, + OPT_LIST_OPTIONS, + OPT_TRACEFILE_SIZE, + OPT_TRACEFILE_COUNT, + OPT_BLOCKING_TIMEOUT, +}; + +static struct lttng_handle *handle; + +const char *output_mmap = "mmap"; +const char *output_splice = "splice"; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, + {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, + {"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0}, + {"discard", 0, POPT_ARG_NONE, 0, OPT_DISCARD, 0, 0}, + {"overwrite", 0, POPT_ARG_NONE, 0, OPT_OVERWRITE, 0, 0}, + {"subbuf-size", 0, POPT_ARG_STRING, 0, OPT_SUBBUF_SIZE, 0, 0}, + {"num-subbuf", 0, POPT_ARG_INT, 0, OPT_NUM_SUBBUF, 0, 0}, + {"switch-timer", 0, POPT_ARG_INT, 0, OPT_SWITCH_TIMER, 0, 0}, + {"monitor-timer", 0, POPT_ARG_INT, 0, OPT_MONITOR_TIMER, 0, 0}, + {"read-timer", 0, POPT_ARG_INT, 0, OPT_READ_TIMER, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {"output", 0, POPT_ARG_STRING, &opt_output, 0, 0, 0}, + {"buffers-uid", 0, POPT_ARG_VAL, &opt_buffer_uid, 1, 0, 0}, + {"buffers-pid", 0, POPT_ARG_VAL, &opt_buffer_pid, 1, 0, 0}, + {"buffers-global", 0, POPT_ARG_VAL, &opt_buffer_global, 1, 0, 0}, + {"tracefile-size", 'C', POPT_ARG_INT, 0, OPT_TRACEFILE_SIZE, 0, 0}, + {"tracefile-count", 'W', POPT_ARG_INT, 0, OPT_TRACEFILE_COUNT, 0, 0}, + {"blocking-timeout", 0, POPT_ARG_INT, 0, OPT_BLOCKING_TIMEOUT, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * Set default attributes depending on those already defined from the command + * line. + */ +static void set_default_attr(struct lttng_domain *dom) +{ + struct lttng_channel_attr default_attr; + + memset(&default_attr, 0, sizeof(default_attr)); + + /* Set attributes */ + lttng_channel_set_default_attr(dom, &default_attr); + + if (chan_opts.attr.overwrite == -1) { + chan_opts.attr.overwrite = default_attr.overwrite; + } + if (chan_opts.attr.subbuf_size == -1) { + chan_opts.attr.subbuf_size = default_attr.subbuf_size; + } + if (chan_opts.attr.num_subbuf == -1) { + chan_opts.attr.num_subbuf = default_attr.num_subbuf; + } + if (chan_opts.attr.switch_timer_interval == -1) { + chan_opts.attr.switch_timer_interval = default_attr.switch_timer_interval; + } + if (chan_opts.attr.read_timer_interval == -1) { + chan_opts.attr.read_timer_interval = default_attr.read_timer_interval; + } + if ((int) chan_opts.attr.output == -1) { + chan_opts.attr.output = default_attr.output; + } + if (chan_opts.attr.tracefile_count == -1) { + chan_opts.attr.tracefile_count = default_attr.tracefile_count; + } + if (chan_opts.attr.tracefile_size == -1) { + chan_opts.attr.tracefile_size = default_attr.tracefile_size; + } +} + +/* + * Adding channel using the lttng API. + */ +static int enable_channel(char *session_name) +{ + struct lttng_channel *channel = NULL; + int ret = CMD_SUCCESS, warn = 0, error = 0, success = 0; + char *channel_name; + struct lttng_domain dom; + + memset(&dom, 0, sizeof(dom)); + + /* Validate options. */ + if (opt_kernel) { + if (opt_blocking_timeout.set) { + ERR("Retry timeout option not supported for kernel domain (-k)"); + ret = CMD_ERROR; + goto error; + } + } + + /* Create lttng domain */ + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + dom.buf_type = LTTNG_BUFFER_GLOBAL; + if (opt_buffer_uid || opt_buffer_pid) { + ERR("Buffer type not supported for domain -k"); + ret = CMD_ERROR; + goto error; + } + } else if (opt_userspace) { + dom.type = LTTNG_DOMAIN_UST; + if (opt_buffer_pid) { + dom.buf_type = LTTNG_BUFFER_PER_PID; + } else { + if (opt_buffer_global) { + ERR("Buffer type not supported for domain -u"); + ret = CMD_ERROR; + goto error; + } + dom.buf_type = LTTNG_BUFFER_PER_UID; + } + } else { + /* Checked by the caller. */ + abort(); + } + + set_default_attr(&dom); + + if (chan_opts.attr.tracefile_size == 0 && chan_opts.attr.tracefile_count) { + ERR("Missing option --tracefile-size. " + "A file count without a size won't do anything."); + ret = CMD_ERROR; + goto error; + } + + if ((chan_opts.attr.tracefile_size > 0) && + (chan_opts.attr.tracefile_size < chan_opts.attr.subbuf_size)) { + WARN("Tracefile size rounded up from (%" PRIu64 ") to subbuffer size (%" PRIu64 ")", + chan_opts.attr.tracefile_size, chan_opts.attr.subbuf_size); + chan_opts.attr.tracefile_size = chan_opts.attr.subbuf_size; + } + + /* Setting channel output */ + if (opt_output) { + if (!strncmp(output_mmap, opt_output, strlen(output_mmap))) { + chan_opts.attr.output = LTTNG_EVENT_MMAP; + } else if (!strncmp(output_splice, opt_output, strlen(output_splice))) { + chan_opts.attr.output = LTTNG_EVENT_SPLICE; + } else { + ERR("Unknown output type %s. Possible values are: %s, %s\n", + opt_output, output_mmap, output_splice); + ret = CMD_ERROR; + goto error; + } + } + + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = -1; + goto error; + } + + /* Mi open channels element */ + if (lttng_opt_mi) { + LTTNG_ASSERT(writer); + ret = mi_lttng_channels_open(writer); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + + /* Strip channel list (format: chan1,chan2,...) */ + channel_name = strtok(opt_channels, ","); + while (channel_name != NULL) { + void *extended_ptr; + + /* Validate channel name's length */ + if (strlen(channel_name) >= sizeof(chan_opts.name)) { + ERR("Channel name is too long (max. %zu characters)", + sizeof(chan_opts.name) - 1); + error = 1; + goto skip_enable; + } + + /* + * A dynamically-allocated channel is used in order to allow + * the configuration of extended attributes (post-2.9). + */ + channel = lttng_channel_create(&dom); + if (!channel) { + ERR("Unable to create channel object"); + error = 1; + goto error; + } + + /* Copy channel name */ + strcpy(channel->name, channel_name); + channel->enabled = 1; + extended_ptr = channel->attr.extended.ptr; + memcpy(&channel->attr, &chan_opts.attr, sizeof(chan_opts.attr)); + channel->attr.extended.ptr = extended_ptr; + if (opt_monitor_timer.set) { + ret = lttng_channel_set_monitor_timer_interval(channel, + opt_monitor_timer.interval); + if (ret) { + ERR("Failed to set the channel's monitor timer interval"); + error = 1; + goto error; + } + } + if (opt_blocking_timeout.set) { + ret = lttng_channel_set_blocking_timeout(channel, + opt_blocking_timeout.value); + if (ret) { + ERR("Failed to set the channel's blocking timeout"); + error = 1; + goto error; + } + } + + DBG("Enabling channel %s", channel_name); + + ret = lttng_enable_channel(handle, channel); + if (ret < 0) { + success = 0; + switch (-ret) { + case LTTNG_ERR_KERN_CHAN_EXIST: + case LTTNG_ERR_UST_CHAN_EXIST: + case LTTNG_ERR_CHAN_EXIST: + WARN("Channel %s: %s (session %s)", channel_name, + lttng_strerror(ret), session_name); + warn = 1; + break; + case LTTNG_ERR_INVALID_CHANNEL_NAME: + ERR("Invalid channel name: \"%s\". " + "Channel names may not start with '.', and " + "may not contain '/'.", channel_name); + error = 1; + break; + default: + ERR("Channel %s: %s (session %s)", channel_name, + lttng_strerror(ret), session_name); + error = 1; + break; + } + } else { + MSG("%s channel %s enabled for session %s", + lttng_domain_type_str(dom.type), + channel_name, session_name); + success = 1; + } + +skip_enable: + if (lttng_opt_mi) { + /* Mi print the channel element and leave it open */ + ret = mi_lttng_channel(writer, channel, 1); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* Individual Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* Close channel element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + + /* Next channel */ + channel_name = strtok(NULL, ","); + lttng_channel_destroy(channel); + channel = NULL; + } + + if (lttng_opt_mi) { + /* Close channels element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + + ret = CMD_SUCCESS; + +error: + if (channel) { + lttng_channel_destroy(channel); + } + /* If more important error happen bypass the warning */ + if (!ret && warn) { + ret = CMD_WARNING; + } + /* If more important error happen bypass the warning */ + if (!ret && error) { + ret = CMD_ERROR; + } + + lttng_destroy_handle(handle); + + return ret; +} + +/* + * Default value for channel configuration. + */ +static void init_channel_config(void) +{ + /* + * Put -1 everywhere so we can identify those set by the command line and + * those needed to be set by the default values. + */ + memset(&chan_opts.attr, -1, sizeof(chan_opts.attr)); + chan_opts.attr.extended.ptr = NULL; +} + +/* + * Add channel to trace session + */ +int cmd_enable_channels(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; + static poptContext pc; + char *session_name = NULL; + char *opt_arg = NULL; + const char *leftover = NULL; + + init_channel_config(); + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_DISCARD: + chan_opts.attr.overwrite = 0; + DBG("Channel set to discard"); + break; + case OPT_OVERWRITE: + chan_opts.attr.overwrite = 1; + DBG("Channel set to overwrite"); + break; + case OPT_SUBBUF_SIZE: + { + uint64_t rounded_size; + int order; + + /* Parse the size */ + opt_arg = poptGetOptArg(pc); + if (utils_parse_size_suffix(opt_arg, &chan_opts.attr.subbuf_size) < 0 || !chan_opts.attr.subbuf_size) { + ERR("Wrong value in --subbuf-size parameter: %s", opt_arg); + ret = CMD_ERROR; + goto end; + } + + order = get_count_order_u64(chan_opts.attr.subbuf_size); + LTTNG_ASSERT(order >= 0); + rounded_size = 1ULL << order; + if (rounded_size < chan_opts.attr.subbuf_size) { + ERR("The subbuf size (%" PRIu64 ") is rounded and overflows!", + chan_opts.attr.subbuf_size); + ret = CMD_ERROR; + goto end; + } + + if (rounded_size != chan_opts.attr.subbuf_size) { + WARN("The subbuf size (%" PRIu64 ") is rounded to the next power of 2 (%" PRIu64 ")", + chan_opts.attr.subbuf_size, rounded_size); + chan_opts.attr.subbuf_size = rounded_size; + } + + /* Should now be power of 2 */ + LTTNG_ASSERT(!((chan_opts.attr.subbuf_size - 1) & chan_opts.attr.subbuf_size)); + + DBG("Channel subbuf size set to %" PRIu64, chan_opts.attr.subbuf_size); + break; + } + case OPT_NUM_SUBBUF: + { + uint64_t rounded_size; + int order; + + errno = 0; + opt_arg = poptGetOptArg(pc); + chan_opts.attr.num_subbuf = strtoull(opt_arg, NULL, 0); + if (errno != 0 || !chan_opts.attr.num_subbuf || !isdigit(opt_arg[0])) { + ERR("Wrong value in --num-subbuf parameter: %s", opt_arg); + ret = CMD_ERROR; + goto end; + } + + order = get_count_order_u64(chan_opts.attr.num_subbuf); + LTTNG_ASSERT(order >= 0); + rounded_size = 1ULL << order; + if (rounded_size < chan_opts.attr.num_subbuf) { + ERR("The number of subbuffers (%" PRIu64 ") is rounded and overflows!", + chan_opts.attr.num_subbuf); + ret = CMD_ERROR; + goto end; + } + + if (rounded_size != chan_opts.attr.num_subbuf) { + WARN("The number of subbuffers (%" PRIu64 ") is rounded to the next power of 2 (%" PRIu64 ")", + chan_opts.attr.num_subbuf, rounded_size); + chan_opts.attr.num_subbuf = rounded_size; + } + + /* Should now be power of 2 */ + LTTNG_ASSERT(!((chan_opts.attr.num_subbuf - 1) & chan_opts.attr.num_subbuf)); + + DBG("Channel subbuf num set to %" PRIu64, chan_opts.attr.num_subbuf); + break; + } + case OPT_SWITCH_TIMER: + { + uint64_t v; + + errno = 0; + opt_arg = poptGetOptArg(pc); + + if (utils_parse_time_suffix(opt_arg, &v) < 0) { + ERR("Wrong value for --switch-timer parameter: %s", opt_arg); + ret = CMD_ERROR; + goto end; + } + + if (v != (uint32_t) v) { + ERR("32-bit overflow in --switch-timer parameter: %s", opt_arg); + ret = CMD_ERROR; + goto end; + } + chan_opts.attr.switch_timer_interval = (uint32_t) v; + DBG("Channel switch timer interval set to %d %s", + chan_opts.attr.switch_timer_interval, + USEC_UNIT); + break; + } + case OPT_READ_TIMER: + { + uint64_t v; + + errno = 0; + opt_arg = poptGetOptArg(pc); + + if (utils_parse_time_suffix(opt_arg, &v) < 0) { + ERR("Wrong value for --read-timer parameter: %s", opt_arg); + ret = CMD_ERROR; + goto end; + } + + if (v != (uint32_t) v) { + ERR("32-bit overflow in --read-timer parameter: %s", opt_arg); + ret = CMD_ERROR; + goto end; + } + chan_opts.attr.read_timer_interval = (uint32_t) v; + DBG("Channel read timer interval set to %d %s", + chan_opts.attr.read_timer_interval, + USEC_UNIT); + break; + } + case OPT_MONITOR_TIMER: + { + uint64_t v; + + errno = 0; + opt_arg = poptGetOptArg(pc); + + if (utils_parse_time_suffix(opt_arg, &v) < 0) { + ERR("Wrong value for --monitor-timer parameter: %s", opt_arg); + ret = CMD_ERROR; + goto end; + } + opt_monitor_timer.interval = (uint64_t) v; + opt_monitor_timer.set = true; + DBG("Channel monitor timer interval set to %" PRIu64 " %s", + opt_monitor_timer.interval, + USEC_UNIT); + break; + } + case OPT_BLOCKING_TIMEOUT: + { + uint64_t v; + long long v_msec; + + errno = 0; + opt_arg = poptGetOptArg(pc); + + if (strcmp(opt_arg, "inf") == 0) { + opt_blocking_timeout.value = (int64_t) -1; + opt_blocking_timeout.set = true; + DBG("Channel blocking timeout set to infinity"); + break; + } + + if (utils_parse_time_suffix(opt_arg, &v) < 0) { + ERR("Wrong value for --blocking-timeout parameter: %s", opt_arg); + ret = CMD_ERROR; + goto end; + } + + /* + * While LTTng-UST and LTTng-tools will accept a + * blocking timeout expressed in µs, the current + * tracer implementation relies on poll() which + * takes an "int timeout" parameter expressed in + * msec. + * + * Since the error reporting from the tracer is + * not precise, we perform this check here to + * provide a helpful error message in case of + * overflow. + * + * The setter (liblttng-ctl) also performs an + * equivalent check. + */ + v_msec = v / 1000; + if (v_msec != (int32_t) v_msec) { + ERR("32-bit milliseconds overflow in --blocking-timeout parameter: %s", opt_arg); + ret = CMD_ERROR; + goto end; + } + + opt_blocking_timeout.value = (int64_t) v; + opt_blocking_timeout.set = true; + DBG("Channel blocking timeout set to %" PRId64 " %s%s", + opt_blocking_timeout.value, + USEC_UNIT, + opt_blocking_timeout.value == 0 ? + " (non-blocking)" : ""); + break; + } + case OPT_USERSPACE: + opt_userspace = 1; + break; + case OPT_TRACEFILE_SIZE: + opt_arg = poptGetOptArg(pc); + if (utils_parse_size_suffix(opt_arg, &chan_opts.attr.tracefile_size) < 0) { + ERR("Wrong value in --tracefile-size parameter: %s", opt_arg); + ret = CMD_ERROR; + goto end; + } + DBG("Maximum tracefile size set to %" PRIu64, + chan_opts.attr.tracefile_size); + break; + case OPT_TRACEFILE_COUNT: + { + unsigned long v; + + errno = 0; + opt_arg = poptGetOptArg(pc); + v = strtoul(opt_arg, NULL, 0); + if (errno != 0 || !isdigit(opt_arg[0])) { + ERR("Wrong value in --tracefile-count parameter: %s", opt_arg); + ret = CMD_ERROR; + goto end; + } + if (v != (uint32_t) v) { + ERR("32-bit overflow in --tracefile-count parameter: %s", opt_arg); + ret = CMD_ERROR; + goto end; + } + chan_opts.attr.tracefile_count = (uint32_t) v; + DBG("Maximum tracefile count set to %" PRIu64, + chan_opts.attr.tracefile_count); + break; + } + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + default: + ret = CMD_UNDEFINED; + goto end; + } + } + + ret = print_missing_or_multiple_domains( + opt_kernel + opt_userspace, false); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + if (chan_opts.attr.overwrite == 1 && opt_blocking_timeout.set && + opt_blocking_timeout.value != 0) { + ERR("You cannot specify --overwrite and --blocking-timeout=N, " + "where N is different than 0"); + ret = CMD_ERROR; + goto end; + } + + /* Mi check */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_enable_channels); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + opt_channels = (char*) poptGetArg(pc); + if (opt_channels == NULL) { + ERR("Missing channel name.\n"); + ret = CMD_ERROR; + success = 0; + goto mi_closing; + } + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + ret = CMD_ERROR; + success = 0; + goto mi_closing; + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + command_ret = CMD_ERROR; + success = 0; + goto mi_closing; + } + } else { + session_name = opt_session_name; + } + + command_ret = enable_channel(session_name); + if (command_ret) { + success = 0; + } + +mi_closing: + /* Mi closing */ + if (lttng_opt_mi) { + /* Close output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + goto end; + } + } + +end: + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : LTTNG_ERR_MI_IO_FAIL; + } + + if (!opt_session_name && session_name) { + free(session_name); + } + + /* Overwrite ret if an error occurred when enable_channel */ + ret = command_ret ? command_ret : ret; + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/enable_events.c b/src/bin/lttng/commands/enable_events.c deleted file mode 100644 index bbf7c5699..000000000 --- a/src/bin/lttng/commands/enable_events.c +++ /dev/null @@ -1,1396 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* Mi dependancy */ -#include - -#include -#include - -#include "../command.h" -#include "../loglevel.h" -#include "../uprobe.h" - -#if (LTTNG_SYMBOL_NAME_LEN == 256) -#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255" -#endif - -static char *opt_event_list; -static int opt_event_type; -static const char *opt_loglevel; -static int opt_loglevel_type; -static int opt_kernel; -static char *opt_session_name; -static int opt_userspace; -static int opt_jul; -static int opt_log4j; -static int opt_python; -static int opt_enable_all; -static char *opt_probe; -static char *opt_userspace_probe; -static char *opt_function; -static char *opt_channel_name; -static char *opt_filter; -static char *opt_exclude; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_TRACEPOINT, - OPT_PROBE, - OPT_USERSPACE_PROBE, - OPT_FUNCTION, - OPT_SYSCALL, - OPT_USERSPACE, - OPT_LOGLEVEL, - OPT_LOGLEVEL_ONLY, - OPT_LIST_OPTIONS, - OPT_FILTER, - OPT_EXCLUDE, -}; - -static struct lttng_handle *handle; -static struct mi_writer *writer; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, - {"all", 'a', POPT_ARG_VAL, &opt_enable_all, 1, 0, 0}, - {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0}, - {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, - {"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0}, - {"jul", 'j', POPT_ARG_VAL, &opt_jul, 1, 0, 0}, - {"log4j", 'l', POPT_ARG_VAL, &opt_log4j, 1, 0, 0}, - {"python", 'p', POPT_ARG_VAL, &opt_python, 1, 0, 0}, - {"tracepoint", 0, POPT_ARG_NONE, 0, OPT_TRACEPOINT, 0, 0}, - {"probe", 0, POPT_ARG_STRING, &opt_probe, OPT_PROBE, 0, 0}, - {"userspace-probe",0, POPT_ARG_STRING, &opt_userspace_probe, OPT_USERSPACE_PROBE, 0, 0}, - {"function", 0, POPT_ARG_STRING, &opt_function, OPT_FUNCTION, 0, 0}, - {"syscall", 0, POPT_ARG_NONE, 0, OPT_SYSCALL, 0, 0}, - {"loglevel", 0, POPT_ARG_STRING, 0, OPT_LOGLEVEL, 0, 0}, - {"loglevel-only", 0, POPT_ARG_STRING, 0, OPT_LOGLEVEL_ONLY, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {"filter", 'f', POPT_ARG_STRING, &opt_filter, OPT_FILTER, 0, 0}, - {"exclude", 'x', POPT_ARG_STRING, &opt_exclude, OPT_EXCLUDE, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * Parse probe options. - */ -static int parse_probe_opts(struct lttng_event *ev, char *opt) -{ - int ret = CMD_SUCCESS; - int match; - char s_hex[19]; -#define S_HEX_LEN_SCANF_IS_A_BROKEN_API "18" /* 18 is (19 - 1) (\0 is extra) */ - char name[LTTNG_SYMBOL_NAME_LEN]; - - if (opt == NULL) { - ret = CMD_ERROR; - goto end; - } - - /* Check for symbol+offset */ - match = sscanf(opt, "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API - "[^'+']+%" S_HEX_LEN_SCANF_IS_A_BROKEN_API "s", name, s_hex); - if (match == 2) { - strncpy(ev->attr.probe.symbol_name, name, LTTNG_SYMBOL_NAME_LEN); - ev->attr.probe.symbol_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - DBG("probe symbol %s", ev->attr.probe.symbol_name); - if (*s_hex == '\0') { - ERR("Invalid probe offset %s", s_hex); - ret = CMD_ERROR; - goto end; - } - ev->attr.probe.offset = strtoul(s_hex, NULL, 0); - DBG("probe offset %" PRIu64, ev->attr.probe.offset); - ev->attr.probe.addr = 0; - goto end; - } - - /* Check for symbol */ - if (isalpha(name[0]) || name[0] == '_') { - match = sscanf(opt, "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "s", - name); - if (match == 1) { - strncpy(ev->attr.probe.symbol_name, name, LTTNG_SYMBOL_NAME_LEN); - ev->attr.probe.symbol_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - DBG("probe symbol %s", ev->attr.probe.symbol_name); - ev->attr.probe.offset = 0; - DBG("probe offset %" PRIu64, ev->attr.probe.offset); - ev->attr.probe.addr = 0; - goto end; - } - } - - /* Check for address */ - match = sscanf(opt, "%" S_HEX_LEN_SCANF_IS_A_BROKEN_API "s", s_hex); - if (match > 0) { - /* - * Return an error if the first character of the tentative - * address is NULL or not a digit. It can be "0" if the address - * is in hexadecimal and can be 1 to 9 if it's in decimal. - */ - if (*s_hex == '\0' || !isdigit(*s_hex)) { - ERR("Invalid probe description %s", s_hex); - ret = CMD_ERROR; - goto end; - } - ev->attr.probe.addr = strtoul(s_hex, NULL, 0); - DBG("probe addr %" PRIu64, ev->attr.probe.addr); - ev->attr.probe.offset = 0; - memset(ev->attr.probe.symbol_name, 0, LTTNG_SYMBOL_NAME_LEN); - goto end; - } - - /* No match */ - ret = CMD_ERROR; - -end: - return ret; -} - -static -const char *print_channel_name(const char *name) -{ - return name ? : DEFAULT_CHANNEL_NAME; -} - -static -const char *print_raw_channel_name(const char *name) -{ - return name ? : ""; -} - -/* - * Mi print exlcusion list - */ -static -int mi_print_exclusion(const struct lttng_dynamic_pointer_array *exclusions) -{ - int ret; - size_t i; - const size_t count = lttng_dynamic_pointer_array_get_count(exclusions); - - LTTNG_ASSERT(writer); - - if (count == 0) { - ret = 0; - goto end; - } - - ret = mi_lttng_writer_open_element(writer, config_element_exclusions); - if (ret) { - goto end; - } - - for (i = 0; i < count; i++) { - const char *exclusion = lttng_dynamic_pointer_array_get_pointer( - exclusions, i); - - ret = mi_lttng_writer_write_element_string(writer, - config_element_exclusion, exclusion); - if (ret) { - goto end; - } - } - - /* Close exclusions element */ - ret = mi_lttng_writer_close_element(writer); - -end: - return ret; -} - -/* - * Return allocated string for pretty-printing exclusion names. - */ -static -char *print_exclusions(const struct lttng_dynamic_pointer_array *exclusions) -{ - int length = 0; - size_t i; - const char preamble[] = " excluding "; - char *ret; - const size_t count = lttng_dynamic_pointer_array_get_count(exclusions); - - if (count == 0) { - return strdup(""); - } - - /* Calculate total required length. */ - for (i = 0; i < count; i++) { - const char *exclusion = lttng_dynamic_pointer_array_get_pointer( - exclusions, i); - - length += strlen(exclusion) + 4; - } - - length += sizeof(preamble); - ret = zmalloc(length); - if (!ret) { - return NULL; - } - - strncpy(ret, preamble, length); - for (i = 0; i < count; i++) { - const char *exclusion = lttng_dynamic_pointer_array_get_pointer( - exclusions, i); - - strcat(ret, "\""); - strcat(ret, exclusion); - strcat(ret, "\""); - if (i != count - 1) { - strcat(ret, ", "); - } - } - - return ret; -} - -static -int check_exclusion_subsets(const char *event_name, const char *exclusion) -{ - bool warn = false; - int ret = 0; - const char *e = event_name; - const char *x = exclusion; - - /* Scan both the excluder and the event letter by letter */ - while (true) { - if (*e == '\\') { - if (*x != *e) { - warn = true; - goto end; - } - - e++; - x++; - goto cmp_chars; - } - - if (*x == '*') { - /* Event is a subset of the excluder */ - ERR("Event %s: %s excludes all events from %s", - event_name, exclusion, event_name); - goto error; - } - - if (*e == '*') { - /* - * Reached the end of the event name before the - * end of the exclusion: this is valid. - */ - goto end; - } - -cmp_chars: - if (*x != *e) { - warn = true; - break; - } - - x++; - e++; - } - - goto end; - -error: - ret = -1; - -end: - if (warn) { - WARN("Event %s: %s does not exclude any events from %s", - event_name, exclusion, event_name); - } - - return ret; -} - -int validate_exclusion_list(const char *event_name, - const struct lttng_dynamic_pointer_array *exclusions) -{ - int ret; - - /* Event name must be a valid globbing pattern to allow exclusions. */ - if (!strutils_is_star_glob_pattern(event_name)) { - ERR("Event %s: Exclusions can only be used with a globbing pattern", - event_name); - goto error; - } - - /* - * If the event name is a star-at-end only globbing pattern, - * then we can validate the individual exclusions. Otherwise - * all exclusions are passed to the session daemon. - */ - if (strutils_is_star_at_the_end_only_glob_pattern(event_name)) { - size_t i, num_exclusions; - - num_exclusions = lttng_dynamic_pointer_array_get_count(exclusions); - - for (i = 0; i < num_exclusions; i++) { - const char *exclusion = - lttng_dynamic_pointer_array_get_pointer( - exclusions, i); - - if (!strutils_is_star_glob_pattern(exclusion) || - strutils_is_star_at_the_end_only_glob_pattern(exclusion)) { - ret = check_exclusion_subsets(event_name, exclusion); - if (ret) { - goto error; - } - } - } - } - - ret = 0; - goto end; - -error: - ret = -1; - -end: - return ret; -} - -static int create_exclusion_list_and_validate(const char *event_name, - const char *exclusions_arg, - struct lttng_dynamic_pointer_array *exclusions) -{ - int ret = 0; - - /* Split exclusions. */ - ret = strutils_split(exclusions_arg, ',', true, exclusions); - if (ret < 0) { - goto error; - } - - if (validate_exclusion_list(event_name, exclusions) != - 0) { - goto error; - } - - goto end; - -error: - ret = -1; - lttng_dynamic_pointer_array_reset(exclusions); - -end: - return ret; -} - -static void warn_on_truncated_exclusion_names(const struct lttng_dynamic_pointer_array *exclusions, - int *warn) -{ - size_t i; - const size_t num_exclusions = lttng_dynamic_pointer_array_get_count(exclusions); - - for (i = 0; i < num_exclusions; i++) { - const char * const exclusion = lttng_dynamic_pointer_array_get_pointer(exclusions, i); - - if (strlen(exclusion) >= LTTNG_SYMBOL_NAME_LEN) { - WARN("Event exclusion \"%s\" will be truncated", - exclusion); - *warn = 1; - } - } -} - -/* - * Enabling event using the lttng API. - * Note: in case of error only the last error code will be return. - */ -static int enable_events(char *session_name) -{ - int ret = CMD_SUCCESS, command_ret = CMD_SUCCESS; - int error_holder = CMD_SUCCESS, warn = 0, error = 0, success = 1; - char *event_name, *channel_name = NULL; - struct lttng_event *ev; - struct lttng_domain dom = {}; - struct lttng_dynamic_pointer_array exclusions; - struct lttng_userspace_probe_location *uprobe_loc = NULL; - - lttng_dynamic_pointer_array_init(&exclusions, NULL); - - ev = lttng_event_create(); - if (!ev) { - ret = CMD_ERROR; - goto error; - } - - if (opt_kernel) { - if (opt_loglevel) { - WARN("Kernel loglevels are not supported."); - } - } - - /* Create lttng domain */ - if (opt_kernel) { - dom.type = LTTNG_DOMAIN_KERNEL; - dom.buf_type = LTTNG_BUFFER_GLOBAL; - } else if (opt_userspace) { - dom.type = LTTNG_DOMAIN_UST; - /* Default. */ - dom.buf_type = LTTNG_BUFFER_PER_UID; - } else if (opt_jul) { - dom.type = LTTNG_DOMAIN_JUL; - /* Default. */ - dom.buf_type = LTTNG_BUFFER_PER_UID; - } else if (opt_log4j) { - dom.type = LTTNG_DOMAIN_LOG4J; - /* Default. */ - dom.buf_type = LTTNG_BUFFER_PER_UID; - } else if (opt_python) { - dom.type = LTTNG_DOMAIN_PYTHON; - /* Default. */ - dom.buf_type = LTTNG_BUFFER_PER_UID; - } else { - /* Checked by the caller. */ - abort(); - } - - if (opt_exclude) { - switch (dom.type) { - case LTTNG_DOMAIN_KERNEL: - case LTTNG_DOMAIN_JUL: - case LTTNG_DOMAIN_LOG4J: - case LTTNG_DOMAIN_PYTHON: - ERR("Event name exclusions are not yet implemented for %s events", - lttng_domain_type_str(dom.type)); - ret = CMD_ERROR; - goto error; - case LTTNG_DOMAIN_UST: - /* Exclusions supported */ - break; - default: - abort(); - } - } - - /* - * Adding a filter to a probe, function or userspace-probe would be - * denied by the kernel tracer as it's not supported at the moment. We - * do an early check here to warn the user. - */ - if (opt_filter && opt_kernel) { - switch (opt_event_type) { - case LTTNG_EVENT_ALL: - case LTTNG_EVENT_TRACEPOINT: - case LTTNG_EVENT_SYSCALL: - break; - case LTTNG_EVENT_PROBE: - case LTTNG_EVENT_USERSPACE_PROBE: - case LTTNG_EVENT_FUNCTION: - ERR("Filter expressions are not supported for %s events", - get_event_type_str(opt_event_type)); - ret = CMD_ERROR; - goto error; - default: - ret = CMD_UNDEFINED; - goto error; - } - } - - channel_name = opt_channel_name; - - handle = lttng_create_handle(session_name, &dom); - if (handle == NULL) { - ret = -1; - goto error; - } - - /* Prepare Mi */ - if (lttng_opt_mi) { - /* Open a events element */ - ret = mi_lttng_writer_open_element(writer, config_element_events); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - - if (opt_enable_all) { - /* Default setup for enable all */ - if (opt_kernel) { - ev->type = opt_event_type; - strcpy(ev->name, "*"); - /* kernel loglevels not implemented */ - ev->loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; - } else { - ev->type = LTTNG_EVENT_TRACEPOINT; - strcpy(ev->name, "*"); - ev->loglevel_type = opt_loglevel_type; - if (opt_loglevel) { - int name_search_ret; - - LTTNG_ASSERT(opt_userspace || opt_jul || opt_log4j || opt_python); - - if (opt_userspace) { - enum lttng_loglevel loglevel; - - name_search_ret = loglevel_name_to_value(opt_loglevel, &loglevel); - ev->loglevel = (int) loglevel; - } else if (opt_jul) { - enum lttng_loglevel_jul loglevel; - - name_search_ret = loglevel_jul_name_to_value(opt_loglevel, &loglevel); - ev->loglevel = (int) loglevel; - } else if (opt_log4j) { - enum lttng_loglevel_log4j loglevel; - - name_search_ret = loglevel_log4j_name_to_value(opt_loglevel, &loglevel); - ev->loglevel = (int) loglevel; - } else { - /* python domain. */ - enum lttng_loglevel_python loglevel; - - name_search_ret = loglevel_python_name_to_value(opt_loglevel, &loglevel); - ev->loglevel = (int) loglevel; - } - - if (name_search_ret == -1) { - ERR("Unknown loglevel %s", opt_loglevel); - ret = -LTTNG_ERR_INVALID; - goto error; - } - } else { - LTTNG_ASSERT(opt_userspace || opt_jul || opt_log4j || opt_python); - if (opt_userspace) { - ev->loglevel = -1; - } else if (opt_jul) { - ev->loglevel = LTTNG_LOGLEVEL_JUL_ALL; - } else if (opt_log4j) { - ev->loglevel = LTTNG_LOGLEVEL_LOG4J_ALL; - } else if (opt_python) { - ev->loglevel = LTTNG_LOGLEVEL_PYTHON_DEBUG; - } - } - } - - if (opt_exclude) { - ret = create_exclusion_list_and_validate("*", - opt_exclude, &exclusions); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - ev->exclusion = 1; - warn_on_truncated_exclusion_names(&exclusions, - &warn); - } - if (!opt_filter) { - ret = lttng_enable_event_with_exclusions(handle, - ev, channel_name, - NULL, - lttng_dynamic_pointer_array_get_count(&exclusions), - (char **) exclusions.array.buffer.data); - if (ret < 0) { - switch (-ret) { - case LTTNG_ERR_KERN_EVENT_EXIST: - WARN("Kernel events already enabled (channel %s, session %s)", - print_channel_name(channel_name), session_name); - warn = 1; - break; - case LTTNG_ERR_TRACE_ALREADY_STARTED: - { - const char *msg = "The command tried to enable an event in a new domain for a session that has already been started once."; - ERR("Events: %s (channel %s, session %s)", - msg, - print_channel_name(channel_name), - session_name); - error = 1; - break; - } - default: - ERR("Events: %s (channel %s, session %s)", - lttng_strerror(ret), - ret == -LTTNG_ERR_NEED_CHANNEL_NAME - ? print_raw_channel_name(channel_name) - : print_channel_name(channel_name), - session_name); - error = 1; - break; - } - goto end; - } - - switch (opt_event_type) { - case LTTNG_EVENT_TRACEPOINT: - if (opt_loglevel && dom.type != LTTNG_DOMAIN_KERNEL) { - char *exclusion_string = print_exclusions(&exclusions); - - if (!exclusion_string) { - PERROR("Cannot allocate exclusion_string"); - error = 1; - goto end; - } - MSG("All %s tracepoints%s are enabled in channel %s for loglevel %s", - lttng_domain_type_str(dom.type), - exclusion_string, - print_channel_name(channel_name), - opt_loglevel); - free(exclusion_string); - } else { - char *exclusion_string = print_exclusions(&exclusions); - - if (!exclusion_string) { - PERROR("Cannot allocate exclusion_string"); - error = 1; - goto end; - } - MSG("All %s tracepoints%s are enabled in channel %s", - lttng_domain_type_str(dom.type), - exclusion_string, - print_channel_name(channel_name)); - free(exclusion_string); - } - break; - case LTTNG_EVENT_SYSCALL: - if (opt_kernel) { - MSG("All %s system calls are enabled in channel %s", - lttng_domain_type_str(dom.type), - print_channel_name(channel_name)); - } - break; - case LTTNG_EVENT_ALL: - if (opt_loglevel && dom.type != LTTNG_DOMAIN_KERNEL) { - char *exclusion_string = print_exclusions(&exclusions); - - if (!exclusion_string) { - PERROR("Cannot allocate exclusion_string"); - error = 1; - goto end; - } - MSG("All %s events%s are enabled in channel %s for loglevel %s", - lttng_domain_type_str(dom.type), - exclusion_string, - print_channel_name(channel_name), - opt_loglevel); - free(exclusion_string); - } else { - char *exclusion_string = print_exclusions(&exclusions); - - if (!exclusion_string) { - PERROR("Cannot allocate exclusion_string"); - error = 1; - goto end; - } - MSG("All %s events%s are enabled in channel %s", - lttng_domain_type_str(dom.type), - exclusion_string, - print_channel_name(channel_name)); - free(exclusion_string); - } - break; - default: - /* - * We should not be here since lttng_enable_event should have - * failed on the event type. - */ - goto error; - } - } - - if (opt_filter) { - command_ret = lttng_enable_event_with_exclusions(handle, ev, channel_name, - opt_filter, - lttng_dynamic_pointer_array_get_count(&exclusions), - (char **) exclusions.array.buffer.data); - if (command_ret < 0) { - switch (-command_ret) { - case LTTNG_ERR_FILTER_EXIST: - WARN("Filter on all events is already enabled" - " (channel %s, session %s)", - print_channel_name(channel_name), session_name); - warn = 1; - break; - case LTTNG_ERR_TRACE_ALREADY_STARTED: - { - const char *msg = "The command tried to enable an event in a new domain for a session that has already been started once."; - ERR("All events: %s (channel %s, session %s, filter \'%s\')", - msg, - print_channel_name(channel_name), - session_name, opt_filter); - error = 1; - break; - } - default: - ERR("All events: %s (channel %s, session %s, filter \'%s\')", - lttng_strerror(command_ret), - command_ret == -LTTNG_ERR_NEED_CHANNEL_NAME - ? print_raw_channel_name(channel_name) - : print_channel_name(channel_name), - session_name, opt_filter); - error = 1; - break; - } - error_holder = command_ret; - } else { - ev->filter = 1; - MSG("Filter '%s' successfully set", opt_filter); - } - } - - if (lttng_opt_mi) { - /* The wildcard * is used for kernel and ust domain to - * represent ALL. We copy * in event name to force the wildcard use - * for kernel domain - * - * Note: this is strictly for semantic and printing while in - * machine interface mode. - */ - strcpy(ev->name, "*"); - - /* If we reach here the events are enabled */ - if (!error && !warn) { - ev->enabled = 1; - } else { - ev->enabled = 0; - success = 0; - } - ret = mi_lttng_event(writer, ev, 1, handle->domain.type); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - /* print exclusion */ - ret = mi_print_exclusion(&exclusions); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - /* Close event element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - - goto end; - } - - /* Strip event list */ - event_name = strtok(opt_event_list, ","); - while (event_name != NULL) { - /* Copy name and type of the event */ - strncpy(ev->name, event_name, LTTNG_SYMBOL_NAME_LEN); - ev->name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - ev->type = opt_event_type; - - /* Kernel tracer action */ - if (opt_kernel) { - DBG("Enabling kernel event %s for channel %s", - event_name, - print_channel_name(channel_name)); - - switch (opt_event_type) { - case LTTNG_EVENT_ALL: /* Enable tracepoints and syscalls */ - /* If event name differs from *, select tracepoint. */ - if (strcmp(ev->name, "*")) { - ev->type = LTTNG_EVENT_TRACEPOINT; - } - break; - case LTTNG_EVENT_TRACEPOINT: - break; - case LTTNG_EVENT_PROBE: - ret = parse_probe_opts(ev, opt_probe); - if (ret) { - ERR("Unable to parse probe options"); - ret = CMD_ERROR; - goto error; - } - break; - case LTTNG_EVENT_USERSPACE_PROBE: - LTTNG_ASSERT(ev->type == LTTNG_EVENT_USERSPACE_PROBE); - - ret = parse_userspace_probe_opts(opt_userspace_probe, &uprobe_loc); - if (ret) { - switch (ret) { - case CMD_UNSUPPORTED: - /* - * Error message describing - * what is not supported was - * printed in the function. - */ - break; - case CMD_ERROR: - default: - ERR("Unable to parse userspace probe options"); - break; - } - goto error; - } - - ret = lttng_event_set_userspace_probe_location(ev, uprobe_loc); - if (ret) { - WARN("Failed to set probe location on event"); - ret = CMD_ERROR; - goto error; - } - - /* Ownership of the uprobe location was transferred to the event. */ - uprobe_loc = NULL; - break; - case LTTNG_EVENT_FUNCTION: - ret = parse_probe_opts(ev, opt_function); - if (ret) { - ERR("Unable to parse function probe options"); - ret = CMD_ERROR; - goto error; - } - break; - case LTTNG_EVENT_SYSCALL: - ev->type = LTTNG_EVENT_SYSCALL; - break; - default: - ret = CMD_UNDEFINED; - goto error; - } - - /* kernel loglevels not implemented */ - ev->loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; - } else if (opt_userspace) { /* User-space tracer action */ - DBG("Enabling UST event %s for channel %s, loglevel %s", event_name, - print_channel_name(channel_name), opt_loglevel ? : ""); - - switch (opt_event_type) { - case LTTNG_EVENT_ALL: /* Default behavior is tracepoint */ - /* Fall-through */ - case LTTNG_EVENT_TRACEPOINT: - /* Copy name and type of the event */ - ev->type = LTTNG_EVENT_TRACEPOINT; - strncpy(ev->name, event_name, LTTNG_SYMBOL_NAME_LEN); - ev->name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - break; - case LTTNG_EVENT_PROBE: - case LTTNG_EVENT_FUNCTION: - case LTTNG_EVENT_SYSCALL: - case LTTNG_EVENT_USERSPACE_PROBE: - default: - ERR("Event type not available for user-space tracing"); - ret = CMD_UNSUPPORTED; - goto error; - } - - if (opt_exclude) { - ev->exclusion = 1; - if (opt_event_type != LTTNG_EVENT_ALL && opt_event_type != LTTNG_EVENT_TRACEPOINT) { - ERR("Exclusion option can only be used with tracepoint events"); - ret = CMD_ERROR; - goto error; - } - /* Free previously allocated items. */ - lttng_dynamic_pointer_array_reset(&exclusions); - ret = create_exclusion_list_and_validate( - event_name, opt_exclude, - &exclusions); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - warn_on_truncated_exclusion_names( - &exclusions, &warn); - } - - ev->loglevel_type = opt_loglevel_type; - if (opt_loglevel) { - enum lttng_loglevel loglevel; - const int name_search_ret = loglevel_name_to_value(opt_loglevel, &loglevel); - - if (name_search_ret == -1) { - ERR("Unknown loglevel %s", opt_loglevel); - ret = -LTTNG_ERR_INVALID; - goto error; - } - - ev->loglevel = (int) loglevel; - } else { - ev->loglevel = -1; - } - } else if (opt_jul || opt_log4j || opt_python) { - if (opt_event_type != LTTNG_EVENT_ALL && - opt_event_type != LTTNG_EVENT_TRACEPOINT) { - ERR("Event type not supported for domain."); - ret = CMD_UNSUPPORTED; - goto error; - } - - ev->loglevel_type = opt_loglevel_type; - if (opt_loglevel) { - int name_search_ret; - - if (opt_jul) { - enum lttng_loglevel_jul loglevel; - - name_search_ret = loglevel_jul_name_to_value(opt_loglevel, &loglevel); - ev->loglevel = (int) loglevel; - } else if (opt_log4j) { - enum lttng_loglevel_log4j loglevel; - - name_search_ret = loglevel_log4j_name_to_value(opt_loglevel, &loglevel); - ev->loglevel = (int) loglevel; - } else { - /* python domain. */ - enum lttng_loglevel_python loglevel; - - name_search_ret = loglevel_python_name_to_value(opt_loglevel, &loglevel); - ev->loglevel = (int) loglevel; - } - - if (name_search_ret) { - ERR("Unknown loglevel %s", opt_loglevel); - ret = -LTTNG_ERR_INVALID; - goto error; - } - } else { - if (opt_jul) { - ev->loglevel = LTTNG_LOGLEVEL_JUL_ALL; - } else if (opt_log4j) { - ev->loglevel = LTTNG_LOGLEVEL_LOG4J_ALL; - } else if (opt_python) { - ev->loglevel = LTTNG_LOGLEVEL_PYTHON_DEBUG; - } - } - ev->type = LTTNG_EVENT_TRACEPOINT; - strncpy(ev->name, event_name, LTTNG_SYMBOL_NAME_LEN); - ev->name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - } else { - abort(); - } - - if (!opt_filter) { - char *exclusion_string; - - command_ret = lttng_enable_event_with_exclusions(handle, - ev, channel_name, - NULL, - lttng_dynamic_pointer_array_get_count(&exclusions), - (char **) exclusions.array.buffer.data); - exclusion_string = print_exclusions(&exclusions); - if (!exclusion_string) { - PERROR("Cannot allocate exclusion_string"); - error = 1; - goto end; - } - if (command_ret < 0) { - /* Turn ret to positive value to handle the positive error code */ - switch (-command_ret) { - case LTTNG_ERR_KERN_EVENT_EXIST: - WARN("Kernel event %s%s already enabled (channel %s, session %s)", - event_name, - exclusion_string, - print_channel_name(channel_name), session_name); - warn = 1; - break; - case LTTNG_ERR_TRACE_ALREADY_STARTED: - { - const char *msg = "The command tried to enable an event in a new domain for a session that has already been started once."; - ERR("Event %s%s: %s (channel %s, session %s)", event_name, - exclusion_string, - msg, - print_channel_name(channel_name), - session_name); - error = 1; - break; - } - case LTTNG_ERR_SDT_PROBE_SEMAPHORE: - ERR("SDT probes %s guarded by semaphores are not supported (channel %s, session %s)", - event_name, print_channel_name(channel_name), - session_name); - error = 1; - break; - default: - ERR("Event %s%s: %s (channel %s, session %s)", event_name, - exclusion_string, - lttng_strerror(command_ret), - command_ret == -LTTNG_ERR_NEED_CHANNEL_NAME - ? print_raw_channel_name(channel_name) - : print_channel_name(channel_name), - session_name); - error = 1; - break; - } - error_holder = command_ret; - } else { - switch (dom.type) { - case LTTNG_DOMAIN_KERNEL: - case LTTNG_DOMAIN_UST: - MSG("%s event %s%s created in channel %s", - lttng_domain_type_str(dom.type), - event_name, - exclusion_string, - print_channel_name(channel_name)); - break; - case LTTNG_DOMAIN_JUL: - case LTTNG_DOMAIN_LOG4J: - case LTTNG_DOMAIN_PYTHON: - /* - * Don't print the default channel - * name for agent domains. - */ - MSG("%s event %s%s enabled", - lttng_domain_type_str(dom.type), - event_name, - exclusion_string); - break; - default: - abort(); - } - } - free(exclusion_string); - } - - if (opt_filter) { - char *exclusion_string; - - /* Filter present */ - ev->filter = 1; - - command_ret = lttng_enable_event_with_exclusions(handle, ev, channel_name, - opt_filter, - lttng_dynamic_pointer_array_get_count(&exclusions), - (char **) exclusions.array.buffer.data); - exclusion_string = print_exclusions(&exclusions); - if (!exclusion_string) { - PERROR("Cannot allocate exclusion_string"); - error = 1; - goto end; - } - if (command_ret < 0) { - switch (-command_ret) { - case LTTNG_ERR_FILTER_EXIST: - WARN("Filter on event %s%s is already enabled" - " (channel %s, session %s)", - event_name, - exclusion_string, - print_channel_name(channel_name), session_name); - warn = 1; - break; - case LTTNG_ERR_TRACE_ALREADY_STARTED: - { - const char *msg = "The command tried to enable an event in a new domain for a session that has already been started once."; - ERR("Event %s%s: %s (channel %s, session %s, filter \'%s\')", ev->name, - exclusion_string, - msg, - print_channel_name(channel_name), - session_name, opt_filter); - error = 1; - break; - } - default: - ERR("Event %s%s: %s (channel %s, session %s, filter \'%s\')", ev->name, - exclusion_string, - lttng_strerror(command_ret), - command_ret == -LTTNG_ERR_NEED_CHANNEL_NAME - ? print_raw_channel_name(channel_name) - : print_channel_name(channel_name), - session_name, opt_filter); - error = 1; - break; - } - error_holder = command_ret; - - } else { - MSG("Event %s%s: Filter '%s' successfully set", - event_name, exclusion_string, - opt_filter); - } - free(exclusion_string); - } - - if (lttng_opt_mi) { - if (command_ret) { - success = 0; - ev->enabled = 0; - } else { - ev->enabled = 1; - } - - ret = mi_lttng_event(writer, ev, 1, handle->domain.type); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - /* print exclusion */ - ret = mi_print_exclusion(&exclusions); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Close event element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - /* Next event */ - event_name = strtok(NULL, ","); - /* Reset warn, error and success */ - success = 1; - } - -end: - /* Close Mi */ - if (lttng_opt_mi) { - /* Close events element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } -error: - if (warn) { - ret = CMD_WARNING; - } - if (error) { - ret = CMD_ERROR; - } - lttng_destroy_handle(handle); - lttng_dynamic_pointer_array_reset(&exclusions); - lttng_userspace_probe_location_destroy(uprobe_loc); - - /* Overwrite ret with error_holder if there was an actual error with - * enabling an event. - */ - ret = error_holder ? error_holder : ret; - - lttng_event_destroy(ev); - return ret; -} - -/* - * Add event to trace session - */ -int cmd_enable_events(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; - static poptContext pc; - char *session_name = NULL; - const char *leftover = NULL; - int event_type = -1; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - /* Default event type */ - opt_event_type = LTTNG_EVENT_ALL; - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_TRACEPOINT: - opt_event_type = LTTNG_EVENT_TRACEPOINT; - break; - case OPT_PROBE: - opt_event_type = LTTNG_EVENT_PROBE; - break; - case OPT_USERSPACE_PROBE: - opt_event_type = LTTNG_EVENT_USERSPACE_PROBE; - break; - case OPT_FUNCTION: - opt_event_type = LTTNG_EVENT_FUNCTION; - break; - case OPT_SYSCALL: - opt_event_type = LTTNG_EVENT_SYSCALL; - break; - case OPT_USERSPACE: - opt_userspace = 1; - break; - case OPT_LOGLEVEL: - opt_loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE; - opt_loglevel = poptGetOptArg(pc); - break; - case OPT_LOGLEVEL_ONLY: - opt_loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE; - opt_loglevel = poptGetOptArg(pc); - break; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - case OPT_FILTER: - break; - case OPT_EXCLUDE: - break; - default: - ret = CMD_UNDEFINED; - goto end; - } - - /* Validate event type. Multiple event type are not supported. */ - if (event_type == -1) { - event_type = opt_event_type; - } else { - if (event_type != opt_event_type) { - ERR("Multiple event type not supported."); - ret = CMD_ERROR; - goto end; - } - } - } - - ret = print_missing_or_multiple_domains( - opt_kernel + opt_userspace + opt_jul + opt_log4j + - opt_python, - true); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Mi check */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_enable_event); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - opt_event_list = (char*) poptGetArg(pc); - if (opt_event_list == NULL && opt_enable_all == 0) { - ERR("Missing event name(s).\n"); - ret = CMD_ERROR; - goto end; - } - - leftover = poptGetArg(pc); - if (leftover) { - ERR("Unknown argument: %s", leftover); - ret = CMD_ERROR; - goto end; - } - - if (!opt_session_name) { - session_name = get_session_name(); - if (session_name == NULL) { - command_ret = CMD_ERROR; - success = 0; - goto mi_closing; - } - } else { - session_name = opt_session_name; - } - - command_ret = enable_events(session_name); - if (command_ret) { - success = 0; - goto mi_closing; - } - -mi_closing: - /* Mi closing */ - if (lttng_opt_mi) { - /* Close output element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - -end: - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - ret = ret ? ret : LTTNG_ERR_MI_IO_FAIL; - } - - if (opt_session_name == NULL) { - free(session_name); - } - - /* Overwrite ret if an error occurred in enable_events */ - ret = command_ret ? command_ret : ret; - - poptFreeContext(pc); - return ret; -} - diff --git a/src/bin/lttng/commands/enable_events.cpp b/src/bin/lttng/commands/enable_events.cpp new file mode 100644 index 000000000..50854faaa --- /dev/null +++ b/src/bin/lttng/commands/enable_events.cpp @@ -0,0 +1,1396 @@ +/* + * Copyright (C) 2011 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Mi dependancy */ +#include + +#include +#include + +#include "../command.h" +#include "../loglevel.h" +#include "../uprobe.h" + +#if (LTTNG_SYMBOL_NAME_LEN == 256) +#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255" +#endif + +static char *opt_event_list; +static int opt_event_type; +static const char *opt_loglevel; +static int opt_loglevel_type; +static int opt_kernel; +static char *opt_session_name; +static int opt_userspace; +static int opt_jul; +static int opt_log4j; +static int opt_python; +static int opt_enable_all; +static char *opt_probe; +static char *opt_userspace_probe; +static char *opt_function; +static char *opt_channel_name; +static char *opt_filter; +static char *opt_exclude; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_TRACEPOINT, + OPT_PROBE, + OPT_USERSPACE_PROBE, + OPT_FUNCTION, + OPT_SYSCALL, + OPT_USERSPACE, + OPT_LOGLEVEL, + OPT_LOGLEVEL_ONLY, + OPT_LIST_OPTIONS, + OPT_FILTER, + OPT_EXCLUDE, +}; + +static struct lttng_handle *handle; +static struct mi_writer *writer; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, + {"all", 'a', POPT_ARG_VAL, &opt_enable_all, 1, 0, 0}, + {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0}, + {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, + {"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0}, + {"jul", 'j', POPT_ARG_VAL, &opt_jul, 1, 0, 0}, + {"log4j", 'l', POPT_ARG_VAL, &opt_log4j, 1, 0, 0}, + {"python", 'p', POPT_ARG_VAL, &opt_python, 1, 0, 0}, + {"tracepoint", 0, POPT_ARG_NONE, 0, OPT_TRACEPOINT, 0, 0}, + {"probe", 0, POPT_ARG_STRING, &opt_probe, OPT_PROBE, 0, 0}, + {"userspace-probe",0, POPT_ARG_STRING, &opt_userspace_probe, OPT_USERSPACE_PROBE, 0, 0}, + {"function", 0, POPT_ARG_STRING, &opt_function, OPT_FUNCTION, 0, 0}, + {"syscall", 0, POPT_ARG_NONE, 0, OPT_SYSCALL, 0, 0}, + {"loglevel", 0, POPT_ARG_STRING, 0, OPT_LOGLEVEL, 0, 0}, + {"loglevel-only", 0, POPT_ARG_STRING, 0, OPT_LOGLEVEL_ONLY, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {"filter", 'f', POPT_ARG_STRING, &opt_filter, OPT_FILTER, 0, 0}, + {"exclude", 'x', POPT_ARG_STRING, &opt_exclude, OPT_EXCLUDE, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * Parse probe options. + */ +static int parse_probe_opts(struct lttng_event *ev, char *opt) +{ + int ret = CMD_SUCCESS; + int match; + char s_hex[19]; +#define S_HEX_LEN_SCANF_IS_A_BROKEN_API "18" /* 18 is (19 - 1) (\0 is extra) */ + char name[LTTNG_SYMBOL_NAME_LEN]; + + if (opt == NULL) { + ret = CMD_ERROR; + goto end; + } + + /* Check for symbol+offset */ + match = sscanf(opt, "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API + "[^'+']+%" S_HEX_LEN_SCANF_IS_A_BROKEN_API "s", name, s_hex); + if (match == 2) { + strncpy(ev->attr.probe.symbol_name, name, LTTNG_SYMBOL_NAME_LEN); + ev->attr.probe.symbol_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + DBG("probe symbol %s", ev->attr.probe.symbol_name); + if (*s_hex == '\0') { + ERR("Invalid probe offset %s", s_hex); + ret = CMD_ERROR; + goto end; + } + ev->attr.probe.offset = strtoul(s_hex, NULL, 0); + DBG("probe offset %" PRIu64, ev->attr.probe.offset); + ev->attr.probe.addr = 0; + goto end; + } + + /* Check for symbol */ + if (isalpha(name[0]) || name[0] == '_') { + match = sscanf(opt, "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "s", + name); + if (match == 1) { + strncpy(ev->attr.probe.symbol_name, name, LTTNG_SYMBOL_NAME_LEN); + ev->attr.probe.symbol_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + DBG("probe symbol %s", ev->attr.probe.symbol_name); + ev->attr.probe.offset = 0; + DBG("probe offset %" PRIu64, ev->attr.probe.offset); + ev->attr.probe.addr = 0; + goto end; + } + } + + /* Check for address */ + match = sscanf(opt, "%" S_HEX_LEN_SCANF_IS_A_BROKEN_API "s", s_hex); + if (match > 0) { + /* + * Return an error if the first character of the tentative + * address is NULL or not a digit. It can be "0" if the address + * is in hexadecimal and can be 1 to 9 if it's in decimal. + */ + if (*s_hex == '\0' || !isdigit(*s_hex)) { + ERR("Invalid probe description %s", s_hex); + ret = CMD_ERROR; + goto end; + } + ev->attr.probe.addr = strtoul(s_hex, NULL, 0); + DBG("probe addr %" PRIu64, ev->attr.probe.addr); + ev->attr.probe.offset = 0; + memset(ev->attr.probe.symbol_name, 0, LTTNG_SYMBOL_NAME_LEN); + goto end; + } + + /* No match */ + ret = CMD_ERROR; + +end: + return ret; +} + +static +const char *print_channel_name(const char *name) +{ + return name ? : DEFAULT_CHANNEL_NAME; +} + +static +const char *print_raw_channel_name(const char *name) +{ + return name ? : ""; +} + +/* + * Mi print exlcusion list + */ +static +int mi_print_exclusion(const struct lttng_dynamic_pointer_array *exclusions) +{ + int ret; + size_t i; + const size_t count = lttng_dynamic_pointer_array_get_count(exclusions); + + LTTNG_ASSERT(writer); + + if (count == 0) { + ret = 0; + goto end; + } + + ret = mi_lttng_writer_open_element(writer, config_element_exclusions); + if (ret) { + goto end; + } + + for (i = 0; i < count; i++) { + const char *exclusion = (const char *) lttng_dynamic_pointer_array_get_pointer( + exclusions, i); + + ret = mi_lttng_writer_write_element_string(writer, + config_element_exclusion, exclusion); + if (ret) { + goto end; + } + } + + /* Close exclusions element */ + ret = mi_lttng_writer_close_element(writer); + +end: + return ret; +} + +/* + * Return allocated string for pretty-printing exclusion names. + */ +static +char *print_exclusions(const struct lttng_dynamic_pointer_array *exclusions) +{ + int length = 0; + size_t i; + const char preamble[] = " excluding "; + char *ret; + const size_t count = lttng_dynamic_pointer_array_get_count(exclusions); + + if (count == 0) { + return strdup(""); + } + + /* Calculate total required length. */ + for (i = 0; i < count; i++) { + const char *exclusion = (const char *) lttng_dynamic_pointer_array_get_pointer( + exclusions, i); + + length += strlen(exclusion) + 4; + } + + length += sizeof(preamble); + ret = (char *) zmalloc(length); + if (!ret) { + return NULL; + } + + strncpy(ret, preamble, length); + for (i = 0; i < count; i++) { + const char *exclusion = (const char *) lttng_dynamic_pointer_array_get_pointer( + exclusions, i); + + strcat(ret, "\""); + strcat(ret, exclusion); + strcat(ret, "\""); + if (i != count - 1) { + strcat(ret, ", "); + } + } + + return ret; +} + +static +int check_exclusion_subsets(const char *event_name, const char *exclusion) +{ + bool warn = false; + int ret = 0; + const char *e = event_name; + const char *x = exclusion; + + /* Scan both the excluder and the event letter by letter */ + while (true) { + if (*e == '\\') { + if (*x != *e) { + warn = true; + goto end; + } + + e++; + x++; + goto cmp_chars; + } + + if (*x == '*') { + /* Event is a subset of the excluder */ + ERR("Event %s: %s excludes all events from %s", + event_name, exclusion, event_name); + goto error; + } + + if (*e == '*') { + /* + * Reached the end of the event name before the + * end of the exclusion: this is valid. + */ + goto end; + } + +cmp_chars: + if (*x != *e) { + warn = true; + break; + } + + x++; + e++; + } + + goto end; + +error: + ret = -1; + +end: + if (warn) { + WARN("Event %s: %s does not exclude any events from %s", + event_name, exclusion, event_name); + } + + return ret; +} + +int validate_exclusion_list(const char *event_name, + const struct lttng_dynamic_pointer_array *exclusions) +{ + int ret; + + /* Event name must be a valid globbing pattern to allow exclusions. */ + if (!strutils_is_star_glob_pattern(event_name)) { + ERR("Event %s: Exclusions can only be used with a globbing pattern", + event_name); + goto error; + } + + /* + * If the event name is a star-at-end only globbing pattern, + * then we can validate the individual exclusions. Otherwise + * all exclusions are passed to the session daemon. + */ + if (strutils_is_star_at_the_end_only_glob_pattern(event_name)) { + size_t i, num_exclusions; + + num_exclusions = lttng_dynamic_pointer_array_get_count(exclusions); + + for (i = 0; i < num_exclusions; i++) { + const char *exclusion = + (const char *) lttng_dynamic_pointer_array_get_pointer( + exclusions, i); + + if (!strutils_is_star_glob_pattern(exclusion) || + strutils_is_star_at_the_end_only_glob_pattern(exclusion)) { + ret = check_exclusion_subsets(event_name, exclusion); + if (ret) { + goto error; + } + } + } + } + + ret = 0; + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static int create_exclusion_list_and_validate(const char *event_name, + const char *exclusions_arg, + struct lttng_dynamic_pointer_array *exclusions) +{ + int ret = 0; + + /* Split exclusions. */ + ret = strutils_split(exclusions_arg, ',', true, exclusions); + if (ret < 0) { + goto error; + } + + if (validate_exclusion_list(event_name, exclusions) != + 0) { + goto error; + } + + goto end; + +error: + ret = -1; + lttng_dynamic_pointer_array_reset(exclusions); + +end: + return ret; +} + +static void warn_on_truncated_exclusion_names(const struct lttng_dynamic_pointer_array *exclusions, + int *warn) +{ + size_t i; + const size_t num_exclusions = lttng_dynamic_pointer_array_get_count(exclusions); + + for (i = 0; i < num_exclusions; i++) { + const char * const exclusion = (const char *) lttng_dynamic_pointer_array_get_pointer(exclusions, i); + + if (strlen(exclusion) >= LTTNG_SYMBOL_NAME_LEN) { + WARN("Event exclusion \"%s\" will be truncated", + exclusion); + *warn = 1; + } + } +} + +/* + * Enabling event using the lttng API. + * Note: in case of error only the last error code will be return. + */ +static int enable_events(char *session_name) +{ + int ret = CMD_SUCCESS, command_ret = CMD_SUCCESS; + int error_holder = CMD_SUCCESS, warn = 0, error = 0, success = 1; + char *event_name, *channel_name = NULL; + struct lttng_event *ev; + struct lttng_domain dom = {}; + struct lttng_dynamic_pointer_array exclusions; + struct lttng_userspace_probe_location *uprobe_loc = NULL; + + lttng_dynamic_pointer_array_init(&exclusions, NULL); + + ev = lttng_event_create(); + if (!ev) { + ret = CMD_ERROR; + goto error; + } + + if (opt_kernel) { + if (opt_loglevel) { + WARN("Kernel loglevels are not supported."); + } + } + + /* Create lttng domain */ + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + dom.buf_type = LTTNG_BUFFER_GLOBAL; + } else if (opt_userspace) { + dom.type = LTTNG_DOMAIN_UST; + /* Default. */ + dom.buf_type = LTTNG_BUFFER_PER_UID; + } else if (opt_jul) { + dom.type = LTTNG_DOMAIN_JUL; + /* Default. */ + dom.buf_type = LTTNG_BUFFER_PER_UID; + } else if (opt_log4j) { + dom.type = LTTNG_DOMAIN_LOG4J; + /* Default. */ + dom.buf_type = LTTNG_BUFFER_PER_UID; + } else if (opt_python) { + dom.type = LTTNG_DOMAIN_PYTHON; + /* Default. */ + dom.buf_type = LTTNG_BUFFER_PER_UID; + } else { + /* Checked by the caller. */ + abort(); + } + + if (opt_exclude) { + switch (dom.type) { + case LTTNG_DOMAIN_KERNEL: + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + ERR("Event name exclusions are not yet implemented for %s events", + lttng_domain_type_str(dom.type)); + ret = CMD_ERROR; + goto error; + case LTTNG_DOMAIN_UST: + /* Exclusions supported */ + break; + default: + abort(); + } + } + + /* + * Adding a filter to a probe, function or userspace-probe would be + * denied by the kernel tracer as it's not supported at the moment. We + * do an early check here to warn the user. + */ + if (opt_filter && opt_kernel) { + switch (opt_event_type) { + case LTTNG_EVENT_ALL: + case LTTNG_EVENT_TRACEPOINT: + case LTTNG_EVENT_SYSCALL: + break; + case LTTNG_EVENT_PROBE: + case LTTNG_EVENT_USERSPACE_PROBE: + case LTTNG_EVENT_FUNCTION: + ERR("Filter expressions are not supported for %s events", + get_event_type_str((lttng_event_type) opt_event_type)); + ret = CMD_ERROR; + goto error; + default: + ret = CMD_UNDEFINED; + goto error; + } + } + + channel_name = opt_channel_name; + + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = -1; + goto error; + } + + /* Prepare Mi */ + if (lttng_opt_mi) { + /* Open a events element */ + ret = mi_lttng_writer_open_element(writer, config_element_events); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + + if (opt_enable_all) { + /* Default setup for enable all */ + if (opt_kernel) { + ev->type = (lttng_event_type) opt_event_type; + strcpy(ev->name, "*"); + /* kernel loglevels not implemented */ + ev->loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; + } else { + ev->type = LTTNG_EVENT_TRACEPOINT; + strcpy(ev->name, "*"); + ev->loglevel_type = (lttng_loglevel_type) opt_loglevel_type; + if (opt_loglevel) { + int name_search_ret; + + LTTNG_ASSERT(opt_userspace || opt_jul || opt_log4j || opt_python); + + if (opt_userspace) { + enum lttng_loglevel loglevel; + + name_search_ret = loglevel_name_to_value(opt_loglevel, &loglevel); + ev->loglevel = (int) loglevel; + } else if (opt_jul) { + enum lttng_loglevel_jul loglevel; + + name_search_ret = loglevel_jul_name_to_value(opt_loglevel, &loglevel); + ev->loglevel = (int) loglevel; + } else if (opt_log4j) { + enum lttng_loglevel_log4j loglevel; + + name_search_ret = loglevel_log4j_name_to_value(opt_loglevel, &loglevel); + ev->loglevel = (int) loglevel; + } else { + /* python domain. */ + enum lttng_loglevel_python loglevel; + + name_search_ret = loglevel_python_name_to_value(opt_loglevel, &loglevel); + ev->loglevel = (int) loglevel; + } + + if (name_search_ret == -1) { + ERR("Unknown loglevel %s", opt_loglevel); + ret = -LTTNG_ERR_INVALID; + goto error; + } + } else { + LTTNG_ASSERT(opt_userspace || opt_jul || opt_log4j || opt_python); + if (opt_userspace) { + ev->loglevel = -1; + } else if (opt_jul) { + ev->loglevel = LTTNG_LOGLEVEL_JUL_ALL; + } else if (opt_log4j) { + ev->loglevel = LTTNG_LOGLEVEL_LOG4J_ALL; + } else if (opt_python) { + ev->loglevel = LTTNG_LOGLEVEL_PYTHON_DEBUG; + } + } + } + + if (opt_exclude) { + ret = create_exclusion_list_and_validate("*", + opt_exclude, &exclusions); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + ev->exclusion = 1; + warn_on_truncated_exclusion_names(&exclusions, + &warn); + } + if (!opt_filter) { + ret = lttng_enable_event_with_exclusions(handle, + ev, channel_name, + NULL, + lttng_dynamic_pointer_array_get_count(&exclusions), + (char **) exclusions.array.buffer.data); + if (ret < 0) { + switch (-ret) { + case LTTNG_ERR_KERN_EVENT_EXIST: + WARN("Kernel events already enabled (channel %s, session %s)", + print_channel_name(channel_name), session_name); + warn = 1; + break; + case LTTNG_ERR_TRACE_ALREADY_STARTED: + { + const char *msg = "The command tried to enable an event in a new domain for a session that has already been started once."; + ERR("Events: %s (channel %s, session %s)", + msg, + print_channel_name(channel_name), + session_name); + error = 1; + break; + } + default: + ERR("Events: %s (channel %s, session %s)", + lttng_strerror(ret), + ret == -LTTNG_ERR_NEED_CHANNEL_NAME + ? print_raw_channel_name(channel_name) + : print_channel_name(channel_name), + session_name); + error = 1; + break; + } + goto end; + } + + switch (opt_event_type) { + case LTTNG_EVENT_TRACEPOINT: + if (opt_loglevel && dom.type != LTTNG_DOMAIN_KERNEL) { + char *exclusion_string = print_exclusions(&exclusions); + + if (!exclusion_string) { + PERROR("Cannot allocate exclusion_string"); + error = 1; + goto end; + } + MSG("All %s tracepoints%s are enabled in channel %s for loglevel %s", + lttng_domain_type_str(dom.type), + exclusion_string, + print_channel_name(channel_name), + opt_loglevel); + free(exclusion_string); + } else { + char *exclusion_string = print_exclusions(&exclusions); + + if (!exclusion_string) { + PERROR("Cannot allocate exclusion_string"); + error = 1; + goto end; + } + MSG("All %s tracepoints%s are enabled in channel %s", + lttng_domain_type_str(dom.type), + exclusion_string, + print_channel_name(channel_name)); + free(exclusion_string); + } + break; + case LTTNG_EVENT_SYSCALL: + if (opt_kernel) { + MSG("All %s system calls are enabled in channel %s", + lttng_domain_type_str(dom.type), + print_channel_name(channel_name)); + } + break; + case LTTNG_EVENT_ALL: + if (opt_loglevel && dom.type != LTTNG_DOMAIN_KERNEL) { + char *exclusion_string = print_exclusions(&exclusions); + + if (!exclusion_string) { + PERROR("Cannot allocate exclusion_string"); + error = 1; + goto end; + } + MSG("All %s events%s are enabled in channel %s for loglevel %s", + lttng_domain_type_str(dom.type), + exclusion_string, + print_channel_name(channel_name), + opt_loglevel); + free(exclusion_string); + } else { + char *exclusion_string = print_exclusions(&exclusions); + + if (!exclusion_string) { + PERROR("Cannot allocate exclusion_string"); + error = 1; + goto end; + } + MSG("All %s events%s are enabled in channel %s", + lttng_domain_type_str(dom.type), + exclusion_string, + print_channel_name(channel_name)); + free(exclusion_string); + } + break; + default: + /* + * We should not be here since lttng_enable_event should have + * failed on the event type. + */ + goto error; + } + } + + if (opt_filter) { + command_ret = lttng_enable_event_with_exclusions(handle, ev, channel_name, + opt_filter, + lttng_dynamic_pointer_array_get_count(&exclusions), + (char **) exclusions.array.buffer.data); + if (command_ret < 0) { + switch (-command_ret) { + case LTTNG_ERR_FILTER_EXIST: + WARN("Filter on all events is already enabled" + " (channel %s, session %s)", + print_channel_name(channel_name), session_name); + warn = 1; + break; + case LTTNG_ERR_TRACE_ALREADY_STARTED: + { + const char *msg = "The command tried to enable an event in a new domain for a session that has already been started once."; + ERR("All events: %s (channel %s, session %s, filter \'%s\')", + msg, + print_channel_name(channel_name), + session_name, opt_filter); + error = 1; + break; + } + default: + ERR("All events: %s (channel %s, session %s, filter \'%s\')", + lttng_strerror(command_ret), + command_ret == -LTTNG_ERR_NEED_CHANNEL_NAME + ? print_raw_channel_name(channel_name) + : print_channel_name(channel_name), + session_name, opt_filter); + error = 1; + break; + } + error_holder = command_ret; + } else { + ev->filter = 1; + MSG("Filter '%s' successfully set", opt_filter); + } + } + + if (lttng_opt_mi) { + /* The wildcard * is used for kernel and ust domain to + * represent ALL. We copy * in event name to force the wildcard use + * for kernel domain + * + * Note: this is strictly for semantic and printing while in + * machine interface mode. + */ + strcpy(ev->name, "*"); + + /* If we reach here the events are enabled */ + if (!error && !warn) { + ev->enabled = 1; + } else { + ev->enabled = 0; + success = 0; + } + ret = mi_lttng_event(writer, ev, 1, handle->domain.type); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* print exclusion */ + ret = mi_print_exclusion(&exclusions); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* Close event element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + + goto end; + } + + /* Strip event list */ + event_name = strtok(opt_event_list, ","); + while (event_name != NULL) { + /* Copy name and type of the event */ + strncpy(ev->name, event_name, LTTNG_SYMBOL_NAME_LEN); + ev->name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + ev->type = (lttng_event_type) opt_event_type; + + /* Kernel tracer action */ + if (opt_kernel) { + DBG("Enabling kernel event %s for channel %s", + event_name, + print_channel_name(channel_name)); + + switch (opt_event_type) { + case LTTNG_EVENT_ALL: /* Enable tracepoints and syscalls */ + /* If event name differs from *, select tracepoint. */ + if (strcmp(ev->name, "*")) { + ev->type = LTTNG_EVENT_TRACEPOINT; + } + break; + case LTTNG_EVENT_TRACEPOINT: + break; + case LTTNG_EVENT_PROBE: + ret = parse_probe_opts(ev, opt_probe); + if (ret) { + ERR("Unable to parse probe options"); + ret = CMD_ERROR; + goto error; + } + break; + case LTTNG_EVENT_USERSPACE_PROBE: + LTTNG_ASSERT(ev->type == LTTNG_EVENT_USERSPACE_PROBE); + + ret = parse_userspace_probe_opts(opt_userspace_probe, &uprobe_loc); + if (ret) { + switch (ret) { + case CMD_UNSUPPORTED: + /* + * Error message describing + * what is not supported was + * printed in the function. + */ + break; + case CMD_ERROR: + default: + ERR("Unable to parse userspace probe options"); + break; + } + goto error; + } + + ret = lttng_event_set_userspace_probe_location(ev, uprobe_loc); + if (ret) { + WARN("Failed to set probe location on event"); + ret = CMD_ERROR; + goto error; + } + + /* Ownership of the uprobe location was transferred to the event. */ + uprobe_loc = NULL; + break; + case LTTNG_EVENT_FUNCTION: + ret = parse_probe_opts(ev, opt_function); + if (ret) { + ERR("Unable to parse function probe options"); + ret = CMD_ERROR; + goto error; + } + break; + case LTTNG_EVENT_SYSCALL: + ev->type = LTTNG_EVENT_SYSCALL; + break; + default: + ret = CMD_UNDEFINED; + goto error; + } + + /* kernel loglevels not implemented */ + ev->loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; + } else if (opt_userspace) { /* User-space tracer action */ + DBG("Enabling UST event %s for channel %s, loglevel %s", event_name, + print_channel_name(channel_name), opt_loglevel ? : ""); + + switch (opt_event_type) { + case LTTNG_EVENT_ALL: /* Default behavior is tracepoint */ + /* Fall-through */ + case LTTNG_EVENT_TRACEPOINT: + /* Copy name and type of the event */ + ev->type = LTTNG_EVENT_TRACEPOINT; + strncpy(ev->name, event_name, LTTNG_SYMBOL_NAME_LEN); + ev->name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + break; + case LTTNG_EVENT_PROBE: + case LTTNG_EVENT_FUNCTION: + case LTTNG_EVENT_SYSCALL: + case LTTNG_EVENT_USERSPACE_PROBE: + default: + ERR("Event type not available for user-space tracing"); + ret = CMD_UNSUPPORTED; + goto error; + } + + if (opt_exclude) { + ev->exclusion = 1; + if (opt_event_type != LTTNG_EVENT_ALL && opt_event_type != LTTNG_EVENT_TRACEPOINT) { + ERR("Exclusion option can only be used with tracepoint events"); + ret = CMD_ERROR; + goto error; + } + /* Free previously allocated items. */ + lttng_dynamic_pointer_array_reset(&exclusions); + ret = create_exclusion_list_and_validate( + event_name, opt_exclude, + &exclusions); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + warn_on_truncated_exclusion_names( + &exclusions, &warn); + } + + ev->loglevel_type = (lttng_loglevel_type) opt_loglevel_type; + if (opt_loglevel) { + enum lttng_loglevel loglevel; + const int name_search_ret = loglevel_name_to_value(opt_loglevel, &loglevel); + + if (name_search_ret == -1) { + ERR("Unknown loglevel %s", opt_loglevel); + ret = -LTTNG_ERR_INVALID; + goto error; + } + + ev->loglevel = (int) loglevel; + } else { + ev->loglevel = -1; + } + } else if (opt_jul || opt_log4j || opt_python) { + if (opt_event_type != LTTNG_EVENT_ALL && + opt_event_type != LTTNG_EVENT_TRACEPOINT) { + ERR("Event type not supported for domain."); + ret = CMD_UNSUPPORTED; + goto error; + } + + ev->loglevel_type = (lttng_loglevel_type) opt_loglevel_type; + if (opt_loglevel) { + int name_search_ret; + + if (opt_jul) { + enum lttng_loglevel_jul loglevel; + + name_search_ret = loglevel_jul_name_to_value(opt_loglevel, &loglevel); + ev->loglevel = (int) loglevel; + } else if (opt_log4j) { + enum lttng_loglevel_log4j loglevel; + + name_search_ret = loglevel_log4j_name_to_value(opt_loglevel, &loglevel); + ev->loglevel = (int) loglevel; + } else { + /* python domain. */ + enum lttng_loglevel_python loglevel; + + name_search_ret = loglevel_python_name_to_value(opt_loglevel, &loglevel); + ev->loglevel = (int) loglevel; + } + + if (name_search_ret) { + ERR("Unknown loglevel %s", opt_loglevel); + ret = -LTTNG_ERR_INVALID; + goto error; + } + } else { + if (opt_jul) { + ev->loglevel = LTTNG_LOGLEVEL_JUL_ALL; + } else if (opt_log4j) { + ev->loglevel = LTTNG_LOGLEVEL_LOG4J_ALL; + } else if (opt_python) { + ev->loglevel = LTTNG_LOGLEVEL_PYTHON_DEBUG; + } + } + ev->type = LTTNG_EVENT_TRACEPOINT; + strncpy(ev->name, event_name, LTTNG_SYMBOL_NAME_LEN); + ev->name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + } else { + abort(); + } + + if (!opt_filter) { + char *exclusion_string; + + command_ret = lttng_enable_event_with_exclusions(handle, + ev, channel_name, + NULL, + lttng_dynamic_pointer_array_get_count(&exclusions), + (char **) exclusions.array.buffer.data); + exclusion_string = print_exclusions(&exclusions); + if (!exclusion_string) { + PERROR("Cannot allocate exclusion_string"); + error = 1; + goto end; + } + if (command_ret < 0) { + /* Turn ret to positive value to handle the positive error code */ + switch (-command_ret) { + case LTTNG_ERR_KERN_EVENT_EXIST: + WARN("Kernel event %s%s already enabled (channel %s, session %s)", + event_name, + exclusion_string, + print_channel_name(channel_name), session_name); + warn = 1; + break; + case LTTNG_ERR_TRACE_ALREADY_STARTED: + { + const char *msg = "The command tried to enable an event in a new domain for a session that has already been started once."; + ERR("Event %s%s: %s (channel %s, session %s)", event_name, + exclusion_string, + msg, + print_channel_name(channel_name), + session_name); + error = 1; + break; + } + case LTTNG_ERR_SDT_PROBE_SEMAPHORE: + ERR("SDT probes %s guarded by semaphores are not supported (channel %s, session %s)", + event_name, print_channel_name(channel_name), + session_name); + error = 1; + break; + default: + ERR("Event %s%s: %s (channel %s, session %s)", event_name, + exclusion_string, + lttng_strerror(command_ret), + command_ret == -LTTNG_ERR_NEED_CHANNEL_NAME + ? print_raw_channel_name(channel_name) + : print_channel_name(channel_name), + session_name); + error = 1; + break; + } + error_holder = command_ret; + } else { + switch (dom.type) { + case LTTNG_DOMAIN_KERNEL: + case LTTNG_DOMAIN_UST: + MSG("%s event %s%s created in channel %s", + lttng_domain_type_str(dom.type), + event_name, + exclusion_string, + print_channel_name(channel_name)); + break; + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + /* + * Don't print the default channel + * name for agent domains. + */ + MSG("%s event %s%s enabled", + lttng_domain_type_str(dom.type), + event_name, + exclusion_string); + break; + default: + abort(); + } + } + free(exclusion_string); + } + + if (opt_filter) { + char *exclusion_string; + + /* Filter present */ + ev->filter = 1; + + command_ret = lttng_enable_event_with_exclusions(handle, ev, channel_name, + opt_filter, + lttng_dynamic_pointer_array_get_count(&exclusions), + (char **) exclusions.array.buffer.data); + exclusion_string = print_exclusions(&exclusions); + if (!exclusion_string) { + PERROR("Cannot allocate exclusion_string"); + error = 1; + goto end; + } + if (command_ret < 0) { + switch (-command_ret) { + case LTTNG_ERR_FILTER_EXIST: + WARN("Filter on event %s%s is already enabled" + " (channel %s, session %s)", + event_name, + exclusion_string, + print_channel_name(channel_name), session_name); + warn = 1; + break; + case LTTNG_ERR_TRACE_ALREADY_STARTED: + { + const char *msg = "The command tried to enable an event in a new domain for a session that has already been started once."; + ERR("Event %s%s: %s (channel %s, session %s, filter \'%s\')", ev->name, + exclusion_string, + msg, + print_channel_name(channel_name), + session_name, opt_filter); + error = 1; + break; + } + default: + ERR("Event %s%s: %s (channel %s, session %s, filter \'%s\')", ev->name, + exclusion_string, + lttng_strerror(command_ret), + command_ret == -LTTNG_ERR_NEED_CHANNEL_NAME + ? print_raw_channel_name(channel_name) + : print_channel_name(channel_name), + session_name, opt_filter); + error = 1; + break; + } + error_holder = command_ret; + + } else { + MSG("Event %s%s: Filter '%s' successfully set", + event_name, exclusion_string, + opt_filter); + } + free(exclusion_string); + } + + if (lttng_opt_mi) { + if (command_ret) { + success = 0; + ev->enabled = 0; + } else { + ev->enabled = 1; + } + + ret = mi_lttng_event(writer, ev, 1, handle->domain.type); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* print exclusion */ + ret = mi_print_exclusion(&exclusions); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Close event element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + /* Next event */ + event_name = strtok(NULL, ","); + /* Reset warn, error and success */ + success = 1; + } + +end: + /* Close Mi */ + if (lttng_opt_mi) { + /* Close events element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } +error: + if (warn) { + ret = CMD_WARNING; + } + if (error) { + ret = CMD_ERROR; + } + lttng_destroy_handle(handle); + lttng_dynamic_pointer_array_reset(&exclusions); + lttng_userspace_probe_location_destroy(uprobe_loc); + + /* Overwrite ret with error_holder if there was an actual error with + * enabling an event. + */ + ret = error_holder ? error_holder : ret; + + lttng_event_destroy(ev); + return ret; +} + +/* + * Add event to trace session + */ +int cmd_enable_events(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; + static poptContext pc; + char *session_name = NULL; + const char *leftover = NULL; + int event_type = -1; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + /* Default event type */ + opt_event_type = LTTNG_EVENT_ALL; + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_TRACEPOINT: + opt_event_type = LTTNG_EVENT_TRACEPOINT; + break; + case OPT_PROBE: + opt_event_type = LTTNG_EVENT_PROBE; + break; + case OPT_USERSPACE_PROBE: + opt_event_type = LTTNG_EVENT_USERSPACE_PROBE; + break; + case OPT_FUNCTION: + opt_event_type = LTTNG_EVENT_FUNCTION; + break; + case OPT_SYSCALL: + opt_event_type = LTTNG_EVENT_SYSCALL; + break; + case OPT_USERSPACE: + opt_userspace = 1; + break; + case OPT_LOGLEVEL: + opt_loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE; + opt_loglevel = poptGetOptArg(pc); + break; + case OPT_LOGLEVEL_ONLY: + opt_loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE; + opt_loglevel = poptGetOptArg(pc); + break; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + case OPT_FILTER: + break; + case OPT_EXCLUDE: + break; + default: + ret = CMD_UNDEFINED; + goto end; + } + + /* Validate event type. Multiple event type are not supported. */ + if (event_type == -1) { + event_type = opt_event_type; + } else { + if (event_type != opt_event_type) { + ERR("Multiple event type not supported."); + ret = CMD_ERROR; + goto end; + } + } + } + + ret = print_missing_or_multiple_domains( + opt_kernel + opt_userspace + opt_jul + opt_log4j + + opt_python, + true); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Mi check */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_enable_event); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + opt_event_list = (char*) poptGetArg(pc); + if (opt_event_list == NULL && opt_enable_all == 0) { + ERR("Missing event name(s).\n"); + ret = CMD_ERROR; + goto end; + } + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + ret = CMD_ERROR; + goto end; + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + command_ret = CMD_ERROR; + success = 0; + goto mi_closing; + } + } else { + session_name = opt_session_name; + } + + command_ret = enable_events(session_name); + if (command_ret) { + success = 0; + goto mi_closing; + } + +mi_closing: + /* Mi closing */ + if (lttng_opt_mi) { + /* Close output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + +end: + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : LTTNG_ERR_MI_IO_FAIL; + } + + if (opt_session_name == NULL) { + free(session_name); + } + + /* Overwrite ret if an error occurred in enable_events */ + ret = command_ret ? command_ret : ret; + + poptFreeContext(pc); + return ret; +} + diff --git a/src/bin/lttng/commands/enable_rotation.c b/src/bin/lttng/commands/enable_rotation.c deleted file mode 100644 index c10c6a2d7..000000000 --- a/src/bin/lttng/commands/enable_rotation.c +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (C) 2017 Julien Desfossez - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "../command.h" -#include - -static char *opt_session_name; -static struct mi_writer *writer; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, - OPT_TIMER, - OPT_SIZE, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, - {"timer", 0, POPT_ARG_INT, 0, OPT_TIMER, 0, 0}, - {"size", 0, POPT_ARG_INT, 0, OPT_SIZE, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -static const char *schedule_type_str[] = { - [LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC] = "periodic", - [LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD] = "size-based", -}; - -static enum cmd_error_code add_schedule(const char *session_name, - enum lttng_rotation_schedule_type schedule_type, uint64_t value) -{ - enum cmd_error_code ret = CMD_SUCCESS; - struct lttng_rotation_schedule *schedule = NULL; - enum lttng_rotation_status status; - const char *schedule_type_name; - - switch (schedule_type) { - case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC: - schedule = lttng_rotation_schedule_periodic_create(); - if (!schedule) { - ret = CMD_ERROR; - goto end; - } - status = lttng_rotation_schedule_periodic_set_period(schedule, - value); - break; - case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD: - schedule = lttng_rotation_schedule_size_threshold_create(); - if (!schedule) { - ret = CMD_ERROR; - goto end; - } - status = lttng_rotation_schedule_size_threshold_set_threshold( - schedule, value); - break; - default: - ERR("Unknown schedule type"); - abort(); - } - - schedule_type_name = schedule_type_str[schedule_type]; - - switch (status) { - case LTTNG_ROTATION_STATUS_OK: - break; - case LTTNG_ROTATION_STATUS_INVALID: - ERR("Invalid value for %s option", schedule_type_name); - ret = CMD_ERROR; - goto end; - default: - ERR("Unknown error occurred setting %s rotation schedule", - schedule_type_name); - ret = CMD_ERROR; - goto end; - } - - status = lttng_session_add_rotation_schedule(session_name, schedule); - switch (status) { - case LTTNG_ROTATION_STATUS_OK: - ret = CMD_SUCCESS; - switch (schedule_type) { - case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC: - MSG("Enabled %s rotations every %" PRIu64 " %s on session %s", - schedule_type_name, value, USEC_UNIT, session_name); - break; - case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD: - MSG("Enabled %s rotations every %" PRIu64 " bytes written on session %s", - schedule_type_name, value, session_name); - break; - default: - abort(); - } - break; - case LTTNG_ROTATION_STATUS_INVALID: - ERR("Invalid parameter for %s rotation schedule", - schedule_type_name); - ret = CMD_ERROR; - break; - case LTTNG_ROTATION_STATUS_SCHEDULE_ALREADY_SET: - ERR("A %s rotation schedule is already set on session %s", - schedule_type_name, - session_name); - ret = CMD_ERROR; - break; - case LTTNG_ROTATION_STATUS_ERROR: - default: - ERR("Failed to enable %s rotation schedule on session %s", - schedule_type_name, - session_name); - ret = CMD_ERROR; - break; - } - - if (lttng_opt_mi) { - int mi_ret; - - mi_ret = mi_lttng_rotation_schedule_result(writer, - schedule, ret == CMD_SUCCESS); - if (mi_ret < 0) { - ret = CMD_ERROR; - goto end; - } - } - -end: - lttng_rotation_schedule_destroy(schedule); - return ret; -} - -/* - * cmd_enable_rotation - * - * The 'enable-rotation ' first level command - */ -int cmd_enable_rotation(int argc, const char **argv) -{ - int popt_ret, opt, ret = 0; - enum cmd_error_code cmd_ret = CMD_SUCCESS; - static poptContext pc; - char *session_name = NULL; - char *opt_arg = NULL; - bool free_session_name = false; - uint64_t timer_us = 0, size_bytes = 0; - bool periodic_rotation = false, size_rotation = false; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - popt_ret = poptReadDefaultConfig(pc, 0); - if (popt_ret) { - ERR("poptReadDefaultConfig"); - goto error; - } - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - case OPT_TIMER: - errno = 0; - opt_arg = poptGetOptArg(pc); - if (errno != 0 || !isdigit(opt_arg[0])) { - ERR("Invalid value for --timer option: %s", opt_arg); - goto error; - } - if (utils_parse_time_suffix(opt_arg, &timer_us) < 0) { - ERR("Invalid value for --timer option: %s", opt_arg); - goto error; - } - if (periodic_rotation) { - ERR("Only one periodic rotation schedule may be set on a session."); - goto error; - } - periodic_rotation = true; - break; - case OPT_SIZE: - errno = 0; - opt_arg = poptGetOptArg(pc); - if (utils_parse_size_suffix(opt_arg, &size_bytes) < 0) { - ERR("Invalid value for --size option: %s", opt_arg); - goto error; - } - if (size_rotation) { - ERR("Only one size-based rotation schedule may be set on a session."); - goto error; - } - size_rotation = true; - break; - default: - cmd_ret = CMD_UNDEFINED; - goto end; - } - } - - if (opt_session_name == NULL) { - session_name = get_session_name(); - if (session_name == NULL) { - goto error; - } - free_session_name = true; - } else { - session_name = opt_session_name; - } - - /* Mi check */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - goto error; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_enable_rotation); - if (ret) { - goto error; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - goto error; - } - } - - if (!periodic_rotation && !size_rotation) { - ERR("No session rotation schedule parameter provided."); - cmd_ret = CMD_ERROR; - goto close_command; - } - - if (lttng_opt_mi) { - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_rotation_schedule_results); - if (ret) { - goto error; - } - - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_session_name, - session_name); - if (ret) { - goto error; - } - } - - if (periodic_rotation) { - /* - * Continue processing even on error as multiple schedules can - * be specified at once. - */ - cmd_ret = add_schedule(session_name, - LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC, - timer_us); - } - - if (size_rotation) { - enum cmd_error_code tmp_ret; - - /* Don't overwrite cmd_ret if it already indicates an error. */ - tmp_ret = add_schedule(session_name, - LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD, - size_bytes); - cmd_ret = cmd_ret ? cmd_ret : tmp_ret; - } - - if (lttng_opt_mi) { - /* Close rotation schedule results element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto error; - } - } - -close_command: - /* Mi closing */ - if (lttng_opt_mi) { - /* Close output element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto error; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, - cmd_ret == CMD_SUCCESS); - if (ret) { - goto error; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - goto error; - } - } - -end: - (void) mi_lttng_writer_destroy(writer); - poptFreeContext(pc); - if (free_session_name) { - free(session_name); - } - return cmd_ret; - -error: - cmd_ret = CMD_ERROR; - goto end; -} diff --git a/src/bin/lttng/commands/enable_rotation.cpp b/src/bin/lttng/commands/enable_rotation.cpp new file mode 100644 index 000000000..0210f9cb6 --- /dev/null +++ b/src/bin/lttng/commands/enable_rotation.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2017 Julien Desfossez + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../command.h" +#include + +static char *opt_session_name; +static struct mi_writer *writer; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, + OPT_TIMER, + OPT_SIZE, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, + {"timer", 0, POPT_ARG_INT, 0, OPT_TIMER, 0, 0}, + {"size", 0, POPT_ARG_INT, 0, OPT_SIZE, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +static const char *schedule_type_str[] = { + "periodic", + "size-based", +}; + +static enum cmd_error_code add_schedule(const char *session_name, + enum lttng_rotation_schedule_type schedule_type, uint64_t value) +{ + enum cmd_error_code ret = CMD_SUCCESS; + struct lttng_rotation_schedule *schedule = NULL; + enum lttng_rotation_status status; + const char *schedule_type_name; + + switch (schedule_type) { + case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC: + schedule = lttng_rotation_schedule_periodic_create(); + if (!schedule) { + ret = CMD_ERROR; + goto end; + } + status = lttng_rotation_schedule_periodic_set_period(schedule, + value); + break; + case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD: + schedule = lttng_rotation_schedule_size_threshold_create(); + if (!schedule) { + ret = CMD_ERROR; + goto end; + } + status = lttng_rotation_schedule_size_threshold_set_threshold( + schedule, value); + break; + default: + ERR("Unknown schedule type"); + abort(); + } + + schedule_type_name = schedule_type_str[schedule_type]; + + switch (status) { + case LTTNG_ROTATION_STATUS_OK: + break; + case LTTNG_ROTATION_STATUS_INVALID: + ERR("Invalid value for %s option", schedule_type_name); + ret = CMD_ERROR; + goto end; + default: + ERR("Unknown error occurred setting %s rotation schedule", + schedule_type_name); + ret = CMD_ERROR; + goto end; + } + + status = lttng_session_add_rotation_schedule(session_name, schedule); + switch (status) { + case LTTNG_ROTATION_STATUS_OK: + ret = CMD_SUCCESS; + switch (schedule_type) { + case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC: + MSG("Enabled %s rotations every %" PRIu64 " %s on session %s", + schedule_type_name, value, USEC_UNIT, session_name); + break; + case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD: + MSG("Enabled %s rotations every %" PRIu64 " bytes written on session %s", + schedule_type_name, value, session_name); + break; + default: + abort(); + } + break; + case LTTNG_ROTATION_STATUS_INVALID: + ERR("Invalid parameter for %s rotation schedule", + schedule_type_name); + ret = CMD_ERROR; + break; + case LTTNG_ROTATION_STATUS_SCHEDULE_ALREADY_SET: + ERR("A %s rotation schedule is already set on session %s", + schedule_type_name, + session_name); + ret = CMD_ERROR; + break; + case LTTNG_ROTATION_STATUS_ERROR: + default: + ERR("Failed to enable %s rotation schedule on session %s", + schedule_type_name, + session_name); + ret = CMD_ERROR; + break; + } + + if (lttng_opt_mi) { + int mi_ret; + + mi_ret = mi_lttng_rotation_schedule_result(writer, + schedule, ret == CMD_SUCCESS); + if (mi_ret < 0) { + ret = CMD_ERROR; + goto end; + } + } + +end: + lttng_rotation_schedule_destroy(schedule); + return ret; +} + +/* + * cmd_enable_rotation + * + * The 'enable-rotation ' first level command + */ +int cmd_enable_rotation(int argc, const char **argv) +{ + int popt_ret, opt, ret = 0; + enum cmd_error_code cmd_ret = CMD_SUCCESS; + static poptContext pc; + char *session_name = NULL; + char *opt_arg = NULL; + bool free_session_name = false; + uint64_t timer_us = 0, size_bytes = 0; + bool periodic_rotation = false, size_rotation = false; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + popt_ret = poptReadDefaultConfig(pc, 0); + if (popt_ret) { + ERR("poptReadDefaultConfig"); + goto error; + } + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + case OPT_TIMER: + errno = 0; + opt_arg = poptGetOptArg(pc); + if (errno != 0 || !isdigit(opt_arg[0])) { + ERR("Invalid value for --timer option: %s", opt_arg); + goto error; + } + if (utils_parse_time_suffix(opt_arg, &timer_us) < 0) { + ERR("Invalid value for --timer option: %s", opt_arg); + goto error; + } + if (periodic_rotation) { + ERR("Only one periodic rotation schedule may be set on a session."); + goto error; + } + periodic_rotation = true; + break; + case OPT_SIZE: + errno = 0; + opt_arg = poptGetOptArg(pc); + if (utils_parse_size_suffix(opt_arg, &size_bytes) < 0) { + ERR("Invalid value for --size option: %s", opt_arg); + goto error; + } + if (size_rotation) { + ERR("Only one size-based rotation schedule may be set on a session."); + goto error; + } + size_rotation = true; + break; + default: + cmd_ret = CMD_UNDEFINED; + goto end; + } + } + + if (opt_session_name == NULL) { + session_name = get_session_name(); + if (session_name == NULL) { + goto error; + } + free_session_name = true; + } else { + session_name = opt_session_name; + } + + /* Mi check */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + goto error; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_enable_rotation); + if (ret) { + goto error; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + goto error; + } + } + + if (!periodic_rotation && !size_rotation) { + ERR("No session rotation schedule parameter provided."); + cmd_ret = CMD_ERROR; + goto close_command; + } + + if (lttng_opt_mi) { + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_rotation_schedule_results); + if (ret) { + goto error; + } + + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_session_name, + session_name); + if (ret) { + goto error; + } + } + + if (periodic_rotation) { + /* + * Continue processing even on error as multiple schedules can + * be specified at once. + */ + cmd_ret = add_schedule(session_name, + LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC, + timer_us); + } + + if (size_rotation) { + enum cmd_error_code tmp_ret; + + /* Don't overwrite cmd_ret if it already indicates an error. */ + tmp_ret = add_schedule(session_name, + LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD, + size_bytes); + cmd_ret = cmd_ret ? cmd_ret : tmp_ret; + } + + if (lttng_opt_mi) { + /* Close rotation schedule results element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto error; + } + } + +close_command: + /* Mi closing */ + if (lttng_opt_mi) { + /* Close output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto error; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, + cmd_ret == CMD_SUCCESS); + if (ret) { + goto error; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + goto error; + } + } + +end: + (void) mi_lttng_writer_destroy(writer); + poptFreeContext(pc); + if (free_session_name) { + free(session_name); + } + return cmd_ret; + +error: + cmd_ret = CMD_ERROR; + goto end; +} diff --git a/src/bin/lttng/commands/help.c b/src/bin/lttng/commands/help.c deleted file mode 100644 index a9a01e7f1..000000000 --- a/src/bin/lttng/commands/help.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2015 Philippe Proulx - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include - -#include "../command.h" -#include - -#ifdef LTTNG_EMBED_HELP -static const char *help_msg = -#include -; -#endif - -static const char *lttng_help_msg = -#ifdef LTTNG_EMBED_HELP -#include -#else -NULL -#endif -; - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * cmd_help - */ -int cmd_help(int argc, const char **argv, const struct cmd_struct commands[]) -{ - int opt, ret = CMD_SUCCESS; - char *cmd_name; - static poptContext pc; - const struct cmd_struct *cmd; - int found = 0; - const char *cmd_argv[2]; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - default: - ret = CMD_UNDEFINED; - goto end; - } - } - - /* Get command name */ - cmd_name = (char *) poptGetArg(pc); - - if (cmd_name == NULL) { - /* Fall back to lttng(1) */ - ret = utils_show_help(1, "lttng", lttng_help_msg); - if (ret) { - ERR("Cannot show --help for `lttng`"); - perror("exec"); - ret = CMD_ERROR; - } - - goto end; - } - - /* Help about help? */ - if (strcmp(cmd_name, "help") == 0) { - SHOW_HELP(); - goto end; - } - - /* Make sure command name exists */ - cmd = &commands[0]; - - while (cmd->name != NULL) { - if (strcmp(cmd->name, cmd_name) == 0) { - found = 1; - break; - } - - cmd++; - } - - if (!found) { - ERR("Unknown command \"%s\"", cmd_name); - ret = CMD_ERROR; - goto end; - } - - /* Show command's help */ - cmd_argv[0] = cmd->name; - cmd_argv[1] = "--help"; - LTTNG_ASSERT(cmd->func); - ret = cmd->func(2, cmd_argv); - -end: - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/help.cpp b/src/bin/lttng/commands/help.cpp new file mode 100644 index 000000000..a9a01e7f1 --- /dev/null +++ b/src/bin/lttng/commands/help.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2015 Philippe Proulx + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include + +#include "../command.h" +#include + +#ifdef LTTNG_EMBED_HELP +static const char *help_msg = +#include +; +#endif + +static const char *lttng_help_msg = +#ifdef LTTNG_EMBED_HELP +#include +#else +NULL +#endif +; + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * cmd_help + */ +int cmd_help(int argc, const char **argv, const struct cmd_struct commands[]) +{ + int opt, ret = CMD_SUCCESS; + char *cmd_name; + static poptContext pc; + const struct cmd_struct *cmd; + int found = 0; + const char *cmd_argv[2]; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + default: + ret = CMD_UNDEFINED; + goto end; + } + } + + /* Get command name */ + cmd_name = (char *) poptGetArg(pc); + + if (cmd_name == NULL) { + /* Fall back to lttng(1) */ + ret = utils_show_help(1, "lttng", lttng_help_msg); + if (ret) { + ERR("Cannot show --help for `lttng`"); + perror("exec"); + ret = CMD_ERROR; + } + + goto end; + } + + /* Help about help? */ + if (strcmp(cmd_name, "help") == 0) { + SHOW_HELP(); + goto end; + } + + /* Make sure command name exists */ + cmd = &commands[0]; + + while (cmd->name != NULL) { + if (strcmp(cmd->name, cmd_name) == 0) { + found = 1; + break; + } + + cmd++; + } + + if (!found) { + ERR("Unknown command \"%s\"", cmd_name); + ret = CMD_ERROR; + goto end; + } + + /* Show command's help */ + cmd_argv[0] = cmd->name; + cmd_argv[1] = "--help"; + LTTNG_ASSERT(cmd->func); + ret = cmd->func(2, cmd_argv); + +end: + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/list.c b/src/bin/lttng/commands/list.c deleted file mode 100644 index f4c97cbd1..000000000 --- a/src/bin/lttng/commands/list.c +++ /dev/null @@ -1,2635 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * Copyright (C) 2020 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#define _LGPL_SOURCE -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../command.h" - -static int opt_userspace; -static int opt_kernel; -static int opt_jul; -static int opt_log4j; -static int opt_python; -static char *opt_channel; -static int opt_domain; -static int opt_fields; -static int opt_syscall; - -const char *indent4 = " "; -const char *indent6 = " "; -const char *indent8 = " "; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_USERSPACE, - OPT_LIST_OPTIONS, -}; - -static struct lttng_handle *the_handle; -static struct mi_writer *the_writer; - -/* Only set when listing a single session. */ -static struct lttng_session the_listed_session; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, - {"jul", 'j', POPT_ARG_VAL, &opt_jul, 1, 0, 0}, - {"log4j", 'l', POPT_ARG_VAL, &opt_log4j, 1, 0, 0}, - {"python", 'p', POPT_ARG_VAL, &opt_python, 1, 0, 0}, - {"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0}, - {"channel", 'c', POPT_ARG_STRING, &opt_channel, 0, 0, 0}, - {"domain", 'd', POPT_ARG_VAL, &opt_domain, 1, 0, 0}, - {"fields", 'f', POPT_ARG_VAL, &opt_fields, 1, 0, 0}, - {"syscall", 'S', POPT_ARG_VAL, &opt_syscall, 1, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * Get command line from /proc for a specific pid. - * - * On success, return an allocated string pointer to the proc cmdline. - * On error, return NULL. - */ -static char *get_cmdline_by_pid(pid_t pid) -{ - int ret; - FILE *fp = NULL; - char *cmdline = NULL; - /* Can't go bigger than /proc/LTTNG_MAX_PID/cmdline */ - char path[sizeof("/proc//cmdline") + sizeof(LTTNG_MAX_PID_STR) - 1]; - - snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); - fp = fopen(path, "r"); - if (fp == NULL) { - goto end; - } - - /* Caller must free() *cmdline */ - cmdline = zmalloc(PATH_MAX); - if (!cmdline) { - PERROR("malloc cmdline"); - goto end; - } - ret = fread(cmdline, 1, PATH_MAX, fp); - if (ret < 0) { - PERROR("fread proc list"); - } - -end: - if (fp) { - fclose(fp); - } - return cmdline; -} - -static -const char *active_string(int value) -{ - switch (value) { - case 0: return "inactive"; - case 1: return "active"; - case -1: return ""; - default: return NULL; - } -} - -static const char *snapshot_string(int value) -{ - switch (value) { - case 1: - return " snapshot"; - default: - return ""; - } -} - -static -const char *enabled_string(int value) -{ - switch (value) { - case 0: return " [disabled]"; - case 1: return " [enabled]"; - case -1: return ""; - default: return NULL; - } -} - -static -const char *safe_string(const char *str) -{ - return str ? str : ""; -} - -static const char *logleveltype_string(enum lttng_loglevel_type value) -{ - switch (value) { - case LTTNG_EVENT_LOGLEVEL_ALL: - return ":"; - case LTTNG_EVENT_LOGLEVEL_RANGE: - return " <="; - case LTTNG_EVENT_LOGLEVEL_SINGLE: - return " =="; - default: - return " <>"; - } -} - -static const char *bitness_event(enum lttng_event_flag flags) -{ - if (flags & LTTNG_EVENT_FLAG_SYSCALL_32) { - if (flags & LTTNG_EVENT_FLAG_SYSCALL_64) { - return " [32/64-bit]"; - } else { - return " [32-bit]"; - } - } else if (flags & LTTNG_EVENT_FLAG_SYSCALL_64) { - return " [64-bit]"; - } else { - return ""; - } -} - -/* - * Get exclusion names message for a single event. - * - * Returned pointer must be freed by caller. Returns NULL on error. - */ -static char *get_exclusion_names_msg(struct lttng_event *event) -{ - int ret; - int exclusion_count; - char *exclusion_msg = NULL; - char *at; - size_t i; - const char * const exclusion_fmt = " [exclusions: "; - const size_t exclusion_fmt_len = strlen(exclusion_fmt); - - exclusion_count = lttng_event_get_exclusion_name_count(event); - if (exclusion_count < 0) { - goto end; - } else if (exclusion_count == 0) { - /* - * No exclusions: return copy of empty string so that - * it can be freed by caller. - */ - exclusion_msg = strdup(""); - goto end; - } - - /* - * exclusion_msg's size is bounded by the exclusion_fmt string, - * a comma per entry, the entry count (fixed-size), a closing - * bracket, and a trailing \0. - */ - exclusion_msg = malloc(exclusion_count + - exclusion_count * LTTNG_SYMBOL_NAME_LEN + - exclusion_fmt_len + 1); - if (!exclusion_msg) { - goto end; - } - - at = strcpy(exclusion_msg, exclusion_fmt) + exclusion_fmt_len; - for (i = 0; i < exclusion_count; ++i) { - const char *name; - - /* Append comma between exclusion names */ - if (i > 0) { - *at = ','; - at++; - } - - ret = lttng_event_get_exclusion_name(event, i, &name); - if (ret) { - /* Prints '?' on local error; should never happen */ - *at = '?'; - at++; - continue; - } - - /* Append exclusion name */ - at += sprintf(at, "%s", name); - } - - /* This also puts a final '\0' at the end of exclusion_msg */ - strcpy(at, "]"); - -end: - return exclusion_msg; -} - -static void print_userspace_probe_location(struct lttng_event *event) -{ - const struct lttng_userspace_probe_location *location; - const struct lttng_userspace_probe_location_lookup_method *lookup_method; - enum lttng_userspace_probe_location_lookup_method_type lookup_type; - - location = lttng_event_get_userspace_probe_location(event); - if (!location) { - MSG("Event has no userspace probe location"); - return; - } - - lookup_method = lttng_userspace_probe_location_get_lookup_method(location); - if (!lookup_method) { - MSG("Event has no userspace probe location lookup method"); - return; - } - - MSG("%s%s (type: userspace-probe)%s", indent6, event->name, enabled_string(event->enabled)); - - lookup_type = lttng_userspace_probe_location_lookup_method_get_type(lookup_method); - - switch (lttng_userspace_probe_location_get_type(location)) { - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_UNKNOWN: - MSG("%sType: Unknown", indent8); - break; - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: - { - const char *function_name; - char *binary_path; - - MSG("%sType: Function", indent8); - function_name = lttng_userspace_probe_location_function_get_function_name(location); - binary_path = realpath(lttng_userspace_probe_location_function_get_binary_path(location), NULL); - - MSG("%sBinary path: %s", indent8, binary_path ? binary_path : "NULL"); - MSG("%sFunction: %s()", indent8, function_name ? function_name : "NULL"); - switch (lookup_type) { - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: - MSG("%sLookup method: ELF", indent8); - break; - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT: - MSG("%sLookup method: default", indent8); - break; - default: - MSG("%sLookup method: INVALID LOOKUP TYPE ENCOUNTERED", indent8); - break; - } - - free(binary_path); - break; - } - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: - { - const char *probe_name, *provider_name; - char *binary_path; - - MSG("%sType: Tracepoint", indent8); - probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name(location); - provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name(location); - binary_path = realpath(lttng_userspace_probe_location_tracepoint_get_binary_path(location), NULL); - MSG("%sBinary path: %s", indent8, binary_path ? binary_path : "NULL"); - MSG("%sTracepoint: %s:%s", indent8, provider_name ? provider_name : "NULL", probe_name ? probe_name : "NULL"); - switch (lookup_type) { - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: - MSG("%sLookup method: SDT", indent8); - break; - default: - MSG("%sLookup method: INVALID LOOKUP TYPE ENCOUNTERED", indent8); - break; - } - - free(binary_path); - break; - } - default: - ERR("Invalid probe type encountered"); - } -} - -/* - * Pretty print single event. - */ -static void print_events(struct lttng_event *event) -{ - int ret; - const char *filter_str; - char *filter_msg = NULL; - char *exclusion_msg = NULL; - - ret = lttng_event_get_filter_expression(event, &filter_str); - - if (ret) { - filter_msg = strdup(" [failed to retrieve filter]"); - } else if (filter_str) { - const char * const filter_fmt = " [filter: '%s']"; - - filter_msg = malloc(strlen(filter_str) + - strlen(filter_fmt) + 1); - if (filter_msg) { - sprintf(filter_msg, filter_fmt, - filter_str); - } - } - - exclusion_msg = get_exclusion_names_msg(event); - if (!exclusion_msg) { - exclusion_msg = strdup(" [failed to retrieve exclusions]"); - } - - switch (event->type) { - case LTTNG_EVENT_TRACEPOINT: - { - if (event->loglevel != -1) { - MSG("%s%s (loglevel%s %s (%d)) (type: tracepoint)%s%s%s", - indent6, event->name, - logleveltype_string( - event->loglevel_type), - mi_lttng_loglevel_string( - event->loglevel, - the_handle->domain.type), - event->loglevel, - enabled_string(event->enabled), - safe_string(exclusion_msg), - safe_string(filter_msg)); - } else { - MSG("%s%s (type: tracepoint)%s%s%s", - indent6, - event->name, - enabled_string(event->enabled), - safe_string(exclusion_msg), - safe_string(filter_msg)); - } - break; - } - case LTTNG_EVENT_FUNCTION: - MSG("%s%s (type: function)%s%s", indent6, - event->name, enabled_string(event->enabled), - safe_string(filter_msg)); - if (event->attr.probe.addr != 0) { - MSG("%saddr: 0x%" PRIx64, indent8, event->attr.probe.addr); - } else { - MSG("%soffset: 0x%" PRIx64, indent8, event->attr.probe.offset); - MSG("%ssymbol: %s", indent8, event->attr.probe.symbol_name); - } - break; - case LTTNG_EVENT_PROBE: - MSG("%s%s (type: probe)%s%s", indent6, - event->name, enabled_string(event->enabled), - safe_string(filter_msg)); - if (event->attr.probe.addr != 0) { - MSG("%saddr: 0x%" PRIx64, indent8, event->attr.probe.addr); - } else { - MSG("%soffset: 0x%" PRIx64, indent8, event->attr.probe.offset); - MSG("%ssymbol: %s", indent8, event->attr.probe.symbol_name); - } - break; - case LTTNG_EVENT_USERSPACE_PROBE: - print_userspace_probe_location(event); - break; - case LTTNG_EVENT_FUNCTION_ENTRY: - MSG("%s%s (type: function)%s%s", indent6, - event->name, enabled_string(event->enabled), - safe_string(filter_msg)); - MSG("%ssymbol: \"%s\"", indent8, event->attr.ftrace.symbol_name); - break; - case LTTNG_EVENT_SYSCALL: - MSG("%s%s%s%s%s%s", indent6, event->name, - (opt_syscall ? "" : " (type:syscall)"), - enabled_string(event->enabled), - bitness_event(event->flags), - safe_string(filter_msg)); - break; - case LTTNG_EVENT_NOOP: - MSG("%s (type: noop)%s%s", indent6, - enabled_string(event->enabled), - safe_string(filter_msg)); - break; - case LTTNG_EVENT_ALL: - /* Fall-through. */ - default: - /* We should never have "all" events in list. */ - abort(); - break; - } - - free(filter_msg); - free(exclusion_msg); -} - -static const char *field_type(struct lttng_event_field *field) -{ - switch(field->type) { - case LTTNG_EVENT_FIELD_INTEGER: - return "integer"; - case LTTNG_EVENT_FIELD_ENUM: - return "enum"; - case LTTNG_EVENT_FIELD_FLOAT: - return "float"; - case LTTNG_EVENT_FIELD_STRING: - return "string"; - case LTTNG_EVENT_FIELD_OTHER: - default: /* fall-through */ - return "unknown"; - } -} - -/* - * Pretty print single event fields. - */ -static void print_event_field(struct lttng_event_field *field) -{ - if (!field->field_name[0]) { - return; - } - MSG("%sfield: %s (%s)%s", indent8, field->field_name, - field_type(field), field->nowrite ? " [no write]" : ""); -} - -/* - * Machine interface - * Jul and ust event listing - */ -static int mi_list_agent_ust_events(struct lttng_event *events, int count, - struct lttng_domain *domain) -{ - int ret, i; - pid_t cur_pid = 0; - char *cmdline = NULL; - int pid_element_open = 0; - - /* Open domains element */ - ret = mi_lttng_domains_open(the_writer); - if (ret) { - goto end; - } - - /* Write domain */ - ret = mi_lttng_domain(the_writer, domain, 1); - if (ret) { - goto end; - } - - /* Open pids element element */ - ret = mi_lttng_pids_open(the_writer); - if (ret) { - goto end; - } - - for (i = 0; i < count; i++) { - if (cur_pid != events[i].pid) { - if (pid_element_open) { - /* Close the previous events and pid element */ - ret = mi_lttng_close_multi_element( - the_writer, 2); - if (ret) { - goto end; - } - pid_element_open = 0; - } - - cur_pid = events[i].pid; - cmdline = get_cmdline_by_pid(cur_pid); - if (!cmdline) { - ret = CMD_ERROR; - goto end; - } - - if (!pid_element_open) { - /* Open and write a pid element */ - ret = mi_lttng_pid(the_writer, cur_pid, cmdline, - 1); - if (ret) { - goto error; - } - - /* Open events element */ - ret = mi_lttng_events_open(the_writer); - if (ret) { - goto error; - } - - pid_element_open = 1; - } - free(cmdline); - } - - /* Write an event */ - ret = mi_lttng_event(the_writer, &events[i], 0, - the_handle->domain.type); - if (ret) { - goto end; - } - } - - /* Close pids */ - ret = mi_lttng_writer_close_element(the_writer); - if (ret) { - goto end; - } - - /* Close domain, domains */ - ret = mi_lttng_close_multi_element(the_writer, 2); -end: - return ret; -error: - free(cmdline); - return ret; -} - -static int list_agent_events(void) -{ - int i, size, ret = CMD_SUCCESS; - struct lttng_domain domain; - struct lttng_handle *handle = NULL; - struct lttng_event *event_list = NULL; - pid_t cur_pid = 0; - char *cmdline = NULL; - const char *agent_domain_str; - - memset(&domain, 0, sizeof(domain)); - if (opt_jul) { - domain.type = LTTNG_DOMAIN_JUL; - } else if (opt_log4j) { - domain.type = LTTNG_DOMAIN_LOG4J; - } else if (opt_python) { - domain.type = LTTNG_DOMAIN_PYTHON; - } else { - ERR("Invalid agent domain selected."); - ret = CMD_ERROR; - goto error; - } - - agent_domain_str = lttng_domain_type_str(domain.type); - - DBG("Getting %s tracing events", agent_domain_str); - - handle = lttng_create_handle(NULL, &domain); - if (handle == NULL) { - ret = CMD_ERROR; - goto end; - } - - size = lttng_list_tracepoints(handle, &event_list); - if (size < 0) { - ERR("Unable to list %s events: %s", agent_domain_str, - lttng_strerror(size)); - ret = CMD_ERROR; - goto end; - } - - if (lttng_opt_mi) { - /* Mi print */ - ret = mi_list_agent_ust_events(event_list, size, &domain); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } else { - /* Pretty print */ - MSG("%s events (Logger name):\n-------------------------", - agent_domain_str); - - if (size == 0) { - MSG("None"); - } - - for (i = 0; i < size; i++) { - if (cur_pid != event_list[i].pid) { - cur_pid = event_list[i].pid; - cmdline = get_cmdline_by_pid(cur_pid); - if (cmdline == NULL) { - ret = CMD_ERROR; - goto error; - } - MSG("\nPID: %d - Name: %s", cur_pid, cmdline); - free(cmdline); - } - MSG("%s- %s", indent6, event_list[i].name); - } - - MSG(""); - } - -error: - free(event_list); -end: - lttng_destroy_handle(handle); - return ret; -} - -/* - * Ask session daemon for all user space tracepoints available. - */ -static int list_ust_events(void) -{ - int i, size, ret = CMD_SUCCESS; - struct lttng_domain domain; - struct lttng_handle *handle; - struct lttng_event *event_list = NULL; - pid_t cur_pid = 0; - char *cmdline = NULL; - - memset(&domain, 0, sizeof(domain)); - - DBG("Getting UST tracing events"); - - domain.type = LTTNG_DOMAIN_UST; - - handle = lttng_create_handle(NULL, &domain); - if (handle == NULL) { - ret = CMD_ERROR; - goto end; - } - - size = lttng_list_tracepoints(handle, &event_list); - if (size < 0) { - ERR("Unable to list UST events: %s", lttng_strerror(size)); - ret = CMD_ERROR; - goto error; - } - - if (lttng_opt_mi) { - /* Mi print */ - ret = mi_list_agent_ust_events(event_list, size, &domain); - } else { - /* Pretty print */ - MSG("UST events:\n-------------"); - - if (size == 0) { - MSG("None"); - } - - for (i = 0; i < size; i++) { - if (cur_pid != event_list[i].pid) { - cur_pid = event_list[i].pid; - cmdline = get_cmdline_by_pid(cur_pid); - if (cmdline == NULL) { - ret = CMD_ERROR; - goto error; - } - MSG("\nPID: %d - Name: %s", cur_pid, cmdline); - free(cmdline); - } - print_events(&event_list[i]); - } - - MSG(""); - } - -error: - free(event_list); -end: - lttng_destroy_handle(handle); - return ret; -} - -/* - * Machine interface - * List all ust event with their fields - */ -static int mi_list_ust_event_fields(struct lttng_event_field *fields, int count, - struct lttng_domain *domain) -{ - int ret, i; - pid_t cur_pid = 0; - char *cmdline = NULL; - int pid_element_open = 0; - int event_element_open = 0; - struct lttng_event cur_event; - - memset(&cur_event, 0, sizeof(cur_event)); - - /* Open domains element */ - ret = mi_lttng_domains_open(the_writer); - if (ret) { - goto end; - } - - /* Write domain */ - ret = mi_lttng_domain(the_writer, domain, 1); - if (ret) { - goto end; - } - - /* Open pids element */ - ret = mi_lttng_pids_open(the_writer); - if (ret) { - goto end; - } - - for (i = 0; i < count; i++) { - if (cur_pid != fields[i].event.pid) { - if (pid_element_open) { - if (event_element_open) { - /* Close the previous field element and event. */ - ret = mi_lttng_close_multi_element( - the_writer, 2); - if (ret) { - goto end; - } - event_element_open = 0; - } - /* Close the previous events, pid element */ - ret = mi_lttng_close_multi_element( - the_writer, 2); - if (ret) { - goto end; - } - pid_element_open = 0; - } - - cur_pid = fields[i].event.pid; - cmdline = get_cmdline_by_pid(cur_pid); - if (!pid_element_open) { - /* Open and write a pid element */ - ret = mi_lttng_pid(the_writer, cur_pid, cmdline, - 1); - if (ret) { - goto error; - } - - /* Open events element */ - ret = mi_lttng_events_open(the_writer); - if (ret) { - goto error; - } - pid_element_open = 1; - } - free(cmdline); - /* Wipe current event since we are about to print a new PID. */ - memset(&cur_event, 0, sizeof(cur_event)); - } - - if (strcmp(cur_event.name, fields[i].event.name) != 0) { - if (event_element_open) { - /* Close the previous fields element and the previous event */ - ret = mi_lttng_close_multi_element( - the_writer, 2); - if (ret) { - goto end; - } - event_element_open = 0; - } - - memcpy(&cur_event, &fields[i].event, - sizeof(cur_event)); - - if (!event_element_open) { - /* Open and write the event */ - ret = mi_lttng_event(the_writer, &cur_event, 1, - the_handle->domain.type); - if (ret) { - goto end; - } - - /* Open a fields element */ - ret = mi_lttng_event_fields_open(the_writer); - if (ret) { - goto end; - } - event_element_open = 1; - } - } - - /* Print the event_field */ - ret = mi_lttng_event_field(the_writer, &fields[i]); - if (ret) { - goto end; - } - } - - /* Close pids, domain, domains */ - ret = mi_lttng_close_multi_element(the_writer, 3); -end: - return ret; -error: - free(cmdline); - return ret; -} - -/* - * Ask session daemon for all user space tracepoint fields available. - */ -static int list_ust_event_fields(void) -{ - int i, size, ret = CMD_SUCCESS; - struct lttng_domain domain; - struct lttng_handle *handle; - struct lttng_event_field *event_field_list; - pid_t cur_pid = 0; - char *cmdline = NULL; - - struct lttng_event cur_event; - - memset(&domain, 0, sizeof(domain)); - memset(&cur_event, 0, sizeof(cur_event)); - - DBG("Getting UST tracing event fields"); - - domain.type = LTTNG_DOMAIN_UST; - - handle = lttng_create_handle(NULL, &domain); - if (handle == NULL) { - ret = CMD_ERROR; - goto end; - } - - size = lttng_list_tracepoint_fields(handle, &event_field_list); - if (size < 0) { - ERR("Unable to list UST event fields: %s", lttng_strerror(size)); - ret = CMD_ERROR; - goto end; - } - - if (lttng_opt_mi) { - /* Mi print */ - ret = mi_list_ust_event_fields(event_field_list, size, &domain); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } else { - /* Pretty print */ - MSG("UST events:\n-------------"); - - if (size == 0) { - MSG("None"); - } - - for (i = 0; i < size; i++) { - if (cur_pid != event_field_list[i].event.pid) { - cur_pid = event_field_list[i].event.pid; - cmdline = get_cmdline_by_pid(cur_pid); - if (cmdline == NULL) { - ret = CMD_ERROR; - goto error; - } - MSG("\nPID: %d - Name: %s", cur_pid, cmdline); - free(cmdline); - /* Wipe current event since we are about to print a new PID. */ - memset(&cur_event, 0, sizeof(cur_event)); - } - if (strcmp(cur_event.name, event_field_list[i].event.name) != 0) { - print_events(&event_field_list[i].event); - memcpy(&cur_event, &event_field_list[i].event, - sizeof(cur_event)); - } - print_event_field(&event_field_list[i]); - } - - MSG(""); - } - -error: - free(event_field_list); -end: - lttng_destroy_handle(handle); - return ret; -} - -/* - * Machine interface - * Print a list of kernel events - */ -static int mi_list_kernel_events(struct lttng_event *events, int count, - struct lttng_domain *domain) -{ - int ret, i; - - /* Open domains element */ - ret = mi_lttng_domains_open(the_writer); - if (ret) { - goto end; - } - - /* Write domain */ - ret = mi_lttng_domain(the_writer, domain, 1); - if (ret) { - goto end; - } - - /* Open events */ - ret = mi_lttng_events_open(the_writer); - if (ret) { - goto end; - } - - for (i = 0; i < count; i++) { - ret = mi_lttng_event(the_writer, &events[i], 0, - the_handle->domain.type); - if (ret) { - goto end; - } - } - - /* close events, domain and domains */ - ret = mi_lttng_close_multi_element(the_writer, 3); - if (ret) { - goto end; - } - -end: - return ret; -} - -/* - * Ask for all trace events in the kernel - */ -static int list_kernel_events(void) -{ - int i, size, ret = CMD_SUCCESS; - struct lttng_domain domain; - struct lttng_handle *handle; - struct lttng_event *event_list; - - memset(&domain, 0, sizeof(domain)); - - DBG("Getting kernel tracing events"); - - domain.type = LTTNG_DOMAIN_KERNEL; - - handle = lttng_create_handle(NULL, &domain); - if (handle == NULL) { - ret = CMD_ERROR; - goto error; - } - - size = lttng_list_tracepoints(handle, &event_list); - if (size < 0) { - ERR("Unable to list kernel events: %s", lttng_strerror(size)); - lttng_destroy_handle(handle); - return CMD_ERROR; - } - - if (lttng_opt_mi) { - /* Mi print */ - ret = mi_list_kernel_events(event_list, size, &domain); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } else { - MSG("Kernel events:\n-------------"); - - for (i = 0; i < size; i++) { - print_events(&event_list[i]); - } - - MSG(""); - } - -end: - free(event_list); - - lttng_destroy_handle(handle); - return ret; - -error: - lttng_destroy_handle(handle); - return ret; -} - -/* - * Machine interface - * Print a list of system calls. - */ -static int mi_list_syscalls(struct lttng_event *events, int count) -{ - int ret, i; - - /* Open events */ - ret = mi_lttng_events_open(the_writer); - if (ret) { - goto end; - } - - for (i = 0; i < count; i++) { - ret = mi_lttng_event(the_writer, &events[i], 0, - the_handle->domain.type); - if (ret) { - goto end; - } - } - - /* Close events. */ - ret = mi_lttng_writer_close_element(the_writer); - if (ret) { - goto end; - } - -end: - return ret; -} - -/* - * Ask for kernel system calls. - */ -static int list_syscalls(void) -{ - int i, size, ret = CMD_SUCCESS; - struct lttng_event *event_list; - - DBG("Getting kernel system call events"); - - size = lttng_list_syscalls(&event_list); - if (size < 0) { - ERR("Unable to list system calls: %s", lttng_strerror(size)); - ret = CMD_ERROR; - goto error; - } - - if (lttng_opt_mi) { - /* Mi print */ - ret = mi_list_syscalls(event_list, size); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } else { - MSG("System calls:\n-------------"); - - for (i = 0; i < size; i++) { - print_events(&event_list[i]); - } - - MSG(""); - } - -end: - free(event_list); - return ret; - -error: - return ret; -} - -/* - * Machine Interface - * Print a list of agent events - */ -static int mi_list_session_agent_events(struct lttng_event *events, int count) -{ - int ret, i; - - /* Open events element */ - ret = mi_lttng_events_open(the_writer); - if (ret) { - goto end; - } - - for (i = 0; i < count; i++) { - ret = mi_lttng_event(the_writer, &events[i], 0, - the_handle->domain.type); - if (ret) { - goto end; - } - } - - /* Close events element */ - ret = mi_lttng_writer_close_element(the_writer); - -end: - return ret; -} - -/* - * List agent events for a specific session using the handle. - * - * Return CMD_SUCCESS on success else a negative value. - */ -static int list_session_agent_events(void) -{ - int ret = CMD_SUCCESS, count, i; - struct lttng_event *events = NULL; - - count = lttng_list_events(the_handle, "", &events); - if (count < 0) { - ret = CMD_ERROR; - ERR("%s", lttng_strerror(count)); - goto error; - } - - if (lttng_opt_mi) { - /* Mi print */ - ret = mi_list_session_agent_events(events, count); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } else { - /* Pretty print */ - MSG("Events (Logger name):\n---------------------"); - if (count == 0) { - MSG("%sNone\n", indent6); - goto end; - } - - for (i = 0; i < count; i++) { - const char *filter_str; - char *filter_msg = NULL; - struct lttng_event *event = &events[i]; - - ret = lttng_event_get_filter_expression(event, - &filter_str); - if (ret) { - filter_msg = strdup(" [failed to retrieve filter]"); - } else if (filter_str) { - const char * const filter_fmt = - " [filter: '%s']"; - - filter_msg = malloc(strlen(filter_str) + - strlen(filter_fmt) + 1); - if (filter_msg) { - sprintf(filter_msg, filter_fmt, - filter_str); - } - } - - if (event->loglevel_type != - LTTNG_EVENT_LOGLEVEL_ALL) { - MSG("%s- %s%s (loglevel%s %s)%s", indent4, - event->name, - enabled_string(event->enabled), - logleveltype_string( - event->loglevel_type), - mi_lttng_loglevel_string( - event->loglevel, - the_handle->domain - .type), - safe_string(filter_msg)); - } else { - MSG("%s- %s%s%s", indent4, event->name, - enabled_string(event->enabled), - safe_string(filter_msg)); - } - free(filter_msg); - } - - MSG(""); - } - -end: - free(events); -error: - return ret; -} - -/* - * Machine interface - * print a list of event - */ -static int mi_list_events(struct lttng_event *events, int count) -{ - int ret, i; - - /* Open events element */ - ret = mi_lttng_events_open(the_writer); - if (ret) { - goto end; - } - - for (i = 0; i < count; i++) { - ret = mi_lttng_event(the_writer, &events[i], 0, - the_handle->domain.type); - if (ret) { - goto end; - } - } - - /* Close events element */ - ret = mi_lttng_writer_close_element(the_writer); - -end: - return ret; -} - -/* - * List events of channel of session and domain. - */ -static int list_events(const char *channel_name) -{ - int ret = CMD_SUCCESS, count, i; - struct lttng_event *events = NULL; - - count = lttng_list_events(the_handle, channel_name, &events); - if (count < 0) { - ret = CMD_ERROR; - ERR("%s", lttng_strerror(count)); - goto error; - } - - if (lttng_opt_mi) { - /* Mi print */ - ret = mi_list_events(events, count); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } else { - /* Pretty print */ - MSG("\n%sRecording event rules:", indent4); - if (count == 0) { - MSG("%sNone\n", indent6); - goto end; - } - - for (i = 0; i < count; i++) { - print_events(&events[i]); - } - - MSG(""); - } -end: - free(events); -error: - return ret; -} - -static -void print_timer(const char *timer_name, uint32_t space_count, int64_t value) -{ - uint32_t i; - - _MSG("%s%s:", indent6, timer_name); - for (i = 0; i < space_count; i++) { - _MSG(" "); - } - - if (value) { - MSG("%" PRId64 " %s", value, USEC_UNIT); - } else { - MSG("inactive"); - } -} - -/* - * Pretty print channel - */ -static void print_channel(struct lttng_channel *channel) -{ - int ret; - uint64_t discarded_events, lost_packets, monitor_timer_interval; - int64_t blocking_timeout; - - ret = lttng_channel_get_discarded_event_count(channel, - &discarded_events); - if (ret) { - ERR("Failed to retrieve discarded event count of channel"); - return; - } - - ret = lttng_channel_get_lost_packet_count(channel, - &lost_packets); - if (ret) { - ERR("Failed to retrieve lost packet count of channel"); - return; - } - - ret = lttng_channel_get_monitor_timer_interval(channel, - &monitor_timer_interval); - if (ret) { - ERR("Failed to retrieve monitor interval of channel"); - return; - } - - ret = lttng_channel_get_blocking_timeout(channel, - &blocking_timeout); - if (ret) { - ERR("Failed to retrieve blocking timeout of channel"); - return; - } - - MSG("- %s:%s\n", channel->name, enabled_string(channel->enabled)); - MSG("%sAttributes:", indent4); - MSG("%sEvent-loss mode: %s", indent6, channel->attr.overwrite ? "overwrite" : "discard"); - MSG("%sSub-buffer size: %" PRIu64 " bytes", indent6, channel->attr.subbuf_size); - MSG("%sSub-buffer count: %" PRIu64, indent6, channel->attr.num_subbuf); - - print_timer("Switch timer", 5, channel->attr.switch_timer_interval); - print_timer("Read timer", 7, channel->attr.read_timer_interval); - print_timer("Monitor timer", 4, monitor_timer_interval); - - if (!channel->attr.overwrite) { - if (blocking_timeout == -1) { - MSG("%sBlocking timeout: infinite", indent6); - } else { - MSG("%sBlocking timeout: %" PRId64 " %s", indent6, - blocking_timeout, USEC_UNIT); - } - } - - MSG("%sTrace file count: %" PRIu64 " per stream", indent6, - channel->attr.tracefile_count == 0 ? - 1 : channel->attr.tracefile_count); - if (channel->attr.tracefile_size != 0 ) { - MSG("%sTrace file size: %" PRIu64 " bytes", indent6, - channel->attr.tracefile_size); - } else { - MSG("%sTrace file size: %s", indent6, "unlimited"); - } - switch (channel->attr.output) { - case LTTNG_EVENT_SPLICE: - MSG("%sOutput mode: splice", indent6); - break; - case LTTNG_EVENT_MMAP: - MSG("%sOutput mode: mmap", indent6); - break; - } - - MSG("\n%sStatistics:", indent4); - if (the_listed_session.snapshot_mode) { - /* - * The lost packet count is omitted for sessions in snapshot - * mode as it is misleading: it would indicate the number of - * packets that the consumer could not extract during the - * course of recording the snapshot. It does not have the - * same meaning as the "regular" lost packet count that - * would result from the consumer not keeping up with - * event production in an overwrite-mode channel. - * - * A more interesting statistic would be the number of - * packets lost between the first and last extracted - * packets of a given snapshot (which prevents most analyses). - */ - MSG("%sNone", indent6); - goto skip_stats_printing; - } - - if (!channel->attr.overwrite) { - MSG("%sDiscarded events: %" PRIu64, indent6, discarded_events); - } else { - MSG("%sLost packets: %" PRIu64, indent6, lost_packets); - } -skip_stats_printing: - return; -} - -/* - * Machine interface - * Print a list of channel - * - */ -static int mi_list_channels(struct lttng_channel *channels, int count, - const char *channel_name) -{ - int i, ret; - unsigned int chan_found = 0; - - /* Open channels element */ - ret = mi_lttng_channels_open(the_writer); - if (ret) { - goto error; - } - - for (i = 0; i < count; i++) { - if (channel_name != NULL) { - if (strncmp(channels[i].name, channel_name, NAME_MAX) == 0) { - chan_found = 1; - } else { - continue; - } - } - - /* Write channel element and leave it open */ - ret = mi_lttng_channel(the_writer, &channels[i], 1); - if (ret) { - goto error; - } - - /* Listing events per channel */ - ret = list_events(channels[i].name); - if (ret) { - goto error; - } - - /* Closing the channel element we opened earlier */ - ret = mi_lttng_writer_close_element(the_writer); - if (ret) { - goto error; - } - - if (chan_found) { - break; - } - } - - /* Close channels element */ - ret = mi_lttng_writer_close_element(the_writer); - if (ret) { - goto error; - } - -error: - return ret; -} - -/* - * List channel(s) of session and domain. - * - * If channel_name is NULL, all channels are listed. - */ -static int list_channels(const char *channel_name) -{ - int count, i, ret = CMD_SUCCESS; - unsigned int chan_found = 0; - struct lttng_channel *channels = NULL; - - DBG("Listing channel(s) (%s)", channel_name ? : ""); - - count = lttng_list_channels(the_handle, &channels); - if (count < 0) { - switch (-count) { - case LTTNG_ERR_KERN_CHAN_NOT_FOUND: - if (lttng_opt_mi) { - /* When printing mi this is not an error - * but an empty channels element */ - count = 0; - } else { - ret = CMD_SUCCESS; - goto error_channels; - } - break; - default: - /* We had a real error */ - ret = CMD_ERROR; - ERR("%s", lttng_strerror(count)); - goto error_channels; - break; - } - } - - if (lttng_opt_mi) { - /* Mi print */ - ret = mi_list_channels(channels, count, channel_name); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } else { - /* Pretty print */ - if (count) { - MSG("Channels:\n-------------"); - } - - for (i = 0; i < count; i++) { - if (channel_name != NULL) { - if (strncmp(channels[i].name, channel_name, NAME_MAX) == 0) { - chan_found = 1; - } else { - continue; - } - } - print_channel(&channels[i]); - - /* Listing events per channel */ - ret = list_events(channels[i].name); - if (ret) { - goto error; - } - - if (chan_found) { - break; - } - } - - if (!chan_found && channel_name != NULL) { - ret = CMD_ERROR; - ERR("Channel %s not found", channel_name); - goto error; - } - } -error: - free(channels); - -error_channels: - return ret; -} - -static const char *get_capitalized_process_attr_str(enum lttng_process_attr process_attr) -{ - switch (process_attr) { - case LTTNG_PROCESS_ATTR_PROCESS_ID: - return "Process ID"; - case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: - return "Virtual process ID"; - case LTTNG_PROCESS_ATTR_USER_ID: - return "User ID"; - case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: - return "Virtual user ID"; - case LTTNG_PROCESS_ATTR_GROUP_ID: - return "Group ID"; - case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: - return "Virtual group ID"; - default: - return "Unknown"; - } - return NULL; -} - -static int handle_process_attr_status(enum lttng_process_attr process_attr, - enum lttng_process_attr_tracker_handle_status status) -{ - int ret = CMD_SUCCESS; - - switch (status) { - case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_INVALID_TRACKING_POLICY: - case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK: - /* Carry on. */ - break; - case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_COMMUNICATION_ERROR: - ERR("Communication occurred while fetching %s tracker", - lttng_process_attr_to_string(process_attr)); - ret = CMD_ERROR; - break; - case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_SESSION_DOES_NOT_EXIST: - ERR("Failed to get the inclusion set of the %s tracker: session `%s` no longer exists", - lttng_process_attr_to_string(process_attr), - the_handle->session_name); - ret = CMD_ERROR; - break; - default: - ERR("Unknown error occurred while fetching the inclusion set of the %s tracker", - lttng_process_attr_to_string(process_attr)); - ret = CMD_ERROR; - break; - } - - return ret; -} - -static int mi_output_empty_tracker(enum lttng_process_attr process_attr) -{ - int ret; - - ret = mi_lttng_process_attribute_tracker_open(the_writer, process_attr); - if (ret) { - goto end; - } - - ret = mi_lttng_close_multi_element(the_writer, 2); -end: - return ret; -} - -static inline bool is_value_type_name( - enum lttng_process_attr_value_type value_type) -{ - return value_type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME || - value_type == LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME; -} - -/* - * List a process attribute tracker for a session and domain tuple. - */ -static int list_process_attr_tracker(enum lttng_process_attr process_attr) -{ - int ret = 0; - unsigned int count, i; - enum lttng_tracking_policy policy; - enum lttng_error_code ret_code; - enum lttng_process_attr_tracker_handle_status handle_status; - enum lttng_process_attr_values_status values_status; - const struct lttng_process_attr_values *values; - struct lttng_process_attr_tracker_handle *tracker_handle = NULL; - - ret_code = lttng_session_get_tracker_handle(the_handle->session_name, - the_handle->domain.type, process_attr, &tracker_handle); - if (ret_code != LTTNG_OK) { - ERR("Failed to get process attribute tracker handle: %s", - lttng_strerror(ret_code)); - ret = CMD_ERROR; - goto end; - } - - handle_status = lttng_process_attr_tracker_handle_get_inclusion_set( - tracker_handle, &values); - ret = handle_process_attr_status(process_attr, handle_status); - if (ret != CMD_SUCCESS) { - goto end; - } - - handle_status = lttng_process_attr_tracker_handle_get_tracking_policy( - tracker_handle, &policy); - ret = handle_process_attr_status(process_attr, handle_status); - if (ret != CMD_SUCCESS) { - goto end; - } - - { - char *process_attr_name; - const int print_ret = asprintf(&process_attr_name, "%ss:", - get_capitalized_process_attr_str(process_attr)); - - if (print_ret == -1) { - ret = CMD_FATAL; - goto end; - } - _MSG(" %-22s", process_attr_name); - free(process_attr_name); - } - switch (policy) { - case LTTNG_TRACKING_POLICY_INCLUDE_SET: - break; - case LTTNG_TRACKING_POLICY_EXCLUDE_ALL: - if (the_writer) { - mi_output_empty_tracker(process_attr); - } - MSG("none"); - ret = CMD_SUCCESS; - goto end; - case LTTNG_TRACKING_POLICY_INCLUDE_ALL: - MSG("all"); - ret = CMD_SUCCESS; - goto end; - default: - ERR("Unknown tracking policy encoutered while listing the %s process attribute tracker of session `%s`", - lttng_process_attr_to_string(process_attr), - the_handle->session_name); - ret = CMD_FATAL; - goto end; - } - - values_status = lttng_process_attr_values_get_count(values, &count); - if (values_status != LTTNG_PROCESS_ATTR_VALUES_STATUS_OK) { - ERR("Failed to get the count of values in the inclusion set of the %s process attribute tracker of session `%s`", - lttng_process_attr_to_string(process_attr), - the_handle->session_name); - ret = CMD_FATAL; - goto end; - } - - if (count == 0) { - /* Functionally equivalent to the 'exclude all' policy. */ - if (the_writer) { - mi_output_empty_tracker(process_attr); - } - MSG("none"); - ret = CMD_SUCCESS; - goto end; - } - - /* Mi tracker_id element */ - if (the_writer) { - /* Open tracker_id and targets elements */ - ret = mi_lttng_process_attribute_tracker_open( - the_writer, process_attr); - if (ret) { - goto end; - } - } - - for (i = 0; i < count; i++) { - const enum lttng_process_attr_value_type value_type = - lttng_process_attr_values_get_type_at_index( - values, i); - int64_t integral_value = INT64_MAX; - const char *name = "error"; - - if (i >= 1) { - _MSG(", "); - } - switch (value_type) { - case LTTNG_PROCESS_ATTR_VALUE_TYPE_PID: - { - pid_t pid; - - values_status = lttng_process_attr_values_get_pid_at_index( - values, i, &pid); - integral_value = (int64_t) pid; - break; - } - case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID: - { - uid_t uid; - - values_status = lttng_process_attr_values_get_uid_at_index( - values, i, &uid); - integral_value = (int64_t) uid; - break; - } - case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID: - { - gid_t gid; - - values_status = lttng_process_attr_values_get_gid_at_index( - values, i, &gid); - integral_value = (int64_t) gid; - break; - } - case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME: - values_status = lttng_process_attr_values_get_user_name_at_index( - values, i, &name); - break; - case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME: - values_status = lttng_process_attr_values_get_group_name_at_index( - values, i, &name); - break; - default: - ret = CMD_ERROR; - goto end; - } - - if (values_status != LTTNG_PROCESS_ATTR_VALUES_STATUS_OK) { - /* - * Not possible given the current liblttng-ctl - * implementation. - */ - ERR("Unknown error occurred while fetching process attribute value in inclusion list"); - ret = CMD_FATAL; - goto end; - } - - if (is_value_type_name(value_type)) { - _MSG("`%s`", name); - } else { - _MSG("%" PRIi64, integral_value); - } - - /* Mi */ - if (the_writer) { - ret = is_value_type_name(value_type) ? - mi_lttng_string_process_attribute_value( - the_writer, - process_attr, name, - false) : - mi_lttng_integral_process_attribute_value( - the_writer, - process_attr, - integral_value, false); - if (ret) { - goto end; - } - } - } - MSG(""); - - /* Mi close tracker_id and targets */ - if (the_writer) { - ret = mi_lttng_close_multi_element(the_writer, 2); - if (ret) { - goto end; - } - } -end: - lttng_process_attr_tracker_handle_destroy(tracker_handle); - return ret; -} - -/* - * List all trackers of a domain - */ -static int list_trackers(const struct lttng_domain *domain) -{ - int ret = 0; - - MSG("Tracked process attributes"); - /* Trackers listing */ - if (lttng_opt_mi) { - ret = mi_lttng_trackers_open(the_writer); - if (ret) { - goto end; - } - } - - switch (domain->type) { - case LTTNG_DOMAIN_KERNEL: - /* pid tracker */ - ret = list_process_attr_tracker(LTTNG_PROCESS_ATTR_PROCESS_ID); - if (ret) { - goto end; - } - /* vpid tracker */ - ret = list_process_attr_tracker( - LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID); - if (ret) { - goto end; - } - /* uid tracker */ - ret = list_process_attr_tracker(LTTNG_PROCESS_ATTR_USER_ID); - if (ret) { - goto end; - } - /* vuid tracker */ - ret = list_process_attr_tracker( - LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID); - if (ret) { - goto end; - } - /* gid tracker */ - ret = list_process_attr_tracker(LTTNG_PROCESS_ATTR_GROUP_ID); - if (ret) { - goto end; - } - /* vgid tracker */ - ret = list_process_attr_tracker( - LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID); - if (ret) { - goto end; - } - break; - case LTTNG_DOMAIN_UST: - /* vpid tracker */ - ret = list_process_attr_tracker( - LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID); - if (ret) { - goto end; - } - /* vuid tracker */ - ret = list_process_attr_tracker( - LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID); - if (ret) { - goto end; - } - /* vgid tracker */ - ret = list_process_attr_tracker( - LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID); - if (ret) { - goto end; - } - break; - default: - break; - } - MSG(); - if (lttng_opt_mi) { - /* Close trackers element */ - ret = mi_lttng_writer_close_element(the_writer); - if (ret) { - goto end; - } - } - -end: - return ret; -} - -static enum cmd_error_code print_periodic_rotation_schedule( - const struct lttng_rotation_schedule *schedule) -{ - enum cmd_error_code ret; - enum lttng_rotation_status status; - uint64_t value; - - status = lttng_rotation_schedule_periodic_get_period(schedule, - &value); - if (status != LTTNG_ROTATION_STATUS_OK) { - ERR("Failed to retrieve period parameter from periodic rotation schedule."); - ret = CMD_ERROR; - goto end; - } - - MSG(" timer period: %" PRIu64" %s", value, USEC_UNIT); - ret = CMD_SUCCESS; -end: - return ret; -} - -static enum cmd_error_code print_size_threshold_rotation_schedule( - const struct lttng_rotation_schedule *schedule) -{ - enum cmd_error_code ret; - enum lttng_rotation_status status; - uint64_t value; - - status = lttng_rotation_schedule_size_threshold_get_threshold(schedule, - &value); - if (status != LTTNG_ROTATION_STATUS_OK) { - ERR("Failed to retrieve size parameter from size-based rotation schedule."); - ret = CMD_ERROR; - goto end; - } - - MSG(" size threshold: %" PRIu64" bytes", value); - ret = CMD_SUCCESS; -end: - return ret; -} - -static enum cmd_error_code print_rotation_schedule( - const struct lttng_rotation_schedule *schedule) -{ - enum cmd_error_code ret; - - switch (lttng_rotation_schedule_get_type(schedule)) { - case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD: - ret = print_size_threshold_rotation_schedule(schedule); - break; - case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC: - ret = print_periodic_rotation_schedule(schedule); - break; - default: - ret = CMD_ERROR; - } - return ret; -} - -/* - * List the automatic rotation settings. - */ -static enum cmd_error_code list_rotate_settings(const char *session_name) -{ - int ret; - enum cmd_error_code cmd_ret = CMD_SUCCESS; - unsigned int count, i; - struct lttng_rotation_schedules *schedules = NULL; - enum lttng_rotation_status status; - - ret = lttng_session_list_rotation_schedules(session_name, &schedules); - if (ret != LTTNG_OK) { - ERR("Failed to list session rotation schedules: %s", lttng_strerror(ret)); - cmd_ret = CMD_ERROR; - goto end; - } - - status = lttng_rotation_schedules_get_count(schedules, &count); - if (status != LTTNG_ROTATION_STATUS_OK) { - ERR("Failed to retrieve the number of session rotation schedules."); - cmd_ret = CMD_ERROR; - goto end; - } - - if (count == 0) { - cmd_ret = CMD_SUCCESS; - goto end; - } - - MSG("Automatic rotation schedules:"); - if (lttng_opt_mi) { - ret = mi_lttng_writer_open_element(the_writer, - mi_lttng_element_rotation_schedules); - if (ret) { - cmd_ret = CMD_ERROR; - goto end; - } - } - - for (i = 0; i < count; i++) { - enum cmd_error_code tmp_ret = CMD_SUCCESS; - const struct lttng_rotation_schedule *schedule; - - schedule = lttng_rotation_schedules_get_at_index(schedules, i); - if (!schedule) { - ERR("Failed to retrieve session rotation schedule."); - cmd_ret = CMD_ERROR; - goto end; - } - - if (lttng_opt_mi) { - ret = mi_lttng_rotation_schedule(the_writer, schedule); - if (ret) { - tmp_ret = CMD_ERROR; - } - } else { - tmp_ret = print_rotation_schedule(schedule); - } - - /* - * Report an error if the serialization of any of the - * descriptors failed. - */ - cmd_ret = cmd_ret ? cmd_ret : tmp_ret; - } - - _MSG("\n"); - if (lttng_opt_mi) { - /* Close the rotation_schedules element. */ - ret = mi_lttng_writer_close_element(the_writer); - if (ret) { - cmd_ret = CMD_ERROR; - goto end; - } - } -end: - lttng_rotation_schedules_destroy(schedules); - return cmd_ret; -} - -/* - * Machine interface - * Find the session with session_name as name - * and print his informations. - */ -static int mi_list_session(const char *session_name, - struct lttng_session *sessions, int count) -{ - int ret, i; - unsigned int session_found = 0; - - if (session_name == NULL) { - ret = -LTTNG_ERR_SESS_NOT_FOUND; - goto end; - } - - for (i = 0; i < count; i++) { - if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) { - /* We need to leave it open to append other informations - * like domain, channel, events etc.*/ - session_found = 1; - ret = mi_lttng_session(the_writer, &sessions[i], 1); - if (ret) { - goto end; - } - break; - } - } - - if (!session_found) { - ERR("Session '%s' not found", session_name); - ret = -LTTNG_ERR_SESS_NOT_FOUND; - goto end; - } - -end: - return ret; -} - -/* - * Machine interface - * List all availables session - */ -static int mi_list_sessions(struct lttng_session *sessions, int count) -{ - int ret, i; - - /* Opening sessions element */ - ret = mi_lttng_sessions_open(the_writer); - if (ret) { - goto end; - } - - /* Listing sessions */ - for (i = 0; i < count; i++) { - ret = mi_lttng_session(the_writer, &sessions[i], 0); - if (ret) { - goto end; - } - } - - /* Closing sessions element */ - ret = mi_lttng_writer_close_element(the_writer); - if (ret) { - goto end; - } - -end: - return ret; -} - -/* - * List available tracing session. List only basic information. - * - * If session_name is NULL, all sessions are listed. - */ -static int list_sessions(const char *session_name) -{ - int ret = CMD_SUCCESS; - int count, i; - unsigned int session_found = 0; - struct lttng_session *sessions = NULL; - - count = lttng_list_sessions(&sessions); - DBG("Session count %d", count); - if (count < 0) { - ret = CMD_ERROR; - ERR("%s", lttng_strerror(count)); - goto end; - } - - if (lttng_opt_mi) { - /* Mi */ - if (session_name == NULL) { - /* List all sessions */ - ret = mi_list_sessions(sessions, count); - } else { - /* Note : this return an open session element */ - ret = mi_list_session(session_name, sessions, count); - } - if (ret) { - ret = CMD_ERROR; - goto end; - } - } else { - /* Pretty print */ - if (count == 0) { - MSG("Currently no available recording session"); - goto end; - } - - if (session_name == NULL) { - MSG("Available recording sessions:"); - } - - for (i = 0; i < count; i++) { - if (session_name != NULL) { - if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) { - session_found = 1; - MSG("Recording session %s: [%s%s]", session_name, - active_string(sessions[i].enabled), - snapshot_string(sessions[i].snapshot_mode)); - if (*sessions[i].path) { - MSG("%sTrace output: %s\n", indent4, sessions[i].path); - } - memcpy(&the_listed_session, - &sessions[i], - sizeof(the_listed_session)); - break; - } - } else { - MSG(" %d) %s [%s%s]", i + 1, - sessions[i].name, - active_string(sessions[i].enabled), - snapshot_string(sessions[i].snapshot_mode)); - if (*sessions[i].path) { - MSG("%sTrace output: %s", indent4, sessions[i].path); - } - if (sessions[i].live_timer_interval != 0) { - MSG("%sLive timer interval: %u %s", indent4, - sessions[i].live_timer_interval, - USEC_UNIT); - } - MSG(""); - } - } - - if (!session_found && session_name != NULL) { - ERR("Session '%s' not found", session_name); - ret = CMD_ERROR; - goto end; - } - - if (session_name == NULL) { - MSG("\nUse lttng list for more details"); - } - } - -end: - free(sessions); - return ret; -} - - -/* - * Machine Interface - * list available domain(s) for a session. - */ -static int mi_list_domains(struct lttng_domain *domains, int count) -{ - int i, ret; - /* Open domains element */ - ret = mi_lttng_domains_open(the_writer); - if (ret) { - goto end; - } - - for (i = 0; i < count; i++) { - ret = mi_lttng_domain(the_writer, &domains[i], 0); - if (ret) { - goto end; - } - } - - /* Closing domains element */ - ret = mi_lttng_writer_close_element(the_writer); - if (ret) { - goto end; - } -end: - return ret; -} - -/* - * List available domain(s) for a session. - */ -static int list_domains(const char *session_name) -{ - int i, count, ret = CMD_SUCCESS; - struct lttng_domain *domains = NULL; - - - count = lttng_list_domains(session_name, &domains); - if (count < 0) { - ret = CMD_ERROR; - ERR("%s", lttng_strerror(count)); - goto end; - } - - if (lttng_opt_mi) { - /* Mi output */ - ret = mi_list_domains(domains, count); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } else { - /* Pretty print */ - MSG("Domains:\n-------------"); - if (count == 0) { - MSG(" None"); - goto end; - } - - for (i = 0; i < count; i++) { - switch (domains[i].type) { - case LTTNG_DOMAIN_KERNEL: - MSG(" - Kernel"); - break; - case LTTNG_DOMAIN_UST: - MSG(" - UST global"); - break; - case LTTNG_DOMAIN_JUL: - MSG(" - JUL (Java Util Logging)"); - break; - case LTTNG_DOMAIN_LOG4J: - MSG(" - LOG4j (Logging for Java)"); - break; - case LTTNG_DOMAIN_PYTHON: - MSG(" - Python (logging)"); - break; - default: - break; - } - } - } - -error: - free(domains); - -end: - return ret; -} - -/* - * The 'list ' first level command - */ -int cmd_list(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS; - const char *session_name, *leftover = NULL; - static poptContext pc; - struct lttng_domain domain; - struct lttng_domain *domains = NULL; - - memset(&domain, 0, sizeof(domain)); - - if (argc < 1) { - ret = CMD_ERROR; - goto end; - } - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_USERSPACE: - opt_userspace = 1; - break; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - default: - ret = CMD_UNDEFINED; - goto end; - } - } - - /* Mi check */ - if (lttng_opt_mi) { - the_writer = mi_lttng_writer_create( - fileno(stdout), lttng_opt_mi); - if (!the_writer) { - ret = CMD_ERROR; - goto end; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open( - the_writer, mi_lttng_element_command_list); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element( - the_writer, mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - /* Get session name (trailing argument) */ - session_name = poptGetArg(pc); - DBG2("Session name: %s", session_name); - - leftover = poptGetArg(pc); - if (leftover) { - ERR("Unknown argument: %s", leftover); - ret = CMD_ERROR; - goto end; - } - - if (opt_kernel) { - domain.type = LTTNG_DOMAIN_KERNEL; - } else if (opt_userspace) { - DBG2("Listing userspace global domain"); - domain.type = LTTNG_DOMAIN_UST; - } else if (opt_jul) { - DBG2("Listing JUL domain"); - domain.type = LTTNG_DOMAIN_JUL; - } else if (opt_log4j) { - domain.type = LTTNG_DOMAIN_LOG4J; - } else if (opt_python) { - domain.type = LTTNG_DOMAIN_PYTHON; - } - - if (!opt_kernel && opt_syscall) { - WARN("--syscall will only work with the Kernel domain (-k)"); - ret = CMD_ERROR; - goto end; - } - - if (opt_kernel || opt_userspace || opt_jul || opt_log4j || opt_python) { - the_handle = lttng_create_handle(session_name, &domain); - if (the_handle == NULL) { - ret = CMD_FATAL; - goto end; - } - } - - if (session_name == NULL) { - if (!opt_kernel && !opt_userspace && !opt_jul && !opt_log4j - && !opt_python) { - ret = list_sessions(NULL); - if (ret) { - goto end; - } - } - if (opt_kernel) { - if (opt_syscall) { - ret = list_syscalls(); - if (ret) { - goto end; - } - } else { - ret = list_kernel_events(); - if (ret) { - goto end; - } - } - } - if (opt_userspace) { - if (opt_fields) { - ret = list_ust_event_fields(); - } else { - ret = list_ust_events(); - } - if (ret) { - goto end; - } - } - if (opt_jul || opt_log4j || opt_python) { - ret = list_agent_events(); - if (ret) { - goto end; - } - } - } else { - /* List session attributes */ - if (lttng_opt_mi) { - /* Open element sessions - * Present for xml consistency */ - ret = mi_lttng_sessions_open(the_writer); - if (ret) { - goto end; - } - } - /* MI: the ouptut of list_sessions is an unclosed session element */ - ret = list_sessions(session_name); - if (ret) { - goto end; - } - - ret = list_rotate_settings(session_name); - if (ret) { - goto end; - } - - /* Domain listing */ - if (opt_domain) { - ret = list_domains(session_name); - goto end; - } - - /* Channel listing */ - if (opt_kernel || opt_userspace) { - if (lttng_opt_mi) { - /* Add of domains and domain element for xml - * consistency and validation - */ - ret = mi_lttng_domains_open(the_writer); - if (ret) { - goto end; - } - - /* Open domain and leave it open for - * nested channels printing */ - ret = mi_lttng_domain(the_writer, &domain, 1); - if (ret) { - goto end; - } - - } - - - /* Trackers */ - ret = list_trackers(&domain); - if (ret) { - goto end; - } - - /* Channels */ - ret = list_channels(opt_channel); - if (ret) { - goto end; - } - - if (lttng_opt_mi) { - /* Close domain and domain element */ - ret = mi_lttng_close_multi_element( - the_writer, 2); - } - if (ret) { - goto end; - } - - - } else { - int i, nb_domain; - - /* We want all domain(s) */ - nb_domain = lttng_list_domains(session_name, &domains); - if (nb_domain < 0) { - ret = CMD_ERROR; - ERR("%s", lttng_strerror(nb_domain)); - goto end; - } - - if (lttng_opt_mi) { - ret = mi_lttng_domains_open(the_writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - for (i = 0; i < nb_domain; i++) { - switch (domains[i].type) { - case LTTNG_DOMAIN_KERNEL: - MSG("=== Domain: Linux kernel ===\n"); - break; - case LTTNG_DOMAIN_UST: - MSG("=== Domain: User space ===\n"); - MSG("Buffering scheme: %s\n", - domains[i].buf_type == - LTTNG_BUFFER_PER_PID ? "per-process" : "per-user"); - break; - case LTTNG_DOMAIN_JUL: - MSG("=== Domain: java.util.logging (JUL) ===\n"); - break; - case LTTNG_DOMAIN_LOG4J: - MSG("=== Domain: log4j ===\n"); - break; - case LTTNG_DOMAIN_PYTHON: - MSG("=== Domain: Python logging ===\n"); - break; - default: - MSG("=== Domain: Unimplemented ===\n"); - break; - } - - if (lttng_opt_mi) { - ret = mi_lttng_domain(the_writer, - &domains[i], 1); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - /* Clean handle before creating a new one */ - if (the_handle) { - lttng_destroy_handle(the_handle); - } - - the_handle = lttng_create_handle( - session_name, &domains[i]); - if (the_handle == NULL) { - ret = CMD_FATAL; - goto end; - } - - if (domains[i].type == LTTNG_DOMAIN_JUL || - domains[i].type == LTTNG_DOMAIN_LOG4J || - domains[i].type == LTTNG_DOMAIN_PYTHON) { - ret = list_session_agent_events(); - if (ret) { - goto end; - } - - goto next_domain; - } - - switch (domains[i].type) { - case LTTNG_DOMAIN_KERNEL: - case LTTNG_DOMAIN_UST: - ret = list_trackers(&domains[i]); - if (ret) { - goto end; - } - break; - default: - break; - } - - ret = list_channels(opt_channel); - if (ret) { - goto end; - } - -next_domain: - if (lttng_opt_mi) { - /* Close domain element */ - ret = mi_lttng_writer_close_element( - the_writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - } - if (lttng_opt_mi) { - /* Close the domains, session and sessions element */ - ret = mi_lttng_close_multi_element( - the_writer, 3); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - } - } - - /* Mi closing */ - if (lttng_opt_mi) { - /* Close output element */ - ret = mi_lttng_writer_close_element(the_writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(the_writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } -end: - /* Mi clean-up */ - if (the_writer && mi_lttng_writer_destroy(the_writer)) { - /* Preserve original error code */ - ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; - } - - free(domains); - if (the_handle) { - lttng_destroy_handle(the_handle); - } - - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/list.cpp b/src/bin/lttng/commands/list.cpp new file mode 100644 index 000000000..4bb681e09 --- /dev/null +++ b/src/bin/lttng/commands/list.cpp @@ -0,0 +1,2635 @@ +/* + * Copyright (C) 2011 David Goulet + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#define _LGPL_SOURCE +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../command.h" + +static int opt_userspace; +static int opt_kernel; +static int opt_jul; +static int opt_log4j; +static int opt_python; +static char *opt_channel; +static int opt_domain; +static int opt_fields; +static int opt_syscall; + +const char *indent4 = " "; +const char *indent6 = " "; +const char *indent8 = " "; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_USERSPACE, + OPT_LIST_OPTIONS, +}; + +static struct lttng_handle *the_handle; +static struct mi_writer *the_writer; + +/* Only set when listing a single session. */ +static struct lttng_session the_listed_session; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, + {"jul", 'j', POPT_ARG_VAL, &opt_jul, 1, 0, 0}, + {"log4j", 'l', POPT_ARG_VAL, &opt_log4j, 1, 0, 0}, + {"python", 'p', POPT_ARG_VAL, &opt_python, 1, 0, 0}, + {"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0}, + {"channel", 'c', POPT_ARG_STRING, &opt_channel, 0, 0, 0}, + {"domain", 'd', POPT_ARG_VAL, &opt_domain, 1, 0, 0}, + {"fields", 'f', POPT_ARG_VAL, &opt_fields, 1, 0, 0}, + {"syscall", 'S', POPT_ARG_VAL, &opt_syscall, 1, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * Get command line from /proc for a specific pid. + * + * On success, return an allocated string pointer to the proc cmdline. + * On error, return NULL. + */ +static char *get_cmdline_by_pid(pid_t pid) +{ + int ret; + FILE *fp = NULL; + char *cmdline = NULL; + /* Can't go bigger than /proc/LTTNG_MAX_PID/cmdline */ + char path[sizeof("/proc//cmdline") + sizeof(LTTNG_MAX_PID_STR) - 1]; + + snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); + fp = fopen(path, "r"); + if (fp == NULL) { + goto end; + } + + /* Caller must free() *cmdline */ + cmdline = (char *) zmalloc(PATH_MAX); + if (!cmdline) { + PERROR("malloc cmdline"); + goto end; + } + ret = fread(cmdline, 1, PATH_MAX, fp); + if (ret < 0) { + PERROR("fread proc list"); + } + +end: + if (fp) { + fclose(fp); + } + return cmdline; +} + +static +const char *active_string(int value) +{ + switch (value) { + case 0: return "inactive"; + case 1: return "active"; + case -1: return ""; + default: return NULL; + } +} + +static const char *snapshot_string(int value) +{ + switch (value) { + case 1: + return " snapshot"; + default: + return ""; + } +} + +static +const char *enabled_string(int value) +{ + switch (value) { + case 0: return " [disabled]"; + case 1: return " [enabled]"; + case -1: return ""; + default: return NULL; + } +} + +static +const char *safe_string(const char *str) +{ + return str ? str : ""; +} + +static const char *logleveltype_string(enum lttng_loglevel_type value) +{ + switch (value) { + case LTTNG_EVENT_LOGLEVEL_ALL: + return ":"; + case LTTNG_EVENT_LOGLEVEL_RANGE: + return " <="; + case LTTNG_EVENT_LOGLEVEL_SINGLE: + return " =="; + default: + return " <>"; + } +} + +static const char *bitness_event(enum lttng_event_flag flags) +{ + if (flags & LTTNG_EVENT_FLAG_SYSCALL_32) { + if (flags & LTTNG_EVENT_FLAG_SYSCALL_64) { + return " [32/64-bit]"; + } else { + return " [32-bit]"; + } + } else if (flags & LTTNG_EVENT_FLAG_SYSCALL_64) { + return " [64-bit]"; + } else { + return ""; + } +} + +/* + * Get exclusion names message for a single event. + * + * Returned pointer must be freed by caller. Returns NULL on error. + */ +static char *get_exclusion_names_msg(struct lttng_event *event) +{ + int ret; + int exclusion_count; + char *exclusion_msg = NULL; + char *at; + size_t i; + const char * const exclusion_fmt = " [exclusions: "; + const size_t exclusion_fmt_len = strlen(exclusion_fmt); + + exclusion_count = lttng_event_get_exclusion_name_count(event); + if (exclusion_count < 0) { + goto end; + } else if (exclusion_count == 0) { + /* + * No exclusions: return copy of empty string so that + * it can be freed by caller. + */ + exclusion_msg = strdup(""); + goto end; + } + + /* + * exclusion_msg's size is bounded by the exclusion_fmt string, + * a comma per entry, the entry count (fixed-size), a closing + * bracket, and a trailing \0. + */ + exclusion_msg = (char *) malloc(exclusion_count + + exclusion_count * LTTNG_SYMBOL_NAME_LEN + + exclusion_fmt_len + 1); + if (!exclusion_msg) { + goto end; + } + + at = strcpy(exclusion_msg, exclusion_fmt) + exclusion_fmt_len; + for (i = 0; i < exclusion_count; ++i) { + const char *name; + + /* Append comma between exclusion names */ + if (i > 0) { + *at = ','; + at++; + } + + ret = lttng_event_get_exclusion_name(event, i, &name); + if (ret) { + /* Prints '?' on local error; should never happen */ + *at = '?'; + at++; + continue; + } + + /* Append exclusion name */ + at += sprintf(at, "%s", name); + } + + /* This also puts a final '\0' at the end of exclusion_msg */ + strcpy(at, "]"); + +end: + return exclusion_msg; +} + +static void print_userspace_probe_location(struct lttng_event *event) +{ + const struct lttng_userspace_probe_location *location; + const struct lttng_userspace_probe_location_lookup_method *lookup_method; + enum lttng_userspace_probe_location_lookup_method_type lookup_type; + + location = lttng_event_get_userspace_probe_location(event); + if (!location) { + MSG("Event has no userspace probe location"); + return; + } + + lookup_method = lttng_userspace_probe_location_get_lookup_method(location); + if (!lookup_method) { + MSG("Event has no userspace probe location lookup method"); + return; + } + + MSG("%s%s (type: userspace-probe)%s", indent6, event->name, enabled_string(event->enabled)); + + lookup_type = lttng_userspace_probe_location_lookup_method_get_type(lookup_method); + + switch (lttng_userspace_probe_location_get_type(location)) { + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_UNKNOWN: + MSG("%sType: Unknown", indent8); + break; + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: + { + const char *function_name; + char *binary_path; + + MSG("%sType: Function", indent8); + function_name = lttng_userspace_probe_location_function_get_function_name(location); + binary_path = realpath(lttng_userspace_probe_location_function_get_binary_path(location), NULL); + + MSG("%sBinary path: %s", indent8, binary_path ? binary_path : "NULL"); + MSG("%sFunction: %s()", indent8, function_name ? function_name : "NULL"); + switch (lookup_type) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + MSG("%sLookup method: ELF", indent8); + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT: + MSG("%sLookup method: default", indent8); + break; + default: + MSG("%sLookup method: INVALID LOOKUP TYPE ENCOUNTERED", indent8); + break; + } + + free(binary_path); + break; + } + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: + { + const char *probe_name, *provider_name; + char *binary_path; + + MSG("%sType: Tracepoint", indent8); + probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name(location); + provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name(location); + binary_path = realpath(lttng_userspace_probe_location_tracepoint_get_binary_path(location), NULL); + MSG("%sBinary path: %s", indent8, binary_path ? binary_path : "NULL"); + MSG("%sTracepoint: %s:%s", indent8, provider_name ? provider_name : "NULL", probe_name ? probe_name : "NULL"); + switch (lookup_type) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + MSG("%sLookup method: SDT", indent8); + break; + default: + MSG("%sLookup method: INVALID LOOKUP TYPE ENCOUNTERED", indent8); + break; + } + + free(binary_path); + break; + } + default: + ERR("Invalid probe type encountered"); + } +} + +/* + * Pretty print single event. + */ +static void print_events(struct lttng_event *event) +{ + int ret; + const char *filter_str; + char *filter_msg = NULL; + char *exclusion_msg = NULL; + + ret = lttng_event_get_filter_expression(event, &filter_str); + + if (ret) { + filter_msg = strdup(" [failed to retrieve filter]"); + } else if (filter_str) { + const char * const filter_fmt = " [filter: '%s']"; + + filter_msg = (char *) malloc(strlen(filter_str) + + strlen(filter_fmt) + 1); + if (filter_msg) { + sprintf(filter_msg, filter_fmt, + filter_str); + } + } + + exclusion_msg = get_exclusion_names_msg(event); + if (!exclusion_msg) { + exclusion_msg = strdup(" [failed to retrieve exclusions]"); + } + + switch (event->type) { + case LTTNG_EVENT_TRACEPOINT: + { + if (event->loglevel != -1) { + MSG("%s%s (loglevel%s %s (%d)) (type: tracepoint)%s%s%s", + indent6, event->name, + logleveltype_string( + event->loglevel_type), + mi_lttng_loglevel_string( + event->loglevel, + the_handle->domain.type), + event->loglevel, + enabled_string(event->enabled), + safe_string(exclusion_msg), + safe_string(filter_msg)); + } else { + MSG("%s%s (type: tracepoint)%s%s%s", + indent6, + event->name, + enabled_string(event->enabled), + safe_string(exclusion_msg), + safe_string(filter_msg)); + } + break; + } + case LTTNG_EVENT_FUNCTION: + MSG("%s%s (type: function)%s%s", indent6, + event->name, enabled_string(event->enabled), + safe_string(filter_msg)); + if (event->attr.probe.addr != 0) { + MSG("%saddr: 0x%" PRIx64, indent8, event->attr.probe.addr); + } else { + MSG("%soffset: 0x%" PRIx64, indent8, event->attr.probe.offset); + MSG("%ssymbol: %s", indent8, event->attr.probe.symbol_name); + } + break; + case LTTNG_EVENT_PROBE: + MSG("%s%s (type: probe)%s%s", indent6, + event->name, enabled_string(event->enabled), + safe_string(filter_msg)); + if (event->attr.probe.addr != 0) { + MSG("%saddr: 0x%" PRIx64, indent8, event->attr.probe.addr); + } else { + MSG("%soffset: 0x%" PRIx64, indent8, event->attr.probe.offset); + MSG("%ssymbol: %s", indent8, event->attr.probe.symbol_name); + } + break; + case LTTNG_EVENT_USERSPACE_PROBE: + print_userspace_probe_location(event); + break; + case LTTNG_EVENT_FUNCTION_ENTRY: + MSG("%s%s (type: function)%s%s", indent6, + event->name, enabled_string(event->enabled), + safe_string(filter_msg)); + MSG("%ssymbol: \"%s\"", indent8, event->attr.ftrace.symbol_name); + break; + case LTTNG_EVENT_SYSCALL: + MSG("%s%s%s%s%s%s", indent6, event->name, + (opt_syscall ? "" : " (type:syscall)"), + enabled_string(event->enabled), + bitness_event(event->flags), + safe_string(filter_msg)); + break; + case LTTNG_EVENT_NOOP: + MSG("%s (type: noop)%s%s", indent6, + enabled_string(event->enabled), + safe_string(filter_msg)); + break; + case LTTNG_EVENT_ALL: + /* Fall-through. */ + default: + /* We should never have "all" events in list. */ + abort(); + break; + } + + free(filter_msg); + free(exclusion_msg); +} + +static const char *field_type(struct lttng_event_field *field) +{ + switch(field->type) { + case LTTNG_EVENT_FIELD_INTEGER: + return "integer"; + case LTTNG_EVENT_FIELD_ENUM: + return "enum"; + case LTTNG_EVENT_FIELD_FLOAT: + return "float"; + case LTTNG_EVENT_FIELD_STRING: + return "string"; + case LTTNG_EVENT_FIELD_OTHER: + default: /* fall-through */ + return "unknown"; + } +} + +/* + * Pretty print single event fields. + */ +static void print_event_field(struct lttng_event_field *field) +{ + if (!field->field_name[0]) { + return; + } + MSG("%sfield: %s (%s)%s", indent8, field->field_name, + field_type(field), field->nowrite ? " [no write]" : ""); +} + +/* + * Machine interface + * Jul and ust event listing + */ +static int mi_list_agent_ust_events(struct lttng_event *events, int count, + struct lttng_domain *domain) +{ + int ret, i; + pid_t cur_pid = 0; + char *cmdline = NULL; + int pid_element_open = 0; + + /* Open domains element */ + ret = mi_lttng_domains_open(the_writer); + if (ret) { + goto end; + } + + /* Write domain */ + ret = mi_lttng_domain(the_writer, domain, 1); + if (ret) { + goto end; + } + + /* Open pids element element */ + ret = mi_lttng_pids_open(the_writer); + if (ret) { + goto end; + } + + for (i = 0; i < count; i++) { + if (cur_pid != events[i].pid) { + if (pid_element_open) { + /* Close the previous events and pid element */ + ret = mi_lttng_close_multi_element( + the_writer, 2); + if (ret) { + goto end; + } + pid_element_open = 0; + } + + cur_pid = events[i].pid; + cmdline = get_cmdline_by_pid(cur_pid); + if (!cmdline) { + ret = CMD_ERROR; + goto end; + } + + if (!pid_element_open) { + /* Open and write a pid element */ + ret = mi_lttng_pid(the_writer, cur_pid, cmdline, + 1); + if (ret) { + goto error; + } + + /* Open events element */ + ret = mi_lttng_events_open(the_writer); + if (ret) { + goto error; + } + + pid_element_open = 1; + } + free(cmdline); + } + + /* Write an event */ + ret = mi_lttng_event(the_writer, &events[i], 0, + the_handle->domain.type); + if (ret) { + goto end; + } + } + + /* Close pids */ + ret = mi_lttng_writer_close_element(the_writer); + if (ret) { + goto end; + } + + /* Close domain, domains */ + ret = mi_lttng_close_multi_element(the_writer, 2); +end: + return ret; +error: + free(cmdline); + return ret; +} + +static int list_agent_events(void) +{ + int i, size, ret = CMD_SUCCESS; + struct lttng_domain domain; + struct lttng_handle *handle = NULL; + struct lttng_event *event_list = NULL; + pid_t cur_pid = 0; + char *cmdline = NULL; + const char *agent_domain_str; + + memset(&domain, 0, sizeof(domain)); + if (opt_jul) { + domain.type = LTTNG_DOMAIN_JUL; + } else if (opt_log4j) { + domain.type = LTTNG_DOMAIN_LOG4J; + } else if (opt_python) { + domain.type = LTTNG_DOMAIN_PYTHON; + } else { + ERR("Invalid agent domain selected."); + ret = CMD_ERROR; + goto error; + } + + agent_domain_str = lttng_domain_type_str(domain.type); + + DBG("Getting %s tracing events", agent_domain_str); + + handle = lttng_create_handle(NULL, &domain); + if (handle == NULL) { + ret = CMD_ERROR; + goto end; + } + + size = lttng_list_tracepoints(handle, &event_list); + if (size < 0) { + ERR("Unable to list %s events: %s", agent_domain_str, + lttng_strerror(size)); + ret = CMD_ERROR; + goto end; + } + + if (lttng_opt_mi) { + /* Mi print */ + ret = mi_list_agent_ust_events(event_list, size, &domain); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } else { + /* Pretty print */ + MSG("%s events (Logger name):\n-------------------------", + agent_domain_str); + + if (size == 0) { + MSG("None"); + } + + for (i = 0; i < size; i++) { + if (cur_pid != event_list[i].pid) { + cur_pid = event_list[i].pid; + cmdline = get_cmdline_by_pid(cur_pid); + if (cmdline == NULL) { + ret = CMD_ERROR; + goto error; + } + MSG("\nPID: %d - Name: %s", cur_pid, cmdline); + free(cmdline); + } + MSG("%s- %s", indent6, event_list[i].name); + } + + MSG(""); + } + +error: + free(event_list); +end: + lttng_destroy_handle(handle); + return ret; +} + +/* + * Ask session daemon for all user space tracepoints available. + */ +static int list_ust_events(void) +{ + int i, size, ret = CMD_SUCCESS; + struct lttng_domain domain; + struct lttng_handle *handle; + struct lttng_event *event_list = NULL; + pid_t cur_pid = 0; + char *cmdline = NULL; + + memset(&domain, 0, sizeof(domain)); + + DBG("Getting UST tracing events"); + + domain.type = LTTNG_DOMAIN_UST; + + handle = lttng_create_handle(NULL, &domain); + if (handle == NULL) { + ret = CMD_ERROR; + goto end; + } + + size = lttng_list_tracepoints(handle, &event_list); + if (size < 0) { + ERR("Unable to list UST events: %s", lttng_strerror(size)); + ret = CMD_ERROR; + goto error; + } + + if (lttng_opt_mi) { + /* Mi print */ + ret = mi_list_agent_ust_events(event_list, size, &domain); + } else { + /* Pretty print */ + MSG("UST events:\n-------------"); + + if (size == 0) { + MSG("None"); + } + + for (i = 0; i < size; i++) { + if (cur_pid != event_list[i].pid) { + cur_pid = event_list[i].pid; + cmdline = get_cmdline_by_pid(cur_pid); + if (cmdline == NULL) { + ret = CMD_ERROR; + goto error; + } + MSG("\nPID: %d - Name: %s", cur_pid, cmdline); + free(cmdline); + } + print_events(&event_list[i]); + } + + MSG(""); + } + +error: + free(event_list); +end: + lttng_destroy_handle(handle); + return ret; +} + +/* + * Machine interface + * List all ust event with their fields + */ +static int mi_list_ust_event_fields(struct lttng_event_field *fields, int count, + struct lttng_domain *domain) +{ + int ret, i; + pid_t cur_pid = 0; + char *cmdline = NULL; + int pid_element_open = 0; + int event_element_open = 0; + struct lttng_event cur_event; + + memset(&cur_event, 0, sizeof(cur_event)); + + /* Open domains element */ + ret = mi_lttng_domains_open(the_writer); + if (ret) { + goto end; + } + + /* Write domain */ + ret = mi_lttng_domain(the_writer, domain, 1); + if (ret) { + goto end; + } + + /* Open pids element */ + ret = mi_lttng_pids_open(the_writer); + if (ret) { + goto end; + } + + for (i = 0; i < count; i++) { + if (cur_pid != fields[i].event.pid) { + if (pid_element_open) { + if (event_element_open) { + /* Close the previous field element and event. */ + ret = mi_lttng_close_multi_element( + the_writer, 2); + if (ret) { + goto end; + } + event_element_open = 0; + } + /* Close the previous events, pid element */ + ret = mi_lttng_close_multi_element( + the_writer, 2); + if (ret) { + goto end; + } + pid_element_open = 0; + } + + cur_pid = fields[i].event.pid; + cmdline = get_cmdline_by_pid(cur_pid); + if (!pid_element_open) { + /* Open and write a pid element */ + ret = mi_lttng_pid(the_writer, cur_pid, cmdline, + 1); + if (ret) { + goto error; + } + + /* Open events element */ + ret = mi_lttng_events_open(the_writer); + if (ret) { + goto error; + } + pid_element_open = 1; + } + free(cmdline); + /* Wipe current event since we are about to print a new PID. */ + memset(&cur_event, 0, sizeof(cur_event)); + } + + if (strcmp(cur_event.name, fields[i].event.name) != 0) { + if (event_element_open) { + /* Close the previous fields element and the previous event */ + ret = mi_lttng_close_multi_element( + the_writer, 2); + if (ret) { + goto end; + } + event_element_open = 0; + } + + memcpy(&cur_event, &fields[i].event, + sizeof(cur_event)); + + if (!event_element_open) { + /* Open and write the event */ + ret = mi_lttng_event(the_writer, &cur_event, 1, + the_handle->domain.type); + if (ret) { + goto end; + } + + /* Open a fields element */ + ret = mi_lttng_event_fields_open(the_writer); + if (ret) { + goto end; + } + event_element_open = 1; + } + } + + /* Print the event_field */ + ret = mi_lttng_event_field(the_writer, &fields[i]); + if (ret) { + goto end; + } + } + + /* Close pids, domain, domains */ + ret = mi_lttng_close_multi_element(the_writer, 3); +end: + return ret; +error: + free(cmdline); + return ret; +} + +/* + * Ask session daemon for all user space tracepoint fields available. + */ +static int list_ust_event_fields(void) +{ + int i, size, ret = CMD_SUCCESS; + struct lttng_domain domain; + struct lttng_handle *handle; + struct lttng_event_field *event_field_list; + pid_t cur_pid = 0; + char *cmdline = NULL; + + struct lttng_event cur_event; + + memset(&domain, 0, sizeof(domain)); + memset(&cur_event, 0, sizeof(cur_event)); + + DBG("Getting UST tracing event fields"); + + domain.type = LTTNG_DOMAIN_UST; + + handle = lttng_create_handle(NULL, &domain); + if (handle == NULL) { + ret = CMD_ERROR; + goto end; + } + + size = lttng_list_tracepoint_fields(handle, &event_field_list); + if (size < 0) { + ERR("Unable to list UST event fields: %s", lttng_strerror(size)); + ret = CMD_ERROR; + goto end; + } + + if (lttng_opt_mi) { + /* Mi print */ + ret = mi_list_ust_event_fields(event_field_list, size, &domain); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } else { + /* Pretty print */ + MSG("UST events:\n-------------"); + + if (size == 0) { + MSG("None"); + } + + for (i = 0; i < size; i++) { + if (cur_pid != event_field_list[i].event.pid) { + cur_pid = event_field_list[i].event.pid; + cmdline = get_cmdline_by_pid(cur_pid); + if (cmdline == NULL) { + ret = CMD_ERROR; + goto error; + } + MSG("\nPID: %d - Name: %s", cur_pid, cmdline); + free(cmdline); + /* Wipe current event since we are about to print a new PID. */ + memset(&cur_event, 0, sizeof(cur_event)); + } + if (strcmp(cur_event.name, event_field_list[i].event.name) != 0) { + print_events(&event_field_list[i].event); + memcpy(&cur_event, &event_field_list[i].event, + sizeof(cur_event)); + } + print_event_field(&event_field_list[i]); + } + + MSG(""); + } + +error: + free(event_field_list); +end: + lttng_destroy_handle(handle); + return ret; +} + +/* + * Machine interface + * Print a list of kernel events + */ +static int mi_list_kernel_events(struct lttng_event *events, int count, + struct lttng_domain *domain) +{ + int ret, i; + + /* Open domains element */ + ret = mi_lttng_domains_open(the_writer); + if (ret) { + goto end; + } + + /* Write domain */ + ret = mi_lttng_domain(the_writer, domain, 1); + if (ret) { + goto end; + } + + /* Open events */ + ret = mi_lttng_events_open(the_writer); + if (ret) { + goto end; + } + + for (i = 0; i < count; i++) { + ret = mi_lttng_event(the_writer, &events[i], 0, + the_handle->domain.type); + if (ret) { + goto end; + } + } + + /* close events, domain and domains */ + ret = mi_lttng_close_multi_element(the_writer, 3); + if (ret) { + goto end; + } + +end: + return ret; +} + +/* + * Ask for all trace events in the kernel + */ +static int list_kernel_events(void) +{ + int i, size, ret = CMD_SUCCESS; + struct lttng_domain domain; + struct lttng_handle *handle; + struct lttng_event *event_list; + + memset(&domain, 0, sizeof(domain)); + + DBG("Getting kernel tracing events"); + + domain.type = LTTNG_DOMAIN_KERNEL; + + handle = lttng_create_handle(NULL, &domain); + if (handle == NULL) { + ret = CMD_ERROR; + goto error; + } + + size = lttng_list_tracepoints(handle, &event_list); + if (size < 0) { + ERR("Unable to list kernel events: %s", lttng_strerror(size)); + lttng_destroy_handle(handle); + return CMD_ERROR; + } + + if (lttng_opt_mi) { + /* Mi print */ + ret = mi_list_kernel_events(event_list, size, &domain); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } else { + MSG("Kernel events:\n-------------"); + + for (i = 0; i < size; i++) { + print_events(&event_list[i]); + } + + MSG(""); + } + +end: + free(event_list); + + lttng_destroy_handle(handle); + return ret; + +error: + lttng_destroy_handle(handle); + return ret; +} + +/* + * Machine interface + * Print a list of system calls. + */ +static int mi_list_syscalls(struct lttng_event *events, int count) +{ + int ret, i; + + /* Open events */ + ret = mi_lttng_events_open(the_writer); + if (ret) { + goto end; + } + + for (i = 0; i < count; i++) { + ret = mi_lttng_event(the_writer, &events[i], 0, + the_handle->domain.type); + if (ret) { + goto end; + } + } + + /* Close events. */ + ret = mi_lttng_writer_close_element(the_writer); + if (ret) { + goto end; + } + +end: + return ret; +} + +/* + * Ask for kernel system calls. + */ +static int list_syscalls(void) +{ + int i, size, ret = CMD_SUCCESS; + struct lttng_event *event_list; + + DBG("Getting kernel system call events"); + + size = lttng_list_syscalls(&event_list); + if (size < 0) { + ERR("Unable to list system calls: %s", lttng_strerror(size)); + ret = CMD_ERROR; + goto error; + } + + if (lttng_opt_mi) { + /* Mi print */ + ret = mi_list_syscalls(event_list, size); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } else { + MSG("System calls:\n-------------"); + + for (i = 0; i < size; i++) { + print_events(&event_list[i]); + } + + MSG(""); + } + +end: + free(event_list); + return ret; + +error: + return ret; +} + +/* + * Machine Interface + * Print a list of agent events + */ +static int mi_list_session_agent_events(struct lttng_event *events, int count) +{ + int ret, i; + + /* Open events element */ + ret = mi_lttng_events_open(the_writer); + if (ret) { + goto end; + } + + for (i = 0; i < count; i++) { + ret = mi_lttng_event(the_writer, &events[i], 0, + the_handle->domain.type); + if (ret) { + goto end; + } + } + + /* Close events element */ + ret = mi_lttng_writer_close_element(the_writer); + +end: + return ret; +} + +/* + * List agent events for a specific session using the handle. + * + * Return CMD_SUCCESS on success else a negative value. + */ +static int list_session_agent_events(void) +{ + int ret = CMD_SUCCESS, count, i; + struct lttng_event *events = NULL; + + count = lttng_list_events(the_handle, "", &events); + if (count < 0) { + ret = CMD_ERROR; + ERR("%s", lttng_strerror(count)); + goto error; + } + + if (lttng_opt_mi) { + /* Mi print */ + ret = mi_list_session_agent_events(events, count); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } else { + /* Pretty print */ + MSG("Events (Logger name):\n---------------------"); + if (count == 0) { + MSG("%sNone\n", indent6); + goto end; + } + + for (i = 0; i < count; i++) { + const char *filter_str; + char *filter_msg = NULL; + struct lttng_event *event = &events[i]; + + ret = lttng_event_get_filter_expression(event, + &filter_str); + if (ret) { + filter_msg = strdup(" [failed to retrieve filter]"); + } else if (filter_str) { + const char * const filter_fmt = + " [filter: '%s']"; + + filter_msg = (char *) malloc(strlen(filter_str) + + strlen(filter_fmt) + 1); + if (filter_msg) { + sprintf(filter_msg, filter_fmt, + filter_str); + } + } + + if (event->loglevel_type != + LTTNG_EVENT_LOGLEVEL_ALL) { + MSG("%s- %s%s (loglevel%s %s)%s", indent4, + event->name, + enabled_string(event->enabled), + logleveltype_string( + event->loglevel_type), + mi_lttng_loglevel_string( + event->loglevel, + the_handle->domain + .type), + safe_string(filter_msg)); + } else { + MSG("%s- %s%s%s", indent4, event->name, + enabled_string(event->enabled), + safe_string(filter_msg)); + } + free(filter_msg); + } + + MSG(""); + } + +end: + free(events); +error: + return ret; +} + +/* + * Machine interface + * print a list of event + */ +static int mi_list_events(struct lttng_event *events, int count) +{ + int ret, i; + + /* Open events element */ + ret = mi_lttng_events_open(the_writer); + if (ret) { + goto end; + } + + for (i = 0; i < count; i++) { + ret = mi_lttng_event(the_writer, &events[i], 0, + the_handle->domain.type); + if (ret) { + goto end; + } + } + + /* Close events element */ + ret = mi_lttng_writer_close_element(the_writer); + +end: + return ret; +} + +/* + * List events of channel of session and domain. + */ +static int list_events(const char *channel_name) +{ + int ret = CMD_SUCCESS, count, i; + struct lttng_event *events = NULL; + + count = lttng_list_events(the_handle, channel_name, &events); + if (count < 0) { + ret = CMD_ERROR; + ERR("%s", lttng_strerror(count)); + goto error; + } + + if (lttng_opt_mi) { + /* Mi print */ + ret = mi_list_events(events, count); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } else { + /* Pretty print */ + MSG("\n%sRecording event rules:", indent4); + if (count == 0) { + MSG("%sNone\n", indent6); + goto end; + } + + for (i = 0; i < count; i++) { + print_events(&events[i]); + } + + MSG(""); + } +end: + free(events); +error: + return ret; +} + +static +void print_timer(const char *timer_name, uint32_t space_count, int64_t value) +{ + uint32_t i; + + _MSG("%s%s:", indent6, timer_name); + for (i = 0; i < space_count; i++) { + _MSG(" "); + } + + if (value) { + MSG("%" PRId64 " %s", value, USEC_UNIT); + } else { + MSG("inactive"); + } +} + +/* + * Pretty print channel + */ +static void print_channel(struct lttng_channel *channel) +{ + int ret; + uint64_t discarded_events, lost_packets, monitor_timer_interval; + int64_t blocking_timeout; + + ret = lttng_channel_get_discarded_event_count(channel, + &discarded_events); + if (ret) { + ERR("Failed to retrieve discarded event count of channel"); + return; + } + + ret = lttng_channel_get_lost_packet_count(channel, + &lost_packets); + if (ret) { + ERR("Failed to retrieve lost packet count of channel"); + return; + } + + ret = lttng_channel_get_monitor_timer_interval(channel, + &monitor_timer_interval); + if (ret) { + ERR("Failed to retrieve monitor interval of channel"); + return; + } + + ret = lttng_channel_get_blocking_timeout(channel, + &blocking_timeout); + if (ret) { + ERR("Failed to retrieve blocking timeout of channel"); + return; + } + + MSG("- %s:%s\n", channel->name, enabled_string(channel->enabled)); + MSG("%sAttributes:", indent4); + MSG("%sEvent-loss mode: %s", indent6, channel->attr.overwrite ? "overwrite" : "discard"); + MSG("%sSub-buffer size: %" PRIu64 " bytes", indent6, channel->attr.subbuf_size); + MSG("%sSub-buffer count: %" PRIu64, indent6, channel->attr.num_subbuf); + + print_timer("Switch timer", 5, channel->attr.switch_timer_interval); + print_timer("Read timer", 7, channel->attr.read_timer_interval); + print_timer("Monitor timer", 4, monitor_timer_interval); + + if (!channel->attr.overwrite) { + if (blocking_timeout == -1) { + MSG("%sBlocking timeout: infinite", indent6); + } else { + MSG("%sBlocking timeout: %" PRId64 " %s", indent6, + blocking_timeout, USEC_UNIT); + } + } + + MSG("%sTrace file count: %" PRIu64 " per stream", indent6, + channel->attr.tracefile_count == 0 ? + 1 : channel->attr.tracefile_count); + if (channel->attr.tracefile_size != 0 ) { + MSG("%sTrace file size: %" PRIu64 " bytes", indent6, + channel->attr.tracefile_size); + } else { + MSG("%sTrace file size: %s", indent6, "unlimited"); + } + switch (channel->attr.output) { + case LTTNG_EVENT_SPLICE: + MSG("%sOutput mode: splice", indent6); + break; + case LTTNG_EVENT_MMAP: + MSG("%sOutput mode: mmap", indent6); + break; + } + + MSG("\n%sStatistics:", indent4); + if (the_listed_session.snapshot_mode) { + /* + * The lost packet count is omitted for sessions in snapshot + * mode as it is misleading: it would indicate the number of + * packets that the consumer could not extract during the + * course of recording the snapshot. It does not have the + * same meaning as the "regular" lost packet count that + * would result from the consumer not keeping up with + * event production in an overwrite-mode channel. + * + * A more interesting statistic would be the number of + * packets lost between the first and last extracted + * packets of a given snapshot (which prevents most analyses). + */ + MSG("%sNone", indent6); + goto skip_stats_printing; + } + + if (!channel->attr.overwrite) { + MSG("%sDiscarded events: %" PRIu64, indent6, discarded_events); + } else { + MSG("%sLost packets: %" PRIu64, indent6, lost_packets); + } +skip_stats_printing: + return; +} + +/* + * Machine interface + * Print a list of channel + * + */ +static int mi_list_channels(struct lttng_channel *channels, int count, + const char *channel_name) +{ + int i, ret; + unsigned int chan_found = 0; + + /* Open channels element */ + ret = mi_lttng_channels_open(the_writer); + if (ret) { + goto error; + } + + for (i = 0; i < count; i++) { + if (channel_name != NULL) { + if (strncmp(channels[i].name, channel_name, NAME_MAX) == 0) { + chan_found = 1; + } else { + continue; + } + } + + /* Write channel element and leave it open */ + ret = mi_lttng_channel(the_writer, &channels[i], 1); + if (ret) { + goto error; + } + + /* Listing events per channel */ + ret = list_events(channels[i].name); + if (ret) { + goto error; + } + + /* Closing the channel element we opened earlier */ + ret = mi_lttng_writer_close_element(the_writer); + if (ret) { + goto error; + } + + if (chan_found) { + break; + } + } + + /* Close channels element */ + ret = mi_lttng_writer_close_element(the_writer); + if (ret) { + goto error; + } + +error: + return ret; +} + +/* + * List channel(s) of session and domain. + * + * If channel_name is NULL, all channels are listed. + */ +static int list_channels(const char *channel_name) +{ + int count, i, ret = CMD_SUCCESS; + unsigned int chan_found = 0; + struct lttng_channel *channels = NULL; + + DBG("Listing channel(s) (%s)", channel_name ? : ""); + + count = lttng_list_channels(the_handle, &channels); + if (count < 0) { + switch (-count) { + case LTTNG_ERR_KERN_CHAN_NOT_FOUND: + if (lttng_opt_mi) { + /* When printing mi this is not an error + * but an empty channels element */ + count = 0; + } else { + ret = CMD_SUCCESS; + goto error_channels; + } + break; + default: + /* We had a real error */ + ret = CMD_ERROR; + ERR("%s", lttng_strerror(count)); + goto error_channels; + break; + } + } + + if (lttng_opt_mi) { + /* Mi print */ + ret = mi_list_channels(channels, count, channel_name); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } else { + /* Pretty print */ + if (count) { + MSG("Channels:\n-------------"); + } + + for (i = 0; i < count; i++) { + if (channel_name != NULL) { + if (strncmp(channels[i].name, channel_name, NAME_MAX) == 0) { + chan_found = 1; + } else { + continue; + } + } + print_channel(&channels[i]); + + /* Listing events per channel */ + ret = list_events(channels[i].name); + if (ret) { + goto error; + } + + if (chan_found) { + break; + } + } + + if (!chan_found && channel_name != NULL) { + ret = CMD_ERROR; + ERR("Channel %s not found", channel_name); + goto error; + } + } +error: + free(channels); + +error_channels: + return ret; +} + +static const char *get_capitalized_process_attr_str(enum lttng_process_attr process_attr) +{ + switch (process_attr) { + case LTTNG_PROCESS_ATTR_PROCESS_ID: + return "Process ID"; + case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: + return "Virtual process ID"; + case LTTNG_PROCESS_ATTR_USER_ID: + return "User ID"; + case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: + return "Virtual user ID"; + case LTTNG_PROCESS_ATTR_GROUP_ID: + return "Group ID"; + case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: + return "Virtual group ID"; + default: + return "Unknown"; + } + return NULL; +} + +static int handle_process_attr_status(enum lttng_process_attr process_attr, + enum lttng_process_attr_tracker_handle_status status) +{ + int ret = CMD_SUCCESS; + + switch (status) { + case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_INVALID_TRACKING_POLICY: + case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK: + /* Carry on. */ + break; + case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_COMMUNICATION_ERROR: + ERR("Communication occurred while fetching %s tracker", + lttng_process_attr_to_string(process_attr)); + ret = CMD_ERROR; + break; + case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_SESSION_DOES_NOT_EXIST: + ERR("Failed to get the inclusion set of the %s tracker: session `%s` no longer exists", + lttng_process_attr_to_string(process_attr), + the_handle->session_name); + ret = CMD_ERROR; + break; + default: + ERR("Unknown error occurred while fetching the inclusion set of the %s tracker", + lttng_process_attr_to_string(process_attr)); + ret = CMD_ERROR; + break; + } + + return ret; +} + +static int mi_output_empty_tracker(enum lttng_process_attr process_attr) +{ + int ret; + + ret = mi_lttng_process_attribute_tracker_open(the_writer, process_attr); + if (ret) { + goto end; + } + + ret = mi_lttng_close_multi_element(the_writer, 2); +end: + return ret; +} + +static inline bool is_value_type_name( + enum lttng_process_attr_value_type value_type) +{ + return value_type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME || + value_type == LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME; +} + +/* + * List a process attribute tracker for a session and domain tuple. + */ +static int list_process_attr_tracker(enum lttng_process_attr process_attr) +{ + int ret = 0; + unsigned int count, i; + enum lttng_tracking_policy policy; + enum lttng_error_code ret_code; + enum lttng_process_attr_tracker_handle_status handle_status; + enum lttng_process_attr_values_status values_status; + const struct lttng_process_attr_values *values; + struct lttng_process_attr_tracker_handle *tracker_handle = NULL; + + ret_code = lttng_session_get_tracker_handle(the_handle->session_name, + the_handle->domain.type, process_attr, &tracker_handle); + if (ret_code != LTTNG_OK) { + ERR("Failed to get process attribute tracker handle: %s", + lttng_strerror(ret_code)); + ret = CMD_ERROR; + goto end; + } + + handle_status = lttng_process_attr_tracker_handle_get_inclusion_set( + tracker_handle, &values); + ret = handle_process_attr_status(process_attr, handle_status); + if (ret != CMD_SUCCESS) { + goto end; + } + + handle_status = lttng_process_attr_tracker_handle_get_tracking_policy( + tracker_handle, &policy); + ret = handle_process_attr_status(process_attr, handle_status); + if (ret != CMD_SUCCESS) { + goto end; + } + + { + char *process_attr_name; + const int print_ret = asprintf(&process_attr_name, "%ss:", + get_capitalized_process_attr_str(process_attr)); + + if (print_ret == -1) { + ret = CMD_FATAL; + goto end; + } + _MSG(" %-22s", process_attr_name); + free(process_attr_name); + } + switch (policy) { + case LTTNG_TRACKING_POLICY_INCLUDE_SET: + break; + case LTTNG_TRACKING_POLICY_EXCLUDE_ALL: + if (the_writer) { + mi_output_empty_tracker(process_attr); + } + MSG("none"); + ret = CMD_SUCCESS; + goto end; + case LTTNG_TRACKING_POLICY_INCLUDE_ALL: + MSG("all"); + ret = CMD_SUCCESS; + goto end; + default: + ERR("Unknown tracking policy encoutered while listing the %s process attribute tracker of session `%s`", + lttng_process_attr_to_string(process_attr), + the_handle->session_name); + ret = CMD_FATAL; + goto end; + } + + values_status = lttng_process_attr_values_get_count(values, &count); + if (values_status != LTTNG_PROCESS_ATTR_VALUES_STATUS_OK) { + ERR("Failed to get the count of values in the inclusion set of the %s process attribute tracker of session `%s`", + lttng_process_attr_to_string(process_attr), + the_handle->session_name); + ret = CMD_FATAL; + goto end; + } + + if (count == 0) { + /* Functionally equivalent to the 'exclude all' policy. */ + if (the_writer) { + mi_output_empty_tracker(process_attr); + } + MSG("none"); + ret = CMD_SUCCESS; + goto end; + } + + /* Mi tracker_id element */ + if (the_writer) { + /* Open tracker_id and targets elements */ + ret = mi_lttng_process_attribute_tracker_open( + the_writer, process_attr); + if (ret) { + goto end; + } + } + + for (i = 0; i < count; i++) { + const enum lttng_process_attr_value_type value_type = + lttng_process_attr_values_get_type_at_index( + values, i); + int64_t integral_value = INT64_MAX; + const char *name = "error"; + + if (i >= 1) { + _MSG(", "); + } + switch (value_type) { + case LTTNG_PROCESS_ATTR_VALUE_TYPE_PID: + { + pid_t pid; + + values_status = lttng_process_attr_values_get_pid_at_index( + values, i, &pid); + integral_value = (int64_t) pid; + break; + } + case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID: + { + uid_t uid; + + values_status = lttng_process_attr_values_get_uid_at_index( + values, i, &uid); + integral_value = (int64_t) uid; + break; + } + case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID: + { + gid_t gid; + + values_status = lttng_process_attr_values_get_gid_at_index( + values, i, &gid); + integral_value = (int64_t) gid; + break; + } + case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME: + values_status = lttng_process_attr_values_get_user_name_at_index( + values, i, &name); + break; + case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME: + values_status = lttng_process_attr_values_get_group_name_at_index( + values, i, &name); + break; + default: + ret = CMD_ERROR; + goto end; + } + + if (values_status != LTTNG_PROCESS_ATTR_VALUES_STATUS_OK) { + /* + * Not possible given the current liblttng-ctl + * implementation. + */ + ERR("Unknown error occurred while fetching process attribute value in inclusion list"); + ret = CMD_FATAL; + goto end; + } + + if (is_value_type_name(value_type)) { + _MSG("`%s`", name); + } else { + _MSG("%" PRIi64, integral_value); + } + + /* Mi */ + if (the_writer) { + ret = is_value_type_name(value_type) ? + mi_lttng_string_process_attribute_value( + the_writer, + process_attr, name, + false) : + mi_lttng_integral_process_attribute_value( + the_writer, + process_attr, + integral_value, false); + if (ret) { + goto end; + } + } + } + MSG(""); + + /* Mi close tracker_id and targets */ + if (the_writer) { + ret = mi_lttng_close_multi_element(the_writer, 2); + if (ret) { + goto end; + } + } +end: + lttng_process_attr_tracker_handle_destroy(tracker_handle); + return ret; +} + +/* + * List all trackers of a domain + */ +static int list_trackers(const struct lttng_domain *domain) +{ + int ret = 0; + + MSG("Tracked process attributes"); + /* Trackers listing */ + if (lttng_opt_mi) { + ret = mi_lttng_trackers_open(the_writer); + if (ret) { + goto end; + } + } + + switch (domain->type) { + case LTTNG_DOMAIN_KERNEL: + /* pid tracker */ + ret = list_process_attr_tracker(LTTNG_PROCESS_ATTR_PROCESS_ID); + if (ret) { + goto end; + } + /* vpid tracker */ + ret = list_process_attr_tracker( + LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID); + if (ret) { + goto end; + } + /* uid tracker */ + ret = list_process_attr_tracker(LTTNG_PROCESS_ATTR_USER_ID); + if (ret) { + goto end; + } + /* vuid tracker */ + ret = list_process_attr_tracker( + LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID); + if (ret) { + goto end; + } + /* gid tracker */ + ret = list_process_attr_tracker(LTTNG_PROCESS_ATTR_GROUP_ID); + if (ret) { + goto end; + } + /* vgid tracker */ + ret = list_process_attr_tracker( + LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID); + if (ret) { + goto end; + } + break; + case LTTNG_DOMAIN_UST: + /* vpid tracker */ + ret = list_process_attr_tracker( + LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID); + if (ret) { + goto end; + } + /* vuid tracker */ + ret = list_process_attr_tracker( + LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID); + if (ret) { + goto end; + } + /* vgid tracker */ + ret = list_process_attr_tracker( + LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID); + if (ret) { + goto end; + } + break; + default: + break; + } + MSG(); + if (lttng_opt_mi) { + /* Close trackers element */ + ret = mi_lttng_writer_close_element(the_writer); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +static enum cmd_error_code print_periodic_rotation_schedule( + const struct lttng_rotation_schedule *schedule) +{ + enum cmd_error_code ret; + enum lttng_rotation_status status; + uint64_t value; + + status = lttng_rotation_schedule_periodic_get_period(schedule, + &value); + if (status != LTTNG_ROTATION_STATUS_OK) { + ERR("Failed to retrieve period parameter from periodic rotation schedule."); + ret = CMD_ERROR; + goto end; + } + + MSG(" timer period: %" PRIu64" %s", value, USEC_UNIT); + ret = CMD_SUCCESS; +end: + return ret; +} + +static enum cmd_error_code print_size_threshold_rotation_schedule( + const struct lttng_rotation_schedule *schedule) +{ + enum cmd_error_code ret; + enum lttng_rotation_status status; + uint64_t value; + + status = lttng_rotation_schedule_size_threshold_get_threshold(schedule, + &value); + if (status != LTTNG_ROTATION_STATUS_OK) { + ERR("Failed to retrieve size parameter from size-based rotation schedule."); + ret = CMD_ERROR; + goto end; + } + + MSG(" size threshold: %" PRIu64" bytes", value); + ret = CMD_SUCCESS; +end: + return ret; +} + +static enum cmd_error_code print_rotation_schedule( + const struct lttng_rotation_schedule *schedule) +{ + enum cmd_error_code ret; + + switch (lttng_rotation_schedule_get_type(schedule)) { + case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD: + ret = print_size_threshold_rotation_schedule(schedule); + break; + case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC: + ret = print_periodic_rotation_schedule(schedule); + break; + default: + ret = CMD_ERROR; + } + return ret; +} + +/* + * List the automatic rotation settings. + */ +static enum cmd_error_code list_rotate_settings(const char *session_name) +{ + int ret; + enum cmd_error_code cmd_ret = CMD_SUCCESS; + unsigned int count, i; + struct lttng_rotation_schedules *schedules = NULL; + enum lttng_rotation_status status; + + ret = lttng_session_list_rotation_schedules(session_name, &schedules); + if (ret != LTTNG_OK) { + ERR("Failed to list session rotation schedules: %s", lttng_strerror(ret)); + cmd_ret = CMD_ERROR; + goto end; + } + + status = lttng_rotation_schedules_get_count(schedules, &count); + if (status != LTTNG_ROTATION_STATUS_OK) { + ERR("Failed to retrieve the number of session rotation schedules."); + cmd_ret = CMD_ERROR; + goto end; + } + + if (count == 0) { + cmd_ret = CMD_SUCCESS; + goto end; + } + + MSG("Automatic rotation schedules:"); + if (lttng_opt_mi) { + ret = mi_lttng_writer_open_element(the_writer, + mi_lttng_element_rotation_schedules); + if (ret) { + cmd_ret = CMD_ERROR; + goto end; + } + } + + for (i = 0; i < count; i++) { + enum cmd_error_code tmp_ret = CMD_SUCCESS; + const struct lttng_rotation_schedule *schedule; + + schedule = lttng_rotation_schedules_get_at_index(schedules, i); + if (!schedule) { + ERR("Failed to retrieve session rotation schedule."); + cmd_ret = CMD_ERROR; + goto end; + } + + if (lttng_opt_mi) { + ret = mi_lttng_rotation_schedule(the_writer, schedule); + if (ret) { + tmp_ret = CMD_ERROR; + } + } else { + tmp_ret = print_rotation_schedule(schedule); + } + + /* + * Report an error if the serialization of any of the + * descriptors failed. + */ + cmd_ret = cmd_ret ? cmd_ret : tmp_ret; + } + + _MSG("\n"); + if (lttng_opt_mi) { + /* Close the rotation_schedules element. */ + ret = mi_lttng_writer_close_element(the_writer); + if (ret) { + cmd_ret = CMD_ERROR; + goto end; + } + } +end: + lttng_rotation_schedules_destroy(schedules); + return cmd_ret; +} + +/* + * Machine interface + * Find the session with session_name as name + * and print his informations. + */ +static int mi_list_session(const char *session_name, + struct lttng_session *sessions, int count) +{ + int ret, i; + unsigned int session_found = 0; + + if (session_name == NULL) { + ret = -LTTNG_ERR_SESS_NOT_FOUND; + goto end; + } + + for (i = 0; i < count; i++) { + if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) { + /* We need to leave it open to append other informations + * like domain, channel, events etc.*/ + session_found = 1; + ret = mi_lttng_session(the_writer, &sessions[i], 1); + if (ret) { + goto end; + } + break; + } + } + + if (!session_found) { + ERR("Session '%s' not found", session_name); + ret = -LTTNG_ERR_SESS_NOT_FOUND; + goto end; + } + +end: + return ret; +} + +/* + * Machine interface + * List all availables session + */ +static int mi_list_sessions(struct lttng_session *sessions, int count) +{ + int ret, i; + + /* Opening sessions element */ + ret = mi_lttng_sessions_open(the_writer); + if (ret) { + goto end; + } + + /* Listing sessions */ + for (i = 0; i < count; i++) { + ret = mi_lttng_session(the_writer, &sessions[i], 0); + if (ret) { + goto end; + } + } + + /* Closing sessions element */ + ret = mi_lttng_writer_close_element(the_writer); + if (ret) { + goto end; + } + +end: + return ret; +} + +/* + * List available tracing session. List only basic information. + * + * If session_name is NULL, all sessions are listed. + */ +static int list_sessions(const char *session_name) +{ + int ret = CMD_SUCCESS; + int count, i; + unsigned int session_found = 0; + struct lttng_session *sessions = NULL; + + count = lttng_list_sessions(&sessions); + DBG("Session count %d", count); + if (count < 0) { + ret = CMD_ERROR; + ERR("%s", lttng_strerror(count)); + goto end; + } + + if (lttng_opt_mi) { + /* Mi */ + if (session_name == NULL) { + /* List all sessions */ + ret = mi_list_sessions(sessions, count); + } else { + /* Note : this return an open session element */ + ret = mi_list_session(session_name, sessions, count); + } + if (ret) { + ret = CMD_ERROR; + goto end; + } + } else { + /* Pretty print */ + if (count == 0) { + MSG("Currently no available recording session"); + goto end; + } + + if (session_name == NULL) { + MSG("Available recording sessions:"); + } + + for (i = 0; i < count; i++) { + if (session_name != NULL) { + if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) { + session_found = 1; + MSG("Recording session %s: [%s%s]", session_name, + active_string(sessions[i].enabled), + snapshot_string(sessions[i].snapshot_mode)); + if (*sessions[i].path) { + MSG("%sTrace output: %s\n", indent4, sessions[i].path); + } + memcpy(&the_listed_session, + &sessions[i], + sizeof(the_listed_session)); + break; + } + } else { + MSG(" %d) %s [%s%s]", i + 1, + sessions[i].name, + active_string(sessions[i].enabled), + snapshot_string(sessions[i].snapshot_mode)); + if (*sessions[i].path) { + MSG("%sTrace output: %s", indent4, sessions[i].path); + } + if (sessions[i].live_timer_interval != 0) { + MSG("%sLive timer interval: %u %s", indent4, + sessions[i].live_timer_interval, + USEC_UNIT); + } + MSG(""); + } + } + + if (!session_found && session_name != NULL) { + ERR("Session '%s' not found", session_name); + ret = CMD_ERROR; + goto end; + } + + if (session_name == NULL) { + MSG("\nUse lttng list for more details"); + } + } + +end: + free(sessions); + return ret; +} + + +/* + * Machine Interface + * list available domain(s) for a session. + */ +static int mi_list_domains(struct lttng_domain *domains, int count) +{ + int i, ret; + /* Open domains element */ + ret = mi_lttng_domains_open(the_writer); + if (ret) { + goto end; + } + + for (i = 0; i < count; i++) { + ret = mi_lttng_domain(the_writer, &domains[i], 0); + if (ret) { + goto end; + } + } + + /* Closing domains element */ + ret = mi_lttng_writer_close_element(the_writer); + if (ret) { + goto end; + } +end: + return ret; +} + +/* + * List available domain(s) for a session. + */ +static int list_domains(const char *session_name) +{ + int i, count, ret = CMD_SUCCESS; + struct lttng_domain *domains = NULL; + + + count = lttng_list_domains(session_name, &domains); + if (count < 0) { + ret = CMD_ERROR; + ERR("%s", lttng_strerror(count)); + goto end; + } + + if (lttng_opt_mi) { + /* Mi output */ + ret = mi_list_domains(domains, count); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } else { + /* Pretty print */ + MSG("Domains:\n-------------"); + if (count == 0) { + MSG(" None"); + goto end; + } + + for (i = 0; i < count; i++) { + switch (domains[i].type) { + case LTTNG_DOMAIN_KERNEL: + MSG(" - Kernel"); + break; + case LTTNG_DOMAIN_UST: + MSG(" - UST global"); + break; + case LTTNG_DOMAIN_JUL: + MSG(" - JUL (Java Util Logging)"); + break; + case LTTNG_DOMAIN_LOG4J: + MSG(" - LOG4j (Logging for Java)"); + break; + case LTTNG_DOMAIN_PYTHON: + MSG(" - Python (logging)"); + break; + default: + break; + } + } + } + +error: + free(domains); + +end: + return ret; +} + +/* + * The 'list ' first level command + */ +int cmd_list(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS; + const char *session_name, *leftover = NULL; + static poptContext pc; + struct lttng_domain domain; + struct lttng_domain *domains = NULL; + + memset(&domain, 0, sizeof(domain)); + + if (argc < 1) { + ret = CMD_ERROR; + goto end; + } + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_USERSPACE: + opt_userspace = 1; + break; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + default: + ret = CMD_UNDEFINED; + goto end; + } + } + + /* Mi check */ + if (lttng_opt_mi) { + the_writer = mi_lttng_writer_create( + fileno(stdout), lttng_opt_mi); + if (!the_writer) { + ret = CMD_ERROR; + goto end; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open( + the_writer, mi_lttng_element_command_list); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element( + the_writer, mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + /* Get session name (trailing argument) */ + session_name = poptGetArg(pc); + DBG2("Session name: %s", session_name); + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + ret = CMD_ERROR; + goto end; + } + + if (opt_kernel) { + domain.type = LTTNG_DOMAIN_KERNEL; + } else if (opt_userspace) { + DBG2("Listing userspace global domain"); + domain.type = LTTNG_DOMAIN_UST; + } else if (opt_jul) { + DBG2("Listing JUL domain"); + domain.type = LTTNG_DOMAIN_JUL; + } else if (opt_log4j) { + domain.type = LTTNG_DOMAIN_LOG4J; + } else if (opt_python) { + domain.type = LTTNG_DOMAIN_PYTHON; + } + + if (!opt_kernel && opt_syscall) { + WARN("--syscall will only work with the Kernel domain (-k)"); + ret = CMD_ERROR; + goto end; + } + + if (opt_kernel || opt_userspace || opt_jul || opt_log4j || opt_python) { + the_handle = lttng_create_handle(session_name, &domain); + if (the_handle == NULL) { + ret = CMD_FATAL; + goto end; + } + } + + if (session_name == NULL) { + if (!opt_kernel && !opt_userspace && !opt_jul && !opt_log4j + && !opt_python) { + ret = list_sessions(NULL); + if (ret) { + goto end; + } + } + if (opt_kernel) { + if (opt_syscall) { + ret = list_syscalls(); + if (ret) { + goto end; + } + } else { + ret = list_kernel_events(); + if (ret) { + goto end; + } + } + } + if (opt_userspace) { + if (opt_fields) { + ret = list_ust_event_fields(); + } else { + ret = list_ust_events(); + } + if (ret) { + goto end; + } + } + if (opt_jul || opt_log4j || opt_python) { + ret = list_agent_events(); + if (ret) { + goto end; + } + } + } else { + /* List session attributes */ + if (lttng_opt_mi) { + /* Open element sessions + * Present for xml consistency */ + ret = mi_lttng_sessions_open(the_writer); + if (ret) { + goto end; + } + } + /* MI: the ouptut of list_sessions is an unclosed session element */ + ret = list_sessions(session_name); + if (ret) { + goto end; + } + + ret = list_rotate_settings(session_name); + if (ret) { + goto end; + } + + /* Domain listing */ + if (opt_domain) { + ret = list_domains(session_name); + goto end; + } + + /* Channel listing */ + if (opt_kernel || opt_userspace) { + if (lttng_opt_mi) { + /* Add of domains and domain element for xml + * consistency and validation + */ + ret = mi_lttng_domains_open(the_writer); + if (ret) { + goto end; + } + + /* Open domain and leave it open for + * nested channels printing */ + ret = mi_lttng_domain(the_writer, &domain, 1); + if (ret) { + goto end; + } + + } + + + /* Trackers */ + ret = list_trackers(&domain); + if (ret) { + goto end; + } + + /* Channels */ + ret = list_channels(opt_channel); + if (ret) { + goto end; + } + + if (lttng_opt_mi) { + /* Close domain and domain element */ + ret = mi_lttng_close_multi_element( + the_writer, 2); + } + if (ret) { + goto end; + } + + + } else { + int i, nb_domain; + + /* We want all domain(s) */ + nb_domain = lttng_list_domains(session_name, &domains); + if (nb_domain < 0) { + ret = CMD_ERROR; + ERR("%s", lttng_strerror(nb_domain)); + goto end; + } + + if (lttng_opt_mi) { + ret = mi_lttng_domains_open(the_writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + for (i = 0; i < nb_domain; i++) { + switch (domains[i].type) { + case LTTNG_DOMAIN_KERNEL: + MSG("=== Domain: Linux kernel ===\n"); + break; + case LTTNG_DOMAIN_UST: + MSG("=== Domain: User space ===\n"); + MSG("Buffering scheme: %s\n", + domains[i].buf_type == + LTTNG_BUFFER_PER_PID ? "per-process" : "per-user"); + break; + case LTTNG_DOMAIN_JUL: + MSG("=== Domain: java.util.logging (JUL) ===\n"); + break; + case LTTNG_DOMAIN_LOG4J: + MSG("=== Domain: log4j ===\n"); + break; + case LTTNG_DOMAIN_PYTHON: + MSG("=== Domain: Python logging ===\n"); + break; + default: + MSG("=== Domain: Unimplemented ===\n"); + break; + } + + if (lttng_opt_mi) { + ret = mi_lttng_domain(the_writer, + &domains[i], 1); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + /* Clean handle before creating a new one */ + if (the_handle) { + lttng_destroy_handle(the_handle); + } + + the_handle = lttng_create_handle( + session_name, &domains[i]); + if (the_handle == NULL) { + ret = CMD_FATAL; + goto end; + } + + if (domains[i].type == LTTNG_DOMAIN_JUL || + domains[i].type == LTTNG_DOMAIN_LOG4J || + domains[i].type == LTTNG_DOMAIN_PYTHON) { + ret = list_session_agent_events(); + if (ret) { + goto end; + } + + goto next_domain; + } + + switch (domains[i].type) { + case LTTNG_DOMAIN_KERNEL: + case LTTNG_DOMAIN_UST: + ret = list_trackers(&domains[i]); + if (ret) { + goto end; + } + break; + default: + break; + } + + ret = list_channels(opt_channel); + if (ret) { + goto end; + } + +next_domain: + if (lttng_opt_mi) { + /* Close domain element */ + ret = mi_lttng_writer_close_element( + the_writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + } + if (lttng_opt_mi) { + /* Close the domains, session and sessions element */ + ret = mi_lttng_close_multi_element( + the_writer, 3); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + } + } + + /* Mi closing */ + if (lttng_opt_mi) { + /* Close output element */ + ret = mi_lttng_writer_close_element(the_writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(the_writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } +end: + /* Mi clean-up */ + if (the_writer && mi_lttng_writer_destroy(the_writer)) { + /* Preserve original error code */ + ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; + } + + free(domains); + if (the_handle) { + lttng_destroy_handle(the_handle); + } + + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/list_triggers.c b/src/bin/lttng/commands/list_triggers.c deleted file mode 100644 index 50c5894df..000000000 --- a/src/bin/lttng/commands/list_triggers.c +++ /dev/null @@ -1,1449 +0,0 @@ -/* - * Copyright (C) 2021 Simon Marchi - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include - -#include "../command.h" - -#include "common/argpar/argpar.h" -#include "common/dynamic-array.h" -#include "common/mi-lttng.h" -/* For lttng_condition_type_str(). */ -#include "lttng/condition/condition-internal.h" -#include "lttng/condition/event-rule-matches.h" -#include "lttng/condition/event-rule-matches-internal.h" -/* For lttng_domain_type_str(). */ -#include "lttng/domain-internal.h" -/* For lttng_event_rule_kernel_syscall_emission_site_str() */ -#include "lttng/event-rule/kernel-syscall-internal.h" -#include "../loglevel.h" -#include - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -#define INDENTATION_LEVEL_STR " " - -typedef enum lttng_event_rule_status (*event_rule_logging_get_name_pattern)( - const struct lttng_event_rule *rule, const char **pattern); -typedef enum lttng_event_rule_status (*event_rule_logging_get_filter)( - const struct lttng_event_rule *rule, const char **expression); -typedef enum lttng_event_rule_status (*event_rule_logging_get_log_level_rule)( - const struct lttng_event_rule *rule, - const struct lttng_log_level_rule **log_level_rule); - -enum { - OPT_HELP, - OPT_LIST_OPTIONS, -}; - -static const -struct argpar_opt_descr list_trigger_options[] = { - { OPT_HELP, 'h', "help", false }, - { OPT_LIST_OPTIONS, '\0', "list-options", false }, - ARGPAR_OPT_DESCR_SENTINEL, -}; - -static void print_condition_session_consumed_size( - const struct lttng_condition *condition) -{ - enum lttng_condition_status condition_status; - const char *session_name; - uint64_t threshold; - - condition_status = - lttng_condition_session_consumed_size_get_session_name( - condition, &session_name); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); - - lttng_condition_session_consumed_size_get_threshold( - condition, &threshold); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); - - MSG(" session name: %s", session_name); - MSG(" threshold: %" PRIu64 " bytes", threshold); -} - -static void print_condition_buffer_usage( - const struct lttng_condition *condition) -{ - enum lttng_condition_status condition_status; - const char *session_name, *channel_name; - enum lttng_domain_type domain_type; - uint64_t threshold; - - condition_status = lttng_condition_buffer_usage_get_session_name( - condition, &session_name); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); - - condition_status = lttng_condition_buffer_usage_get_channel_name( - condition, &channel_name); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); - - condition_status = lttng_condition_buffer_usage_get_domain_type( - condition, &domain_type); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); - - MSG(" session name: %s", session_name); - MSG(" channel name: %s", channel_name); - MSG(" domain: %s", lttng_domain_type_str(domain_type)); - - condition_status = lttng_condition_buffer_usage_get_threshold( - condition, &threshold); - if (condition_status == LTTNG_CONDITION_STATUS_OK) { - MSG(" threshold (bytes): %" PRIu64, threshold); - } else { - double threshold_ratio; - - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_UNSET); - - condition_status = - lttng_condition_buffer_usage_get_threshold_ratio( - condition, &threshold_ratio); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); - - MSG(" threshold (ratio): %.2f", threshold_ratio); - } -} - -static void print_condition_session_rotation( - const struct lttng_condition *condition) -{ - enum lttng_condition_status condition_status; - const char *session_name; - - condition_status = lttng_condition_session_rotation_get_session_name( - condition, &session_name); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); - - MSG(" session name: %s", session_name); -} - -/* - * Returns the human-readable log level name associated with a numerical value - * if there is one. The Log4j and JUL event rule have discontinuous log level - * values (a value can fall between two labels). In those cases, NULL is - * returned. - */ -static const char *get_pretty_loglevel_name( - enum lttng_event_rule_type event_rule_type, int loglevel) -{ - const char *name = NULL; - - switch (event_rule_type) { - case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: - name = loglevel_value_to_name(loglevel); - break; - case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: - name = loglevel_log4j_value_to_name(loglevel); - break; - case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: - name = loglevel_jul_value_to_name(loglevel); - break; - case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: - name = loglevel_python_value_to_name(loglevel); - break; - default: - break; - } - - return name; -} - -static -void print_event_rule_user_tracepoint(const struct lttng_event_rule *event_rule) -{ - enum lttng_event_rule_status event_rule_status; - const char *pattern; - const char *filter; - int log_level; - const struct lttng_log_level_rule *log_level_rule = NULL; - unsigned int exclusions_count; - int i; - - event_rule_status = lttng_event_rule_user_tracepoint_get_name_pattern( - event_rule, &pattern); - LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); - - _MSG(" rule: %s (type: user tracepoint", pattern); - - event_rule_status = lttng_event_rule_user_tracepoint_get_filter( - event_rule, &filter); - if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { - _MSG(", filter: %s", filter); - } else { - LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); - } - - event_rule_status = lttng_event_rule_user_tracepoint_get_log_level_rule( - event_rule, &log_level_rule); - if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { - enum lttng_log_level_rule_status llr_status; - const char *log_level_op; - const char *pretty_loglevel_name; - - switch (lttng_log_level_rule_get_type(log_level_rule)) { - case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: - log_level_op = "is"; - llr_status = lttng_log_level_rule_exactly_get_level( - log_level_rule, &log_level); - break; - case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: - log_level_op = "at least"; - llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level( - log_level_rule, &log_level); - break; - default: - abort(); - } - - LTTNG_ASSERT(llr_status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); - - pretty_loglevel_name = get_pretty_loglevel_name( - LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT, log_level); - if (pretty_loglevel_name) { - _MSG(", log level %s %s", log_level_op, - pretty_loglevel_name); - } else { - _MSG(", log level %s %d", log_level_op, log_level); - } - } else { - LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); - } - - event_rule_status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count( - event_rule, &exclusions_count); - LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); - if (exclusions_count > 0) { - _MSG(", exclusions: "); - for (i = 0; i < exclusions_count; i++) { - const char *exclusion; - - event_rule_status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index( - event_rule, i, &exclusion); - LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); - - _MSG("%s%s", i > 0 ? "," : "", exclusion); - } - } - - MSG(")"); -} - -static -void print_event_rule_kernel_tracepoint(const struct lttng_event_rule *event_rule) -{ - enum lttng_event_rule_status event_rule_status; - const char *pattern; - const char *filter; - - event_rule_status = lttng_event_rule_kernel_tracepoint_get_name_pattern( - event_rule, &pattern); - LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); - - _MSG(" rule: %s (type: kernel tracepoint", pattern); - - event_rule_status = lttng_event_rule_kernel_tracepoint_get_filter( - event_rule, &filter); - if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { - _MSG(", filter: %s", filter); - } else { - LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); - } - - MSG(")"); -} - -static -void print_event_rule_logging(const struct lttng_event_rule *event_rule) -{ - enum lttng_event_rule_status event_rule_status; - enum lttng_event_rule_type event_rule_type = lttng_event_rule_get_type(event_rule); - const char *pattern; - const char *filter; - int log_level; - const struct lttng_log_level_rule *log_level_rule = NULL; - const char *type_str = NULL; - - event_rule_logging_get_name_pattern logging_get_name_pattern; - event_rule_logging_get_filter logging_get_filter; - event_rule_logging_get_log_level_rule logging_get_log_level_rule; - - switch (event_rule_type) { - case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: - logging_get_name_pattern = - lttng_event_rule_jul_logging_get_name_pattern; - logging_get_filter = lttng_event_rule_jul_logging_get_filter; - logging_get_log_level_rule = - lttng_event_rule_jul_logging_get_log_level_rule; - type_str = "jul"; - break; - case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: - logging_get_name_pattern = - lttng_event_rule_log4j_logging_get_name_pattern; - logging_get_filter = lttng_event_rule_log4j_logging_get_filter; - logging_get_log_level_rule = - lttng_event_rule_log4j_logging_get_log_level_rule; - type_str = "log4j"; - break; - case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: - logging_get_name_pattern = - lttng_event_rule_python_logging_get_name_pattern; - logging_get_filter = lttng_event_rule_python_logging_get_filter; - logging_get_log_level_rule = - lttng_event_rule_python_logging_get_log_level_rule; - type_str = "python"; - break; - default: - abort(); - break; - } - - event_rule_status = logging_get_name_pattern( - event_rule, &pattern); - LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); - - _MSG(" rule: %s (type: %s:logging", pattern, type_str); - - event_rule_status = logging_get_filter( - event_rule, &filter); - if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { - _MSG(", filter: %s", filter); - } else { - LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); - } - - event_rule_status = logging_get_log_level_rule( - event_rule, &log_level_rule); - if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { - enum lttng_log_level_rule_status llr_status; - const char *log_level_op; - const char *pretty_loglevel_name; - - switch (lttng_log_level_rule_get_type(log_level_rule)) { - case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: - log_level_op = "is"; - llr_status = lttng_log_level_rule_exactly_get_level( - log_level_rule, &log_level); - break; - case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: - log_level_op = "at least"; - llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level( - log_level_rule, &log_level); - break; - default: - abort(); - } - - LTTNG_ASSERT(llr_status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); - - pretty_loglevel_name = get_pretty_loglevel_name( - event_rule_type, log_level); - if (pretty_loglevel_name) { - _MSG(", log level %s %s", log_level_op, - pretty_loglevel_name); - } else { - _MSG(", log level %s %d", log_level_op, log_level); - } - } else { - LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); - } - - MSG(")"); -} - -static void print_kernel_probe_location( - const struct lttng_kernel_probe_location *location) -{ - enum lttng_kernel_probe_location_status status; - switch (lttng_kernel_probe_location_get_type(location)) { - case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS: - { - uint64_t address; - - status = lttng_kernel_probe_location_address_get_address( - location, &address); - if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) { - ERR("Getting kernel probe location address failed."); - goto end; - } - - _MSG("0x%" PRIx64, address); - - break; - } - case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET: - { - uint64_t offset; - const char *symbol_name; - - symbol_name = lttng_kernel_probe_location_symbol_get_name( - location); - if (!symbol_name) { - ERR("Getting kernel probe location symbol name failed."); - goto end; - } - - status = lttng_kernel_probe_location_symbol_get_offset( - location, &offset); - if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) { - ERR("Getting kernel probe location address failed."); - goto end; - } - - if (offset == 0) { - _MSG("%s", symbol_name); - } else { - _MSG("%s+0x%" PRIx64, symbol_name, offset); - } - - break; - } - default: - abort(); - }; -end: - return; -} - -static -void print_event_rule_kernel_probe(const struct lttng_event_rule *event_rule) -{ - enum lttng_event_rule_status event_rule_status; - const char *name; - const struct lttng_kernel_probe_location *location; - - LTTNG_ASSERT(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE); - - event_rule_status = lttng_event_rule_kernel_kprobe_get_event_name(event_rule, &name); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to get kprobe event rule's name."); - goto end; - } - - event_rule_status = lttng_event_rule_kernel_kprobe_get_location( - event_rule, &location); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to get kprobe event rule's location."); - goto end; - } - - _MSG(" rule: %s (type: kernel:kprobe, location: ", name); - - print_kernel_probe_location(location); - - MSG(")"); - -end: - return; -} - -static -void print_event_rule_userspace_probe(const struct lttng_event_rule *event_rule) -{ - enum lttng_event_rule_status event_rule_status; - const char *name; - const struct lttng_userspace_probe_location *location; - enum lttng_userspace_probe_location_type userspace_probe_location_type; - - LTTNG_ASSERT(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE); - - event_rule_status = lttng_event_rule_kernel_uprobe_get_event_name( - event_rule, &name); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to get uprobe event rule's name."); - goto end; - } - - event_rule_status = lttng_event_rule_kernel_uprobe_get_location( - event_rule, &location); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to get uprobe event rule's location."); - goto end; - } - - _MSG(" rule: %s (type: kernel:uprobe, ", name); - - userspace_probe_location_type = - lttng_userspace_probe_location_get_type(location); - - switch (userspace_probe_location_type) { - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: - { - const char *binary_path, *function_name; - - binary_path = lttng_userspace_probe_location_function_get_binary_path( - location); - function_name = lttng_userspace_probe_location_function_get_function_name( - location); - - _MSG("location type: ELF, location: %s:%s", binary_path, function_name); - break; - } - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: - { - const char *binary_path, *provider_name, *probe_name; - - binary_path = lttng_userspace_probe_location_tracepoint_get_binary_path( - location); - provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name( - location); - probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name( - location); - _MSG("location type: SDT, location: %s:%s:%s", binary_path, provider_name, probe_name); - break; - } - default: - abort(); - } - - MSG(")"); - -end: - return; -} - -static -void print_event_rule_syscall(const struct lttng_event_rule *event_rule) -{ - const char *pattern, *filter; - enum lttng_event_rule_status event_rule_status; - enum lttng_event_rule_kernel_syscall_emission_site emission_site; - - LTTNG_ASSERT(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL); - - emission_site = - lttng_event_rule_kernel_syscall_get_emission_site(event_rule); - - event_rule_status = lttng_event_rule_kernel_syscall_get_name_pattern( - event_rule, &pattern); - LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); - - _MSG(" rule: %s (type: kernel:syscall:%s", pattern, - lttng_event_rule_kernel_syscall_emission_site_str( - emission_site)); - - event_rule_status = lttng_event_rule_kernel_syscall_get_filter( - event_rule, &filter); - if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { - _MSG(", filter: %s", filter); - } else { - LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); - } - - MSG(")"); -} - -static -void print_event_rule(const struct lttng_event_rule *event_rule) -{ - const enum lttng_event_rule_type event_rule_type = - lttng_event_rule_get_type(event_rule); - - switch (event_rule_type) { - case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: - print_event_rule_user_tracepoint(event_rule); - break; - case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: - print_event_rule_kernel_tracepoint(event_rule); - break; - case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: - case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: - case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: - print_event_rule_logging(event_rule); - break; - case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE: - print_event_rule_kernel_probe(event_rule); - break; - case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE: - print_event_rule_userspace_probe(event_rule); - break; - case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: - print_event_rule_syscall(event_rule); - break; - default: - abort(); - } -} - -static -void print_one_event_expr(const struct lttng_event_expr *event_expr) -{ - enum lttng_event_expr_type type; - - type = lttng_event_expr_get_type(event_expr); - - switch (type) { - case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: - { - const char *name; - - name = lttng_event_expr_event_payload_field_get_name( - event_expr); - _MSG("%s", name); - - break; - } - case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: - { - const char *name; - - name = lttng_event_expr_channel_context_field_get_name( - event_expr); - _MSG("$ctx.%s", name); - - break; - } - case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: - { - const char *provider_name; - const char *type_name; - - provider_name = lttng_event_expr_app_specific_context_field_get_provider_name( - event_expr); - type_name = lttng_event_expr_app_specific_context_field_get_type_name( - event_expr); - - _MSG("$app.%s:%s", provider_name, type_name); - - break; - } - case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: - { - unsigned int index; - const struct lttng_event_expr *parent_expr; - enum lttng_event_expr_status status; - - parent_expr = lttng_event_expr_array_field_element_get_parent_expr( - event_expr); - LTTNG_ASSERT(parent_expr != NULL); - - print_one_event_expr(parent_expr); - - status = lttng_event_expr_array_field_element_get_index( - event_expr, &index); - LTTNG_ASSERT(status == LTTNG_EVENT_EXPR_STATUS_OK); - - _MSG("[%u]", index); - - break; - } - default: - abort(); - } -} - -static -void print_indentation(unsigned int indentation_level) -{ - unsigned int i; - - for (i = 0; i < indentation_level; i++) { - _MSG(INDENTATION_LEVEL_STR); - } -} - -static -void print_error_query_results(struct lttng_error_query_results *results, - unsigned int base_indentation_level) -{ - unsigned int i, count, printed_errors_count = 0; - enum lttng_error_query_results_status results_status; - - results_status = lttng_error_query_results_get_count(results, &count); - LTTNG_ASSERT(results_status == LTTNG_ERROR_QUERY_RESULTS_STATUS_OK); - - LTTNG_ASSERT(results); - - print_indentation(base_indentation_level); - _MSG("errors:"); - - for (i = 0; i < count; i++) { - const struct lttng_error_query_result *result; - enum lttng_error_query_result_status result_status; - const char *result_name; - const char *result_description; - uint64_t result_value; - - results_status = lttng_error_query_results_get_result( - results, &result, i); - LTTNG_ASSERT(results_status == LTTNG_ERROR_QUERY_RESULTS_STATUS_OK); - - result_status = lttng_error_query_result_get_name( - result, &result_name); - LTTNG_ASSERT(result_status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK); - result_status = lttng_error_query_result_get_description( - result, &result_description); - LTTNG_ASSERT(result_status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK); - - - if (lttng_error_query_result_get_type(result) == - LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER) { - result_status = lttng_error_query_result_counter_get_value( - result, &result_value); - LTTNG_ASSERT(result_status == - LTTNG_ERROR_QUERY_RESULT_STATUS_OK); - if (result_value == 0) { - continue; - } - - MSG(""); - print_indentation(base_indentation_level + 1); - - _MSG("%s: %" PRIu64, result_name, result_value); - printed_errors_count++; - } else { - MSG(""); - print_indentation(base_indentation_level + 1); - _MSG("Unknown error query result type for result '%s' (%s)", - result_name, result_description); - continue; - } - } - - if (printed_errors_count == 0) { - _MSG(" none"); - } -} - -static void print_condition_event_rule_matches( - const struct lttng_condition *condition) -{ - const struct lttng_event_rule *event_rule; - enum lttng_condition_status condition_status; - unsigned int cap_desc_count, i; - - condition_status = lttng_condition_event_rule_matches_get_rule( - condition, &event_rule); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); - - print_event_rule(event_rule); - - condition_status = - lttng_condition_event_rule_matches_get_capture_descriptor_count( - condition, &cap_desc_count); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); - - if (cap_desc_count > 0) { - MSG(" captures:"); - - for (i = 0; i < cap_desc_count; i++) { - const struct lttng_event_expr *cap_desc = - lttng_condition_event_rule_matches_get_capture_descriptor_at_index( - condition, i); - - _MSG(" - "); - print_one_event_expr(cap_desc); - MSG(""); - } - } -} - -static void print_action_errors(const struct lttng_trigger *trigger, - const struct lttng_action *action, - const uint64_t *action_path_indexes, - size_t action_path_length) -{ - enum lttng_error_code error_query_ret; - struct lttng_error_query_results *results = NULL; - const char *trigger_name; - uid_t trigger_uid; - enum lttng_trigger_status trigger_status; - struct lttng_error_query *query; - struct lttng_action_path *action_path = lttng_action_path_create( - action_path_indexes, action_path_length); - - LTTNG_ASSERT(action_path); - - query = lttng_error_query_action_create(trigger, action_path); - LTTNG_ASSERT(query); - - trigger_status = lttng_trigger_get_name(trigger, &trigger_name); - /* - * Anonymous triggers are not listed; this would be an internal error. - */ - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_uid); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - error_query_ret = lttng_error_query_execute( - query, lttng_session_daemon_command_endpoint, &results); - if (error_query_ret != LTTNG_OK) { - ERR("Failed to query errors of trigger '%s' (owner uid: %d): %s", - trigger_name, (int) trigger_uid, - lttng_strerror(-error_query_ret)); - goto end; - } - - print_error_query_results(results, 3); - -end: - MSG(""); - lttng_error_query_destroy(query); - lttng_error_query_results_destroy(results); - lttng_action_path_destroy(action_path); -} - -static -void print_one_action(const struct lttng_trigger *trigger, - const struct lttng_action *action, - const uint64_t *action_path_indexes, - size_t action_path_length) -{ - enum lttng_action_type action_type; - enum lttng_action_status action_status; - const struct lttng_rate_policy *policy = NULL; - const char *value; - - action_type = lttng_action_get_type(action); - LTTNG_ASSERT(action_type != LTTNG_ACTION_TYPE_LIST); - - switch (action_type) { - case LTTNG_ACTION_TYPE_NOTIFY: - _MSG("notify"); - - action_status = lttng_action_notify_get_rate_policy( - action, &policy); - if (action_status != LTTNG_ACTION_STATUS_OK) { - ERR("Failed to retrieve rate policy."); - goto end; - } - break; - case LTTNG_ACTION_TYPE_START_SESSION: - action_status = lttng_action_start_session_get_session_name( - action, &value); - LTTNG_ASSERT(action_status == LTTNG_ACTION_STATUS_OK); - _MSG("start session `%s`", value); - - action_status = lttng_action_start_session_get_rate_policy( - action, &policy); - if (action_status != LTTNG_ACTION_STATUS_OK) { - ERR("Failed to retrieve rate policy."); - goto end; - } - break; - case LTTNG_ACTION_TYPE_STOP_SESSION: - action_status = lttng_action_stop_session_get_session_name( - action, &value); - LTTNG_ASSERT(action_status == LTTNG_ACTION_STATUS_OK); - _MSG("stop session `%s`", value); - - action_status = lttng_action_stop_session_get_rate_policy( - action, &policy); - if (action_status != LTTNG_ACTION_STATUS_OK) { - ERR("Failed to retrieve rate policy."); - goto end; - } - break; - case LTTNG_ACTION_TYPE_ROTATE_SESSION: - action_status = lttng_action_rotate_session_get_session_name( - action, &value); - LTTNG_ASSERT(action_status == LTTNG_ACTION_STATUS_OK); - _MSG("rotate session `%s`", value); - - action_status = lttng_action_rotate_session_get_rate_policy( - action, &policy); - if (action_status != LTTNG_ACTION_STATUS_OK) { - ERR("Failed to retrieve rate policy."); - goto end; - } - break; - case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: - { - const struct lttng_snapshot_output *output; - - action_status = lttng_action_snapshot_session_get_session_name( - action, &value); - LTTNG_ASSERT(action_status == LTTNG_ACTION_STATUS_OK); - _MSG("snapshot session `%s`", value); - - action_status = lttng_action_snapshot_session_get_output( - action, &output); - if (action_status == LTTNG_ACTION_STATUS_OK) { - const char *name; - uint64_t max_size; - const char *ctrl_url, *data_url; - bool starts_with_file, starts_with_net, starts_with_net6; - - ctrl_url = lttng_snapshot_output_get_ctrl_url(output); - LTTNG_ASSERT(ctrl_url && strlen(ctrl_url) > 0); - - data_url = lttng_snapshot_output_get_data_url(output); - LTTNG_ASSERT(data_url); - - starts_with_file = strncmp(ctrl_url, "file://", strlen("file://")) == 0; - starts_with_net = strncmp(ctrl_url, "net://", strlen("net://")) == 0; - starts_with_net6 = strncmp(ctrl_url, "net6://", strlen("net6://")) == 0; - - if (ctrl_url[0] == '/' || starts_with_file) { - if (starts_with_file) { - ctrl_url += strlen("file://"); - } - - _MSG(", path: %s", ctrl_url); - } else if (starts_with_net || starts_with_net6) { - _MSG(", url: %s", ctrl_url); - } else { - LTTNG_ASSERT(strlen(data_url) > 0); - - _MSG(", control url: %s, data url: %s", ctrl_url, data_url); - } - - name = lttng_snapshot_output_get_name(output); - LTTNG_ASSERT(name); - if (strlen(name) > 0) { - _MSG(", name: %s", name); - } - - max_size = lttng_snapshot_output_get_maxsize(output); - if (max_size != -1ULL) { - _MSG(", max size: %" PRIu64, max_size); - } - } - - action_status = lttng_action_snapshot_session_get_rate_policy( - action, &policy); - if (action_status != LTTNG_ACTION_STATUS_OK) { - ERR("Failed to retrieve rate policy."); - goto end; - } - break; - } - default: - abort(); - } - - if (policy) { - enum lttng_rate_policy_type policy_type; - enum lttng_rate_policy_status policy_status; - uint64_t policy_value = 0; - - policy_type = lttng_rate_policy_get_type(policy); - - switch (policy_type) { - case LTTNG_RATE_POLICY_TYPE_EVERY_N: - policy_status = lttng_rate_policy_every_n_get_interval( - policy, &policy_value); - if (policy_status != LTTNG_RATE_POLICY_STATUS_OK) { - ERR("Failed to get action rate policy interval"); - goto end; - } - if (policy_value > 1) { - /* The default is 1 so print only when it is a - * special case. - */ - _MSG(", rate policy: every %" PRIu64 - " occurrences", - policy_value); - } - break; - case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N: - policy_status = lttng_rate_policy_once_after_n_get_threshold( - policy, &policy_value); - if (policy_status != LTTNG_RATE_POLICY_STATUS_OK) { - ERR("Failed to get action rate policy interval"); - goto end; - } - _MSG(", rate policy: once after %" PRIu64 - " occurrences", - policy_value); - break; - default: - abort(); - } - } - - MSG(""); - print_action_errors(trigger, action, action_path_indexes, - action_path_length); - -end: - return; -} - -static -void print_trigger_errors(const struct lttng_trigger *trigger) -{ - enum lttng_error_code error_query_ret; - struct lttng_error_query_results *results = NULL; - enum lttng_trigger_status trigger_status; - const char *trigger_name; - uid_t trigger_uid; - struct lttng_error_query *query = - lttng_error_query_trigger_create(trigger); - - LTTNG_ASSERT(query); - /* - * Anonymous triggers are not listed; this would be an internal error. - */ - trigger_status = lttng_trigger_get_name(trigger, &trigger_name); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_uid); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - error_query_ret = lttng_error_query_execute( - query, lttng_session_daemon_command_endpoint, &results); - if (error_query_ret != LTTNG_OK) { - ERR("Failed to query errors of trigger '%s' (owner uid: %d): %s", - trigger_name, (int) trigger_uid, - lttng_strerror(-error_query_ret)); - goto end; - } - - print_error_query_results(results, 1); - -end: - MSG(""); - lttng_error_query_destroy(query); - lttng_error_query_results_destroy(results); -} - -static -void print_condition_errors(const struct lttng_trigger *trigger) -{ - enum lttng_error_code error_query_ret; - struct lttng_error_query_results *results = NULL; - enum lttng_trigger_status trigger_status; - const char *trigger_name; - uid_t trigger_uid; - struct lttng_error_query *query = - lttng_error_query_condition_create(trigger); - - LTTNG_ASSERT(query); - /* - * Anonymous triggers are not listed; this would be an internal error. - */ - trigger_status = lttng_trigger_get_name(trigger, &trigger_name); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_uid); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - error_query_ret = lttng_error_query_execute( - query, lttng_session_daemon_command_endpoint, &results); - if (error_query_ret != LTTNG_OK) { - ERR("Failed to query errors of condition of trigger '%s' (owner uid: %d): %s", - trigger_name, (int) trigger_uid, - lttng_strerror(-error_query_ret)); - goto end; - } - - print_error_query_results(results, 2); - -end: - MSG(""); - lttng_error_query_destroy(query); - lttng_error_query_results_destroy(results); -} - -static -void print_one_trigger(const struct lttng_trigger *trigger) -{ - const struct lttng_condition *condition; - enum lttng_condition_type condition_type; - const struct lttng_action *action; - enum lttng_action_type action_type; - enum lttng_trigger_status trigger_status; - const char *name; - uid_t trigger_uid; - - /* - * Anonymous triggers are not listed since they can't be specified nor - * referenced through the CLI. - */ - trigger_status = lttng_trigger_get_name(trigger, &name); - if (trigger_status == LTTNG_TRIGGER_STATUS_UNSET) { - goto end; - } - - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_uid); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - MSG("- name: %s", name); - MSG(" owner uid: %d", trigger_uid); - - condition = lttng_trigger_get_const_condition(trigger); - condition_type = lttng_condition_get_type(condition); - MSG(" condition: %s", lttng_condition_type_str(condition_type)); - switch (condition_type) { - case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: - print_condition_session_consumed_size(condition); - break; - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: - print_condition_buffer_usage(condition); - break; - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: - print_condition_session_rotation(condition); - break; - case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES: - print_condition_event_rule_matches(condition); - break; - default: - abort(); - } - - print_condition_errors(trigger); - - action = lttng_trigger_get_const_action(trigger); - action_type = lttng_action_get_type(action); - if (action_type == LTTNG_ACTION_TYPE_LIST) { - unsigned int count, i; - enum lttng_action_status action_status; - - MSG(" actions:"); - - action_status = lttng_action_list_get_count(action, &count); - LTTNG_ASSERT(action_status == LTTNG_ACTION_STATUS_OK); - - for (i = 0; i < count; i++) { - const uint64_t action_path_index = i; - const struct lttng_action *subaction = - lttng_action_list_get_at_index( - action, i); - - _MSG(" "); - print_one_action(trigger, subaction, &action_path_index, - 1); - } - } else { - _MSG(" action:"); - print_one_action(trigger, action, NULL, 0); - } - - print_trigger_errors(trigger); -end: - return; -} - -static -int compare_triggers_by_name(const void *a, const void *b) -{ - const struct lttng_trigger *trigger_a = *((const struct lttng_trigger **) a); - const struct lttng_trigger *trigger_b = *((const struct lttng_trigger **) b); - const char *name_a, *name_b; - enum lttng_trigger_status trigger_status; - - /* Anonymous triggers are not reachable here. */ - trigger_status = lttng_trigger_get_name(trigger_a, &name_a); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - trigger_status = lttng_trigger_get_name(trigger_b, &name_b); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - return strcmp(name_a, name_b); -} - -static int print_sorted_triggers(const struct lttng_triggers *triggers) -{ - int ret; - int i; - struct lttng_dynamic_pointer_array sorted_triggers; - enum lttng_trigger_status trigger_status; - unsigned int num_triggers; - - lttng_dynamic_pointer_array_init(&sorted_triggers, NULL); - - trigger_status = lttng_triggers_get_count(triggers, &num_triggers); - if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { - ERR("Failed to get trigger count."); - goto error; - } - - for (i = 0; i < num_triggers; i++) { - int add_ret; - const char *unused_name; - const struct lttng_trigger *trigger = - lttng_triggers_get_at_index(triggers, i); - - trigger_status = lttng_trigger_get_name(trigger, &unused_name); - switch (trigger_status) { - case LTTNG_TRIGGER_STATUS_OK: - break; - case LTTNG_TRIGGER_STATUS_UNSET: - /* Don't list anonymous triggers. */ - continue; - default: - abort(); - } - - add_ret = lttng_dynamic_pointer_array_add_pointer( - &sorted_triggers, (void *) trigger); - if (add_ret) { - ERR("Failed to allocate array of struct lttng_trigger *."); - goto error; - } - } - - qsort(sorted_triggers.array.buffer.data, num_triggers, - sizeof(struct lttng_trigger *), - compare_triggers_by_name); - - for (i = 0; i < lttng_dynamic_pointer_array_get_count(&sorted_triggers); - i++) { - const struct lttng_trigger *trigger_to_print = (const struct lttng_trigger - *) - lttng_dynamic_pointer_array_get_pointer( - &sorted_triggers, i); - - print_one_trigger(trigger_to_print); - } - - ret = 0; - goto end; -error: - ret = 1; - -end: - lttng_dynamic_pointer_array_reset(&sorted_triggers); - return ret; -} - -static enum lttng_error_code mi_error_query_trigger_callback( - const struct lttng_trigger *trigger, - struct lttng_error_query_results **results) -{ - enum lttng_error_code ret_code; - struct lttng_error_query *query = - lttng_error_query_trigger_create(trigger); - - LTTNG_ASSERT(results); - LTTNG_ASSERT(query); - - ret_code = lttng_error_query_execute( - query, lttng_session_daemon_command_endpoint, results); - if (ret_code != LTTNG_OK) { - enum lttng_trigger_status trigger_status; - const char *trigger_name; - uid_t trigger_uid; - - trigger_status = lttng_trigger_get_name(trigger, &trigger_name); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - trigger_status = lttng_trigger_get_owner_uid( - trigger, &trigger_uid); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - ERR("Failed to query errors of trigger '%s' (owner uid: %d): %s", - trigger_name, (int) trigger_uid, - lttng_strerror(-ret_code)); - } - - lttng_error_query_destroy(query); - return ret_code; -} - -static enum lttng_error_code mi_error_query_action_callback( - const struct lttng_trigger *trigger, - const struct lttng_action_path *action_path, - struct lttng_error_query_results **results) -{ - enum lttng_error_code ret_code; - struct lttng_error_query *query = - lttng_error_query_action_create(trigger, action_path); - - LTTNG_ASSERT(results); - LTTNG_ASSERT(query); - - ret_code = lttng_error_query_execute( - query, lttng_session_daemon_command_endpoint, results); - if (ret_code != LTTNG_OK) { - enum lttng_trigger_status trigger_status; - const char *trigger_name; - uid_t trigger_uid; - - trigger_status = lttng_trigger_get_name(trigger, &trigger_name); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - trigger_status = lttng_trigger_get_owner_uid( - trigger, &trigger_uid); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - ERR("Failed to query errors of an action for trigger '%s' (owner uid: %d): %s", - trigger_name, (int) trigger_uid, - lttng_strerror(-ret_code)); - } - - lttng_error_query_destroy(query); - return ret_code; -} - -static enum lttng_error_code mi_error_query_condition_callback( - const struct lttng_trigger *trigger, - struct lttng_error_query_results **results) -{ - enum lttng_error_code ret_code; - struct lttng_error_query *query = - lttng_error_query_condition_create(trigger); - - LTTNG_ASSERT(results); - LTTNG_ASSERT(query); - - ret_code = lttng_error_query_execute( - query, lttng_session_daemon_command_endpoint, results); - if (ret_code != LTTNG_OK) { - enum lttng_trigger_status trigger_status; - const char *trigger_name; - uid_t trigger_uid; - - trigger_status = lttng_trigger_get_name(trigger, &trigger_name); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - trigger_status = lttng_trigger_get_owner_uid( - trigger, &trigger_uid); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - ERR("Failed to query errors of of condition for condition of trigger '%s' (owner uid: %d): %s", - trigger_name, (int) trigger_uid, - lttng_strerror(-ret_code)); - } - - lttng_error_query_destroy(query); - return ret_code; -} - -int cmd_list_triggers(int argc, const char **argv) -{ - int ret; - struct argpar_parse_ret argpar_parse_ret = {}; - struct lttng_triggers *triggers = NULL; - int i; - struct mi_writer *mi_writer = NULL; - - argpar_parse_ret = argpar_parse( - argc - 1, argv + 1, list_trigger_options, true); - if (!argpar_parse_ret.items) { - ERR("%s", argpar_parse_ret.error); - goto error; - } - - for (i = 0; i < argpar_parse_ret.items->n_items; i++) { - const struct argpar_item *item = - argpar_parse_ret.items->items[i]; - - if (item->type == ARGPAR_ITEM_TYPE_OPT) { - const struct argpar_item_opt *item_opt = - (const struct argpar_item_opt *) item; - - switch (item_opt->descr->id) { - case OPT_HELP: - SHOW_HELP(); - ret = 0; - goto end; - - case OPT_LIST_OPTIONS: - list_cmd_options_argpar( - stdout, list_trigger_options); - ret = 0; - goto end; - - default: - abort(); - } - - } else { - const struct argpar_item_non_opt *item_non_opt = - (const struct argpar_item_non_opt *) item; - - ERR("Unexpected argument: %s", item_non_opt->arg); - } - } - - ret = lttng_list_triggers(&triggers); - if (ret != LTTNG_OK) { - ERR("Error listing triggers: %s.", lttng_strerror(-ret)); - goto error; - } - - if (lttng_opt_mi) { - mi_writer = mi_lttng_writer_create( - fileno(stdout), lttng_opt_mi); - if (!mi_writer) { - ret = CMD_ERROR; - goto end; - } - - /* Open command element. */ - ret = mi_lttng_writer_command_open(mi_writer, - mi_lttng_element_command_list_trigger); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element. */ - ret = mi_lttng_writer_open_element( - mi_writer, mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - if (lttng_opt_mi) { - const struct mi_lttng_error_query_callbacks callbacks = { - .trigger_cb = mi_error_query_trigger_callback, - .action_cb = mi_error_query_action_callback, - .condition_cb = mi_error_query_condition_callback, - }; - - ret = lttng_triggers_mi_serialize( - triggers, mi_writer, &callbacks); - if (ret != LTTNG_OK) { - ERR("Error printing MI triggers: %s.", - lttng_strerror(-ret)); - goto error; - } - } else { - ret = print_sorted_triggers(triggers); - if (ret) { - ERR("Error printing triggers"); - goto error; - } - } - - /* Mi closing. */ - if (lttng_opt_mi) { - /* Close output element. */ - ret = mi_lttng_writer_close_element(mi_writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Command element close. */ - ret = mi_lttng_writer_command_close(mi_writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - ret = 0; - goto end; - -error: - ret = 1; - -end: - argpar_parse_ret_fini(&argpar_parse_ret); - lttng_triggers_destroy(triggers); - /* Mi clean-up. */ - if (mi_writer && mi_lttng_writer_destroy(mi_writer)) { - /* Preserve original error code. */ - ret = ret ? ret : CMD_ERROR; - } - return ret; -} diff --git a/src/bin/lttng/commands/list_triggers.cpp b/src/bin/lttng/commands/list_triggers.cpp new file mode 100644 index 000000000..9a57714b6 --- /dev/null +++ b/src/bin/lttng/commands/list_triggers.cpp @@ -0,0 +1,1449 @@ +/* + * Copyright (C) 2021 Simon Marchi + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include + +#include "../command.h" + +#include "common/argpar/argpar.h" +#include "common/dynamic-array.h" +#include "common/mi-lttng.h" +/* For lttng_condition_type_str(). */ +#include "lttng/condition/condition-internal.h" +#include "lttng/condition/event-rule-matches.h" +#include "lttng/condition/event-rule-matches-internal.h" +/* For lttng_domain_type_str(). */ +#include "lttng/domain-internal.h" +/* For lttng_event_rule_kernel_syscall_emission_site_str() */ +#include "lttng/event-rule/kernel-syscall-internal.h" +#include "../loglevel.h" +#include + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +#define INDENTATION_LEVEL_STR " " + +typedef enum lttng_event_rule_status (*event_rule_logging_get_name_pattern)( + const struct lttng_event_rule *rule, const char **pattern); +typedef enum lttng_event_rule_status (*event_rule_logging_get_filter)( + const struct lttng_event_rule *rule, const char **expression); +typedef enum lttng_event_rule_status (*event_rule_logging_get_log_level_rule)( + const struct lttng_event_rule *rule, + const struct lttng_log_level_rule **log_level_rule); + +enum { + OPT_HELP, + OPT_LIST_OPTIONS, +}; + +static const +struct argpar_opt_descr list_trigger_options[] = { + { OPT_HELP, 'h', "help", false }, + { OPT_LIST_OPTIONS, '\0', "list-options", false }, + ARGPAR_OPT_DESCR_SENTINEL, +}; + +static void print_condition_session_consumed_size( + const struct lttng_condition *condition) +{ + enum lttng_condition_status condition_status; + const char *session_name; + uint64_t threshold; + + condition_status = + lttng_condition_session_consumed_size_get_session_name( + condition, &session_name); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); + + lttng_condition_session_consumed_size_get_threshold( + condition, &threshold); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); + + MSG(" session name: %s", session_name); + MSG(" threshold: %" PRIu64 " bytes", threshold); +} + +static void print_condition_buffer_usage( + const struct lttng_condition *condition) +{ + enum lttng_condition_status condition_status; + const char *session_name, *channel_name; + enum lttng_domain_type domain_type; + uint64_t threshold; + + condition_status = lttng_condition_buffer_usage_get_session_name( + condition, &session_name); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); + + condition_status = lttng_condition_buffer_usage_get_channel_name( + condition, &channel_name); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); + + condition_status = lttng_condition_buffer_usage_get_domain_type( + condition, &domain_type); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); + + MSG(" session name: %s", session_name); + MSG(" channel name: %s", channel_name); + MSG(" domain: %s", lttng_domain_type_str(domain_type)); + + condition_status = lttng_condition_buffer_usage_get_threshold( + condition, &threshold); + if (condition_status == LTTNG_CONDITION_STATUS_OK) { + MSG(" threshold (bytes): %" PRIu64, threshold); + } else { + double threshold_ratio; + + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_UNSET); + + condition_status = + lttng_condition_buffer_usage_get_threshold_ratio( + condition, &threshold_ratio); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); + + MSG(" threshold (ratio): %.2f", threshold_ratio); + } +} + +static void print_condition_session_rotation( + const struct lttng_condition *condition) +{ + enum lttng_condition_status condition_status; + const char *session_name; + + condition_status = lttng_condition_session_rotation_get_session_name( + condition, &session_name); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); + + MSG(" session name: %s", session_name); +} + +/* + * Returns the human-readable log level name associated with a numerical value + * if there is one. The Log4j and JUL event rule have discontinuous log level + * values (a value can fall between two labels). In those cases, NULL is + * returned. + */ +static const char *get_pretty_loglevel_name( + enum lttng_event_rule_type event_rule_type, int loglevel) +{ + const char *name = NULL; + + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: + name = loglevel_value_to_name(loglevel); + break; + case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: + name = loglevel_log4j_value_to_name(loglevel); + break; + case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: + name = loglevel_jul_value_to_name(loglevel); + break; + case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: + name = loglevel_python_value_to_name(loglevel); + break; + default: + break; + } + + return name; +} + +static +void print_event_rule_user_tracepoint(const struct lttng_event_rule *event_rule) +{ + enum lttng_event_rule_status event_rule_status; + const char *pattern; + const char *filter; + int log_level; + const struct lttng_log_level_rule *log_level_rule = NULL; + unsigned int exclusions_count; + int i; + + event_rule_status = lttng_event_rule_user_tracepoint_get_name_pattern( + event_rule, &pattern); + LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + + _MSG(" rule: %s (type: user tracepoint", pattern); + + event_rule_status = lttng_event_rule_user_tracepoint_get_filter( + event_rule, &filter); + if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { + _MSG(", filter: %s", filter); + } else { + LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); + } + + event_rule_status = lttng_event_rule_user_tracepoint_get_log_level_rule( + event_rule, &log_level_rule); + if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { + enum lttng_log_level_rule_status llr_status; + const char *log_level_op; + const char *pretty_loglevel_name; + + switch (lttng_log_level_rule_get_type(log_level_rule)) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + log_level_op = "is"; + llr_status = lttng_log_level_rule_exactly_get_level( + log_level_rule, &log_level); + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + log_level_op = "at least"; + llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level( + log_level_rule, &log_level); + break; + default: + abort(); + } + + LTTNG_ASSERT(llr_status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); + + pretty_loglevel_name = get_pretty_loglevel_name( + LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT, log_level); + if (pretty_loglevel_name) { + _MSG(", log level %s %s", log_level_op, + pretty_loglevel_name); + } else { + _MSG(", log level %s %d", log_level_op, log_level); + } + } else { + LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); + } + + event_rule_status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count( + event_rule, &exclusions_count); + LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + if (exclusions_count > 0) { + _MSG(", exclusions: "); + for (i = 0; i < exclusions_count; i++) { + const char *exclusion; + + event_rule_status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index( + event_rule, i, &exclusion); + LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + + _MSG("%s%s", i > 0 ? "," : "", exclusion); + } + } + + MSG(")"); +} + +static +void print_event_rule_kernel_tracepoint(const struct lttng_event_rule *event_rule) +{ + enum lttng_event_rule_status event_rule_status; + const char *pattern; + const char *filter; + + event_rule_status = lttng_event_rule_kernel_tracepoint_get_name_pattern( + event_rule, &pattern); + LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + + _MSG(" rule: %s (type: kernel tracepoint", pattern); + + event_rule_status = lttng_event_rule_kernel_tracepoint_get_filter( + event_rule, &filter); + if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { + _MSG(", filter: %s", filter); + } else { + LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); + } + + MSG(")"); +} + +static +void print_event_rule_logging(const struct lttng_event_rule *event_rule) +{ + enum lttng_event_rule_status event_rule_status; + enum lttng_event_rule_type event_rule_type = lttng_event_rule_get_type(event_rule); + const char *pattern; + const char *filter; + int log_level; + const struct lttng_log_level_rule *log_level_rule = NULL; + const char *type_str = NULL; + + event_rule_logging_get_name_pattern logging_get_name_pattern; + event_rule_logging_get_filter logging_get_filter; + event_rule_logging_get_log_level_rule logging_get_log_level_rule; + + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: + logging_get_name_pattern = + lttng_event_rule_jul_logging_get_name_pattern; + logging_get_filter = lttng_event_rule_jul_logging_get_filter; + logging_get_log_level_rule = + lttng_event_rule_jul_logging_get_log_level_rule; + type_str = "jul"; + break; + case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: + logging_get_name_pattern = + lttng_event_rule_log4j_logging_get_name_pattern; + logging_get_filter = lttng_event_rule_log4j_logging_get_filter; + logging_get_log_level_rule = + lttng_event_rule_log4j_logging_get_log_level_rule; + type_str = "log4j"; + break; + case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: + logging_get_name_pattern = + lttng_event_rule_python_logging_get_name_pattern; + logging_get_filter = lttng_event_rule_python_logging_get_filter; + logging_get_log_level_rule = + lttng_event_rule_python_logging_get_log_level_rule; + type_str = "python"; + break; + default: + abort(); + break; + } + + event_rule_status = logging_get_name_pattern( + event_rule, &pattern); + LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + + _MSG(" rule: %s (type: %s:logging", pattern, type_str); + + event_rule_status = logging_get_filter( + event_rule, &filter); + if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { + _MSG(", filter: %s", filter); + } else { + LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); + } + + event_rule_status = logging_get_log_level_rule( + event_rule, &log_level_rule); + if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { + enum lttng_log_level_rule_status llr_status; + const char *log_level_op; + const char *pretty_loglevel_name; + + switch (lttng_log_level_rule_get_type(log_level_rule)) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + log_level_op = "is"; + llr_status = lttng_log_level_rule_exactly_get_level( + log_level_rule, &log_level); + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + log_level_op = "at least"; + llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level( + log_level_rule, &log_level); + break; + default: + abort(); + } + + LTTNG_ASSERT(llr_status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); + + pretty_loglevel_name = get_pretty_loglevel_name( + event_rule_type, log_level); + if (pretty_loglevel_name) { + _MSG(", log level %s %s", log_level_op, + pretty_loglevel_name); + } else { + _MSG(", log level %s %d", log_level_op, log_level); + } + } else { + LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); + } + + MSG(")"); +} + +static void print_kernel_probe_location( + const struct lttng_kernel_probe_location *location) +{ + enum lttng_kernel_probe_location_status status; + switch (lttng_kernel_probe_location_get_type(location)) { + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS: + { + uint64_t address; + + status = lttng_kernel_probe_location_address_get_address( + location, &address); + if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) { + ERR("Getting kernel probe location address failed."); + goto end; + } + + _MSG("0x%" PRIx64, address); + + break; + } + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET: + { + uint64_t offset; + const char *symbol_name; + + symbol_name = lttng_kernel_probe_location_symbol_get_name( + location); + if (!symbol_name) { + ERR("Getting kernel probe location symbol name failed."); + goto end; + } + + status = lttng_kernel_probe_location_symbol_get_offset( + location, &offset); + if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) { + ERR("Getting kernel probe location address failed."); + goto end; + } + + if (offset == 0) { + _MSG("%s", symbol_name); + } else { + _MSG("%s+0x%" PRIx64, symbol_name, offset); + } + + break; + } + default: + abort(); + }; +end: + return; +} + +static +void print_event_rule_kernel_probe(const struct lttng_event_rule *event_rule) +{ + enum lttng_event_rule_status event_rule_status; + const char *name; + const struct lttng_kernel_probe_location *location; + + LTTNG_ASSERT(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE); + + event_rule_status = lttng_event_rule_kernel_kprobe_get_event_name(event_rule, &name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to get kprobe event rule's name."); + goto end; + } + + event_rule_status = lttng_event_rule_kernel_kprobe_get_location( + event_rule, &location); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to get kprobe event rule's location."); + goto end; + } + + _MSG(" rule: %s (type: kernel:kprobe, location: ", name); + + print_kernel_probe_location(location); + + MSG(")"); + +end: + return; +} + +static +void print_event_rule_userspace_probe(const struct lttng_event_rule *event_rule) +{ + enum lttng_event_rule_status event_rule_status; + const char *name; + const struct lttng_userspace_probe_location *location; + enum lttng_userspace_probe_location_type userspace_probe_location_type; + + LTTNG_ASSERT(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE); + + event_rule_status = lttng_event_rule_kernel_uprobe_get_event_name( + event_rule, &name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to get uprobe event rule's name."); + goto end; + } + + event_rule_status = lttng_event_rule_kernel_uprobe_get_location( + event_rule, &location); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to get uprobe event rule's location."); + goto end; + } + + _MSG(" rule: %s (type: kernel:uprobe, ", name); + + userspace_probe_location_type = + lttng_userspace_probe_location_get_type(location); + + switch (userspace_probe_location_type) { + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: + { + const char *binary_path, *function_name; + + binary_path = lttng_userspace_probe_location_function_get_binary_path( + location); + function_name = lttng_userspace_probe_location_function_get_function_name( + location); + + _MSG("location type: ELF, location: %s:%s", binary_path, function_name); + break; + } + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: + { + const char *binary_path, *provider_name, *probe_name; + + binary_path = lttng_userspace_probe_location_tracepoint_get_binary_path( + location); + provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name( + location); + probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name( + location); + _MSG("location type: SDT, location: %s:%s:%s", binary_path, provider_name, probe_name); + break; + } + default: + abort(); + } + + MSG(")"); + +end: + return; +} + +static +void print_event_rule_syscall(const struct lttng_event_rule *event_rule) +{ + const char *pattern, *filter; + enum lttng_event_rule_status event_rule_status; + enum lttng_event_rule_kernel_syscall_emission_site emission_site; + + LTTNG_ASSERT(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL); + + emission_site = + lttng_event_rule_kernel_syscall_get_emission_site(event_rule); + + event_rule_status = lttng_event_rule_kernel_syscall_get_name_pattern( + event_rule, &pattern); + LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + + _MSG(" rule: %s (type: kernel:syscall:%s", pattern, + lttng_event_rule_kernel_syscall_emission_site_str( + emission_site)); + + event_rule_status = lttng_event_rule_kernel_syscall_get_filter( + event_rule, &filter); + if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { + _MSG(", filter: %s", filter); + } else { + LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); + } + + MSG(")"); +} + +static +void print_event_rule(const struct lttng_event_rule *event_rule) +{ + const enum lttng_event_rule_type event_rule_type = + lttng_event_rule_get_type(event_rule); + + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: + print_event_rule_user_tracepoint(event_rule); + break; + case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: + print_event_rule_kernel_tracepoint(event_rule); + break; + case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: + case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: + case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: + print_event_rule_logging(event_rule); + break; + case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE: + print_event_rule_kernel_probe(event_rule); + break; + case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE: + print_event_rule_userspace_probe(event_rule); + break; + case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: + print_event_rule_syscall(event_rule); + break; + default: + abort(); + } +} + +static +void print_one_event_expr(const struct lttng_event_expr *event_expr) +{ + enum lttng_event_expr_type type; + + type = lttng_event_expr_get_type(event_expr); + + switch (type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + { + const char *name; + + name = lttng_event_expr_event_payload_field_get_name( + event_expr); + _MSG("%s", name); + + break; + } + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + { + const char *name; + + name = lttng_event_expr_channel_context_field_get_name( + event_expr); + _MSG("$ctx.%s", name); + + break; + } + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + const char *provider_name; + const char *type_name; + + provider_name = lttng_event_expr_app_specific_context_field_get_provider_name( + event_expr); + type_name = lttng_event_expr_app_specific_context_field_get_type_name( + event_expr); + + _MSG("$app.%s:%s", provider_name, type_name); + + break; + } + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + unsigned int index; + const struct lttng_event_expr *parent_expr; + enum lttng_event_expr_status status; + + parent_expr = lttng_event_expr_array_field_element_get_parent_expr( + event_expr); + LTTNG_ASSERT(parent_expr != NULL); + + print_one_event_expr(parent_expr); + + status = lttng_event_expr_array_field_element_get_index( + event_expr, &index); + LTTNG_ASSERT(status == LTTNG_EVENT_EXPR_STATUS_OK); + + _MSG("[%u]", index); + + break; + } + default: + abort(); + } +} + +static +void print_indentation(unsigned int indentation_level) +{ + unsigned int i; + + for (i = 0; i < indentation_level; i++) { + _MSG(INDENTATION_LEVEL_STR); + } +} + +static +void print_error_query_results(struct lttng_error_query_results *results, + unsigned int base_indentation_level) +{ + unsigned int i, count, printed_errors_count = 0; + enum lttng_error_query_results_status results_status; + + results_status = lttng_error_query_results_get_count(results, &count); + LTTNG_ASSERT(results_status == LTTNG_ERROR_QUERY_RESULTS_STATUS_OK); + + LTTNG_ASSERT(results); + + print_indentation(base_indentation_level); + _MSG("errors:"); + + for (i = 0; i < count; i++) { + const struct lttng_error_query_result *result; + enum lttng_error_query_result_status result_status; + const char *result_name; + const char *result_description; + uint64_t result_value; + + results_status = lttng_error_query_results_get_result( + results, &result, i); + LTTNG_ASSERT(results_status == LTTNG_ERROR_QUERY_RESULTS_STATUS_OK); + + result_status = lttng_error_query_result_get_name( + result, &result_name); + LTTNG_ASSERT(result_status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK); + result_status = lttng_error_query_result_get_description( + result, &result_description); + LTTNG_ASSERT(result_status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK); + + + if (lttng_error_query_result_get_type(result) == + LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER) { + result_status = lttng_error_query_result_counter_get_value( + result, &result_value); + LTTNG_ASSERT(result_status == + LTTNG_ERROR_QUERY_RESULT_STATUS_OK); + if (result_value == 0) { + continue; + } + + MSG(""); + print_indentation(base_indentation_level + 1); + + _MSG("%s: %" PRIu64, result_name, result_value); + printed_errors_count++; + } else { + MSG(""); + print_indentation(base_indentation_level + 1); + _MSG("Unknown error query result type for result '%s' (%s)", + result_name, result_description); + continue; + } + } + + if (printed_errors_count == 0) { + _MSG(" none"); + } +} + +static void print_condition_event_rule_matches( + const struct lttng_condition *condition) +{ + const struct lttng_event_rule *event_rule; + enum lttng_condition_status condition_status; + unsigned int cap_desc_count, i; + + condition_status = lttng_condition_event_rule_matches_get_rule( + condition, &event_rule); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); + + print_event_rule(event_rule); + + condition_status = + lttng_condition_event_rule_matches_get_capture_descriptor_count( + condition, &cap_desc_count); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); + + if (cap_desc_count > 0) { + MSG(" captures:"); + + for (i = 0; i < cap_desc_count; i++) { + const struct lttng_event_expr *cap_desc = + lttng_condition_event_rule_matches_get_capture_descriptor_at_index( + condition, i); + + _MSG(" - "); + print_one_event_expr(cap_desc); + MSG(""); + } + } +} + +static void print_action_errors(const struct lttng_trigger *trigger, + const struct lttng_action *action, + const uint64_t *action_path_indexes, + size_t action_path_length) +{ + enum lttng_error_code error_query_ret; + struct lttng_error_query_results *results = NULL; + const char *trigger_name; + uid_t trigger_uid; + enum lttng_trigger_status trigger_status; + struct lttng_error_query *query; + struct lttng_action_path *action_path = lttng_action_path_create( + action_path_indexes, action_path_length); + + LTTNG_ASSERT(action_path); + + query = lttng_error_query_action_create(trigger, action_path); + LTTNG_ASSERT(query); + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + /* + * Anonymous triggers are not listed; this would be an internal error. + */ + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_uid); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + error_query_ret = lttng_error_query_execute( + query, lttng_session_daemon_command_endpoint, &results); + if (error_query_ret != LTTNG_OK) { + ERR("Failed to query errors of trigger '%s' (owner uid: %d): %s", + trigger_name, (int) trigger_uid, + lttng_strerror(-error_query_ret)); + goto end; + } + + print_error_query_results(results, 3); + +end: + MSG(""); + lttng_error_query_destroy(query); + lttng_error_query_results_destroy(results); + lttng_action_path_destroy(action_path); +} + +static +void print_one_action(const struct lttng_trigger *trigger, + const struct lttng_action *action, + const uint64_t *action_path_indexes, + size_t action_path_length) +{ + enum lttng_action_type action_type; + enum lttng_action_status action_status; + const struct lttng_rate_policy *policy = NULL; + const char *value; + + action_type = lttng_action_get_type(action); + LTTNG_ASSERT(action_type != LTTNG_ACTION_TYPE_LIST); + + switch (action_type) { + case LTTNG_ACTION_TYPE_NOTIFY: + _MSG("notify"); + + action_status = lttng_action_notify_get_rate_policy( + action, &policy); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to retrieve rate policy."); + goto end; + } + break; + case LTTNG_ACTION_TYPE_START_SESSION: + action_status = lttng_action_start_session_get_session_name( + action, &value); + LTTNG_ASSERT(action_status == LTTNG_ACTION_STATUS_OK); + _MSG("start session `%s`", value); + + action_status = lttng_action_start_session_get_rate_policy( + action, &policy); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to retrieve rate policy."); + goto end; + } + break; + case LTTNG_ACTION_TYPE_STOP_SESSION: + action_status = lttng_action_stop_session_get_session_name( + action, &value); + LTTNG_ASSERT(action_status == LTTNG_ACTION_STATUS_OK); + _MSG("stop session `%s`", value); + + action_status = lttng_action_stop_session_get_rate_policy( + action, &policy); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to retrieve rate policy."); + goto end; + } + break; + case LTTNG_ACTION_TYPE_ROTATE_SESSION: + action_status = lttng_action_rotate_session_get_session_name( + action, &value); + LTTNG_ASSERT(action_status == LTTNG_ACTION_STATUS_OK); + _MSG("rotate session `%s`", value); + + action_status = lttng_action_rotate_session_get_rate_policy( + action, &policy); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to retrieve rate policy."); + goto end; + } + break; + case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: + { + const struct lttng_snapshot_output *output; + + action_status = lttng_action_snapshot_session_get_session_name( + action, &value); + LTTNG_ASSERT(action_status == LTTNG_ACTION_STATUS_OK); + _MSG("snapshot session `%s`", value); + + action_status = lttng_action_snapshot_session_get_output( + action, &output); + if (action_status == LTTNG_ACTION_STATUS_OK) { + const char *name; + uint64_t max_size; + const char *ctrl_url, *data_url; + bool starts_with_file, starts_with_net, starts_with_net6; + + ctrl_url = lttng_snapshot_output_get_ctrl_url(output); + LTTNG_ASSERT(ctrl_url && strlen(ctrl_url) > 0); + + data_url = lttng_snapshot_output_get_data_url(output); + LTTNG_ASSERT(data_url); + + starts_with_file = strncmp(ctrl_url, "file://", strlen("file://")) == 0; + starts_with_net = strncmp(ctrl_url, "net://", strlen("net://")) == 0; + starts_with_net6 = strncmp(ctrl_url, "net6://", strlen("net6://")) == 0; + + if (ctrl_url[0] == '/' || starts_with_file) { + if (starts_with_file) { + ctrl_url += strlen("file://"); + } + + _MSG(", path: %s", ctrl_url); + } else if (starts_with_net || starts_with_net6) { + _MSG(", url: %s", ctrl_url); + } else { + LTTNG_ASSERT(strlen(data_url) > 0); + + _MSG(", control url: %s, data url: %s", ctrl_url, data_url); + } + + name = lttng_snapshot_output_get_name(output); + LTTNG_ASSERT(name); + if (strlen(name) > 0) { + _MSG(", name: %s", name); + } + + max_size = lttng_snapshot_output_get_maxsize(output); + if (max_size != -1ULL) { + _MSG(", max size: %" PRIu64, max_size); + } + } + + action_status = lttng_action_snapshot_session_get_rate_policy( + action, &policy); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to retrieve rate policy."); + goto end; + } + break; + } + default: + abort(); + } + + if (policy) { + enum lttng_rate_policy_type policy_type; + enum lttng_rate_policy_status policy_status; + uint64_t policy_value = 0; + + policy_type = lttng_rate_policy_get_type(policy); + + switch (policy_type) { + case LTTNG_RATE_POLICY_TYPE_EVERY_N: + policy_status = lttng_rate_policy_every_n_get_interval( + policy, &policy_value); + if (policy_status != LTTNG_RATE_POLICY_STATUS_OK) { + ERR("Failed to get action rate policy interval"); + goto end; + } + if (policy_value > 1) { + /* The default is 1 so print only when it is a + * special case. + */ + _MSG(", rate policy: every %" PRIu64 + " occurrences", + policy_value); + } + break; + case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N: + policy_status = lttng_rate_policy_once_after_n_get_threshold( + policy, &policy_value); + if (policy_status != LTTNG_RATE_POLICY_STATUS_OK) { + ERR("Failed to get action rate policy interval"); + goto end; + } + _MSG(", rate policy: once after %" PRIu64 + " occurrences", + policy_value); + break; + default: + abort(); + } + } + + MSG(""); + print_action_errors(trigger, action, action_path_indexes, + action_path_length); + +end: + return; +} + +static +void print_trigger_errors(const struct lttng_trigger *trigger) +{ + enum lttng_error_code error_query_ret; + struct lttng_error_query_results *results = NULL; + enum lttng_trigger_status trigger_status; + const char *trigger_name; + uid_t trigger_uid; + struct lttng_error_query *query = + lttng_error_query_trigger_create(trigger); + + LTTNG_ASSERT(query); + /* + * Anonymous triggers are not listed; this would be an internal error. + */ + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_uid); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + error_query_ret = lttng_error_query_execute( + query, lttng_session_daemon_command_endpoint, &results); + if (error_query_ret != LTTNG_OK) { + ERR("Failed to query errors of trigger '%s' (owner uid: %d): %s", + trigger_name, (int) trigger_uid, + lttng_strerror(-error_query_ret)); + goto end; + } + + print_error_query_results(results, 1); + +end: + MSG(""); + lttng_error_query_destroy(query); + lttng_error_query_results_destroy(results); +} + +static +void print_condition_errors(const struct lttng_trigger *trigger) +{ + enum lttng_error_code error_query_ret; + struct lttng_error_query_results *results = NULL; + enum lttng_trigger_status trigger_status; + const char *trigger_name; + uid_t trigger_uid; + struct lttng_error_query *query = + lttng_error_query_condition_create(trigger); + + LTTNG_ASSERT(query); + /* + * Anonymous triggers are not listed; this would be an internal error. + */ + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_uid); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + error_query_ret = lttng_error_query_execute( + query, lttng_session_daemon_command_endpoint, &results); + if (error_query_ret != LTTNG_OK) { + ERR("Failed to query errors of condition of trigger '%s' (owner uid: %d): %s", + trigger_name, (int) trigger_uid, + lttng_strerror(-error_query_ret)); + goto end; + } + + print_error_query_results(results, 2); + +end: + MSG(""); + lttng_error_query_destroy(query); + lttng_error_query_results_destroy(results); +} + +static +void print_one_trigger(const struct lttng_trigger *trigger) +{ + const struct lttng_condition *condition; + enum lttng_condition_type condition_type; + const struct lttng_action *action; + enum lttng_action_type action_type; + enum lttng_trigger_status trigger_status; + const char *name; + uid_t trigger_uid; + + /* + * Anonymous triggers are not listed since they can't be specified nor + * referenced through the CLI. + */ + trigger_status = lttng_trigger_get_name(trigger, &name); + if (trigger_status == LTTNG_TRIGGER_STATUS_UNSET) { + goto end; + } + + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_uid); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + MSG("- name: %s", name); + MSG(" owner uid: %d", trigger_uid); + + condition = lttng_trigger_get_const_condition(trigger); + condition_type = lttng_condition_get_type(condition); + MSG(" condition: %s", lttng_condition_type_str(condition_type)); + switch (condition_type) { + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + print_condition_session_consumed_size(condition); + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + print_condition_buffer_usage(condition); + break; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + print_condition_session_rotation(condition); + break; + case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES: + print_condition_event_rule_matches(condition); + break; + default: + abort(); + } + + print_condition_errors(trigger); + + action = lttng_trigger_get_const_action(trigger); + action_type = lttng_action_get_type(action); + if (action_type == LTTNG_ACTION_TYPE_LIST) { + unsigned int count, i; + enum lttng_action_status action_status; + + MSG(" actions:"); + + action_status = lttng_action_list_get_count(action, &count); + LTTNG_ASSERT(action_status == LTTNG_ACTION_STATUS_OK); + + for (i = 0; i < count; i++) { + const uint64_t action_path_index = i; + const struct lttng_action *subaction = + lttng_action_list_get_at_index( + action, i); + + _MSG(" "); + print_one_action(trigger, subaction, &action_path_index, + 1); + } + } else { + _MSG(" action:"); + print_one_action(trigger, action, NULL, 0); + } + + print_trigger_errors(trigger); +end: + return; +} + +static +int compare_triggers_by_name(const void *a, const void *b) +{ + const struct lttng_trigger *trigger_a = *((const struct lttng_trigger **) a); + const struct lttng_trigger *trigger_b = *((const struct lttng_trigger **) b); + const char *name_a, *name_b; + enum lttng_trigger_status trigger_status; + + /* Anonymous triggers are not reachable here. */ + trigger_status = lttng_trigger_get_name(trigger_a, &name_a); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + trigger_status = lttng_trigger_get_name(trigger_b, &name_b); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + return strcmp(name_a, name_b); +} + +static int print_sorted_triggers(const struct lttng_triggers *triggers) +{ + int ret; + int i; + struct lttng_dynamic_pointer_array sorted_triggers; + enum lttng_trigger_status trigger_status; + unsigned int num_triggers; + + lttng_dynamic_pointer_array_init(&sorted_triggers, NULL); + + trigger_status = lttng_triggers_get_count(triggers, &num_triggers); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + ERR("Failed to get trigger count."); + goto error; + } + + for (i = 0; i < num_triggers; i++) { + int add_ret; + const char *unused_name; + const struct lttng_trigger *trigger = + lttng_triggers_get_at_index(triggers, i); + + trigger_status = lttng_trigger_get_name(trigger, &unused_name); + switch (trigger_status) { + case LTTNG_TRIGGER_STATUS_OK: + break; + case LTTNG_TRIGGER_STATUS_UNSET: + /* Don't list anonymous triggers. */ + continue; + default: + abort(); + } + + add_ret = lttng_dynamic_pointer_array_add_pointer( + &sorted_triggers, (void *) trigger); + if (add_ret) { + ERR("Failed to allocate array of struct lttng_trigger *."); + goto error; + } + } + + qsort(sorted_triggers.array.buffer.data, num_triggers, + sizeof(struct lttng_trigger *), + compare_triggers_by_name); + + for (i = 0; i < lttng_dynamic_pointer_array_get_count(&sorted_triggers); + i++) { + const struct lttng_trigger *trigger_to_print = (const struct lttng_trigger + *) + lttng_dynamic_pointer_array_get_pointer( + &sorted_triggers, i); + + print_one_trigger(trigger_to_print); + } + + ret = 0; + goto end; +error: + ret = 1; + +end: + lttng_dynamic_pointer_array_reset(&sorted_triggers); + return ret; +} + +static enum lttng_error_code mi_error_query_trigger_callback( + const struct lttng_trigger *trigger, + struct lttng_error_query_results **results) +{ + enum lttng_error_code ret_code; + struct lttng_error_query *query = + lttng_error_query_trigger_create(trigger); + + LTTNG_ASSERT(results); + LTTNG_ASSERT(query); + + ret_code = lttng_error_query_execute( + query, lttng_session_daemon_command_endpoint, results); + if (ret_code != LTTNG_OK) { + enum lttng_trigger_status trigger_status; + const char *trigger_name; + uid_t trigger_uid; + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + trigger_status = lttng_trigger_get_owner_uid( + trigger, &trigger_uid); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + ERR("Failed to query errors of trigger '%s' (owner uid: %d): %s", + trigger_name, (int) trigger_uid, + lttng_strerror(-ret_code)); + } + + lttng_error_query_destroy(query); + return ret_code; +} + +static enum lttng_error_code mi_error_query_action_callback( + const struct lttng_trigger *trigger, + const struct lttng_action_path *action_path, + struct lttng_error_query_results **results) +{ + enum lttng_error_code ret_code; + struct lttng_error_query *query = + lttng_error_query_action_create(trigger, action_path); + + LTTNG_ASSERT(results); + LTTNG_ASSERT(query); + + ret_code = lttng_error_query_execute( + query, lttng_session_daemon_command_endpoint, results); + if (ret_code != LTTNG_OK) { + enum lttng_trigger_status trigger_status; + const char *trigger_name; + uid_t trigger_uid; + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + trigger_status = lttng_trigger_get_owner_uid( + trigger, &trigger_uid); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + ERR("Failed to query errors of an action for trigger '%s' (owner uid: %d): %s", + trigger_name, (int) trigger_uid, + lttng_strerror(-ret_code)); + } + + lttng_error_query_destroy(query); + return ret_code; +} + +static enum lttng_error_code mi_error_query_condition_callback( + const struct lttng_trigger *trigger, + struct lttng_error_query_results **results) +{ + enum lttng_error_code ret_code; + struct lttng_error_query *query = + lttng_error_query_condition_create(trigger); + + LTTNG_ASSERT(results); + LTTNG_ASSERT(query); + + ret_code = lttng_error_query_execute( + query, lttng_session_daemon_command_endpoint, results); + if (ret_code != LTTNG_OK) { + enum lttng_trigger_status trigger_status; + const char *trigger_name; + uid_t trigger_uid; + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + trigger_status = lttng_trigger_get_owner_uid( + trigger, &trigger_uid); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + ERR("Failed to query errors of of condition for condition of trigger '%s' (owner uid: %d): %s", + trigger_name, (int) trigger_uid, + lttng_strerror(-ret_code)); + } + + lttng_error_query_destroy(query); + return ret_code; +} + +int cmd_list_triggers(int argc, const char **argv) +{ + int ret; + struct argpar_parse_ret argpar_parse_ret = {}; + struct lttng_triggers *triggers = NULL; + int i; + struct mi_writer *mi_writer = NULL; + + argpar_parse_ret = argpar_parse( + argc - 1, argv + 1, list_trigger_options, true); + if (!argpar_parse_ret.items) { + ERR("%s", argpar_parse_ret.error); + goto error; + } + + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + const struct argpar_item *item = + argpar_parse_ret.items->items[i]; + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + const struct argpar_item_opt *item_opt = + (const struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = 0; + goto end; + + case OPT_LIST_OPTIONS: + list_cmd_options_argpar( + stdout, list_trigger_options); + ret = 0; + goto end; + + default: + abort(); + } + + } else { + const struct argpar_item_non_opt *item_non_opt = + (const struct argpar_item_non_opt *) item; + + ERR("Unexpected argument: %s", item_non_opt->arg); + } + } + + ret = lttng_list_triggers(&triggers); + if (ret != LTTNG_OK) { + ERR("Error listing triggers: %s.", lttng_strerror(-ret)); + goto error; + } + + if (lttng_opt_mi) { + mi_writer = mi_lttng_writer_create( + fileno(stdout), lttng_opt_mi); + if (!mi_writer) { + ret = CMD_ERROR; + goto end; + } + + /* Open command element. */ + ret = mi_lttng_writer_command_open(mi_writer, + mi_lttng_element_command_list_trigger); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element. */ + ret = mi_lttng_writer_open_element( + mi_writer, mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + if (lttng_opt_mi) { + const struct mi_lttng_error_query_callbacks callbacks = { + .trigger_cb = mi_error_query_trigger_callback, + .condition_cb = mi_error_query_condition_callback, + .action_cb = mi_error_query_action_callback, + }; + + ret = lttng_triggers_mi_serialize( + triggers, mi_writer, &callbacks); + if (ret != LTTNG_OK) { + ERR("Error printing MI triggers: %s.", + lttng_strerror(-ret)); + goto error; + } + } else { + ret = print_sorted_triggers(triggers); + if (ret) { + ERR("Error printing triggers"); + goto error; + } + } + + /* Mi closing. */ + if (lttng_opt_mi) { + /* Close output element. */ + ret = mi_lttng_writer_close_element(mi_writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close. */ + ret = mi_lttng_writer_command_close(mi_writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + ret = 0; + goto end; + +error: + ret = 1; + +end: + argpar_parse_ret_fini(&argpar_parse_ret); + lttng_triggers_destroy(triggers); + /* Mi clean-up. */ + if (mi_writer && mi_lttng_writer_destroy(mi_writer)) { + /* Preserve original error code. */ + ret = ret ? ret : CMD_ERROR; + } + return ret; +} diff --git a/src/bin/lttng/commands/load.c b/src/bin/lttng/commands/load.c deleted file mode 100644 index cdc390516..000000000 --- a/src/bin/lttng/commands/load.c +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright (C) 2014 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "../command.h" - -static char *the_opt_input_path; -static char *the_opt_override_url; -static char *the_opt_override_session_name; -static int the_opt_force; -static int the_opt_load_all; - -static const char *the_session_name; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_ALL, - OPT_FORCE, - OPT_LIST_OPTIONS, -}; - -static struct mi_writer *the_writer; - -static struct poptOption the_load_opts[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"all", 'a', POPT_ARG_NONE, 0, OPT_ALL, 0, 0}, - {"input-path", 'i', POPT_ARG_STRING, &the_opt_input_path, 0, 0, 0}, - {"force", 'f', POPT_ARG_NONE, 0, OPT_FORCE, 0, 0}, - {"override-url", 0, POPT_ARG_STRING, &the_opt_override_url, 0, 0, 0}, - {"override-name", 0, POPT_ARG_STRING, &the_opt_override_session_name, 0, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {0, 0, 0, 0, 0, 0, 0} -}; - -static int mi_partial_session(const char *session_name) -{ - int ret; - LTTNG_ASSERT(the_writer); - LTTNG_ASSERT(session_name); - - /* Open session element */ - ret = mi_lttng_writer_open_element(the_writer, config_element_session); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_write_element_string(the_writer, config_element_name, - session_name); - if (ret) { - goto end; - } - - /* Closing session element */ - ret = mi_lttng_writer_close_element(the_writer); -end: - return ret; -} - -/* - * Mi print of load command - */ -static int mi_load_print(const char *session_name) -{ - int ret; - LTTNG_ASSERT(the_writer); - - if (the_opt_load_all) { - /* We use a wildcard to represent all sessions */ - session_name = "*"; - } - - /* Print load element */ - ret = mi_lttng_writer_open_element(the_writer, mi_lttng_element_load); - if (ret) { - goto end; - } - - /* Print session element */ - ret = mi_partial_session(session_name); - if (ret) { - goto end; - } - - /* Path element */ - if (the_opt_input_path) { - ret = mi_lttng_writer_write_element_string(the_writer, config_element_path, - the_opt_input_path); - if (ret) { - goto end; - } - } - - /* Print override elements */ - ret = mi_lttng_writer_open_element(the_writer, mi_lttng_element_load_overrides); - if (ret) { - goto end; - } - - /* Session name override element */ - if (the_opt_override_session_name) { - ret = mi_lttng_writer_write_element_string(the_writer, - config_element_name, the_opt_override_session_name); - if (ret) { - goto end; - } - } - - /* Session url override element */ - if (the_opt_override_url) { - ret = mi_lttng_writer_write_element_string(the_writer, - mi_lttng_element_load_override_url, - the_opt_override_url); - if (ret) { - goto end; - } - } - - /* Close override and load element */ - ret = mi_lttng_close_multi_element(the_writer, 2); -end: - return ret; -} - -/* - * The 'load ' first level command - */ -int cmd_load(int argc, const char **argv) -{ - int ret, success; - int opt; - poptContext pc; - struct lttng_load_session_attr *session_attr = NULL; - char *input_path = NULL; - const char *leftover = NULL; - - pc = poptGetContext(NULL, argc, argv, the_load_opts, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - ret = CMD_SUCCESS; - goto end; - case OPT_ALL: - the_opt_load_all = 1; - break; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, the_load_opts); - ret = CMD_SUCCESS; - goto end; - case OPT_FORCE: - the_opt_force = 1; - break; - default: - ret = CMD_UNDEFINED; - goto end; - } - } - - ret = lttng_session_daemon_alive(); - if (!ret) { - ERR("No session daemon is available"); - ret = CMD_ERROR; - goto end; - } - - if (!the_opt_load_all) { - the_session_name = poptGetArg(pc); - if (the_session_name) { - DBG2("Loading session name: %s", the_session_name); - } else { - /* Default to load_all */ - the_opt_load_all = 1; - } - } - - leftover = poptGetArg(pc); - if (leftover) { - ERR("Unknown argument: %s", leftover); - ret = CMD_ERROR; - goto end; - } - - /* Mi check */ - if (lttng_opt_mi) { - the_writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!the_writer) { - ret = CMD_ERROR; - goto end; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open(the_writer, - mi_lttng_element_command_load); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(the_writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - /* Prepare load attributes */ - session_attr = lttng_load_session_attr_create(); - if (!session_attr) { - ERR("Failed to create load session attributes"); - ret = CMD_ERROR; - goto end; - } - - /* - * Set the input url - * lttng_load_session_attr_set_input_url only suppports absolute path. - * Use realpath to resolve any relative path. - * */ - if (the_opt_input_path) { - input_path = realpath(the_opt_input_path, NULL); - if (!input_path) { - PERROR("Invalid input path"); - ret = CMD_ERROR; - goto end; - } - } else { - input_path = NULL; - } - - ret = lttng_load_session_attr_set_input_url(session_attr, - input_path); - if (ret) { - ERR("Invalid input path"); - ret = CMD_ERROR; - goto end; - } - - /* Set the session name. NULL means all sessions should be loaded */ - ret = lttng_load_session_attr_set_session_name(session_attr, - the_session_name); - if (ret) { - ERR("Invalid session name"); - ret = CMD_ERROR; - goto end; - } - - /* Set the overwrite attribute */ - ret = lttng_load_session_attr_set_overwrite(session_attr, the_opt_force); - if (ret) { - ERR("Force argument could not be applied"); - ret = CMD_ERROR; - goto end; - } - - /* Set the overrides attributes if any */ - if (the_opt_override_url) { - ret = lttng_load_session_attr_set_override_url(session_attr, - the_opt_override_url); - if (ret) { - ERR("Url override is invalid"); - goto end; - } - } - - if (the_opt_override_session_name) { - if (the_opt_load_all) { - ERR("Options --all and --override-name cannot be used simultaneously"); - ret = CMD_ERROR; - goto end; - } - ret = lttng_load_session_attr_set_override_session_name(session_attr, - the_opt_override_session_name); - if (ret) { - ERR("Failed to set session name override"); - ret = CMD_ERROR; - goto end; - } - } - - ret = lttng_load_session(session_attr); - if (ret) { - ERR("%s", lttng_strerror(ret)); - success = 0; - ret = CMD_ERROR; - } else { - if (the_opt_load_all) { - MSG("All sessions have been loaded successfully"); - } else if (the_session_name) { - ret = config_init((char *) the_session_name); - if (ret < 0) { - WARN("Could not set %s as the default session", - the_session_name); - } - MSG("Session %s has been loaded successfully", the_session_name); - } else { - MSG("Session has been loaded successfully"); - } - - if (the_opt_override_session_name) { - MSG("Session name overridden with %s", - the_opt_override_session_name); - } - - if (the_opt_override_url) { - MSG("Session output url overridden with %s", the_opt_override_url); - } - success = 1; - ret = CMD_SUCCESS; - } - - /* Mi Printing and closing */ - if (lttng_opt_mi) { - /* Mi print */ - ret = mi_load_print(the_session_name); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Close output element */ - ret = mi_lttng_writer_close_element(the_writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(the_writer, - mi_lttng_element_command_success, success); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(the_writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } -end: - if (the_writer && mi_lttng_writer_destroy(the_writer)) { - ERR("Failed to destroy mi lttng writer"); - } - - lttng_load_session_attr_destroy(session_attr); - free(input_path); - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/load.cpp b/src/bin/lttng/commands/load.cpp new file mode 100644 index 000000000..cdc390516 --- /dev/null +++ b/src/bin/lttng/commands/load.cpp @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2014 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../command.h" + +static char *the_opt_input_path; +static char *the_opt_override_url; +static char *the_opt_override_session_name; +static int the_opt_force; +static int the_opt_load_all; + +static const char *the_session_name; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_ALL, + OPT_FORCE, + OPT_LIST_OPTIONS, +}; + +static struct mi_writer *the_writer; + +static struct poptOption the_load_opts[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"all", 'a', POPT_ARG_NONE, 0, OPT_ALL, 0, 0}, + {"input-path", 'i', POPT_ARG_STRING, &the_opt_input_path, 0, 0, 0}, + {"force", 'f', POPT_ARG_NONE, 0, OPT_FORCE, 0, 0}, + {"override-url", 0, POPT_ARG_STRING, &the_opt_override_url, 0, 0, 0}, + {"override-name", 0, POPT_ARG_STRING, &the_opt_override_session_name, 0, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {0, 0, 0, 0, 0, 0, 0} +}; + +static int mi_partial_session(const char *session_name) +{ + int ret; + LTTNG_ASSERT(the_writer); + LTTNG_ASSERT(session_name); + + /* Open session element */ + ret = mi_lttng_writer_open_element(the_writer, config_element_session); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_string(the_writer, config_element_name, + session_name); + if (ret) { + goto end; + } + + /* Closing session element */ + ret = mi_lttng_writer_close_element(the_writer); +end: + return ret; +} + +/* + * Mi print of load command + */ +static int mi_load_print(const char *session_name) +{ + int ret; + LTTNG_ASSERT(the_writer); + + if (the_opt_load_all) { + /* We use a wildcard to represent all sessions */ + session_name = "*"; + } + + /* Print load element */ + ret = mi_lttng_writer_open_element(the_writer, mi_lttng_element_load); + if (ret) { + goto end; + } + + /* Print session element */ + ret = mi_partial_session(session_name); + if (ret) { + goto end; + } + + /* Path element */ + if (the_opt_input_path) { + ret = mi_lttng_writer_write_element_string(the_writer, config_element_path, + the_opt_input_path); + if (ret) { + goto end; + } + } + + /* Print override elements */ + ret = mi_lttng_writer_open_element(the_writer, mi_lttng_element_load_overrides); + if (ret) { + goto end; + } + + /* Session name override element */ + if (the_opt_override_session_name) { + ret = mi_lttng_writer_write_element_string(the_writer, + config_element_name, the_opt_override_session_name); + if (ret) { + goto end; + } + } + + /* Session url override element */ + if (the_opt_override_url) { + ret = mi_lttng_writer_write_element_string(the_writer, + mi_lttng_element_load_override_url, + the_opt_override_url); + if (ret) { + goto end; + } + } + + /* Close override and load element */ + ret = mi_lttng_close_multi_element(the_writer, 2); +end: + return ret; +} + +/* + * The 'load ' first level command + */ +int cmd_load(int argc, const char **argv) +{ + int ret, success; + int opt; + poptContext pc; + struct lttng_load_session_attr *session_attr = NULL; + char *input_path = NULL; + const char *leftover = NULL; + + pc = poptGetContext(NULL, argc, argv, the_load_opts, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + ret = CMD_SUCCESS; + goto end; + case OPT_ALL: + the_opt_load_all = 1; + break; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, the_load_opts); + ret = CMD_SUCCESS; + goto end; + case OPT_FORCE: + the_opt_force = 1; + break; + default: + ret = CMD_UNDEFINED; + goto end; + } + } + + ret = lttng_session_daemon_alive(); + if (!ret) { + ERR("No session daemon is available"); + ret = CMD_ERROR; + goto end; + } + + if (!the_opt_load_all) { + the_session_name = poptGetArg(pc); + if (the_session_name) { + DBG2("Loading session name: %s", the_session_name); + } else { + /* Default to load_all */ + the_opt_load_all = 1; + } + } + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + ret = CMD_ERROR; + goto end; + } + + /* Mi check */ + if (lttng_opt_mi) { + the_writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!the_writer) { + ret = CMD_ERROR; + goto end; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(the_writer, + mi_lttng_element_command_load); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(the_writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + /* Prepare load attributes */ + session_attr = lttng_load_session_attr_create(); + if (!session_attr) { + ERR("Failed to create load session attributes"); + ret = CMD_ERROR; + goto end; + } + + /* + * Set the input url + * lttng_load_session_attr_set_input_url only suppports absolute path. + * Use realpath to resolve any relative path. + * */ + if (the_opt_input_path) { + input_path = realpath(the_opt_input_path, NULL); + if (!input_path) { + PERROR("Invalid input path"); + ret = CMD_ERROR; + goto end; + } + } else { + input_path = NULL; + } + + ret = lttng_load_session_attr_set_input_url(session_attr, + input_path); + if (ret) { + ERR("Invalid input path"); + ret = CMD_ERROR; + goto end; + } + + /* Set the session name. NULL means all sessions should be loaded */ + ret = lttng_load_session_attr_set_session_name(session_attr, + the_session_name); + if (ret) { + ERR("Invalid session name"); + ret = CMD_ERROR; + goto end; + } + + /* Set the overwrite attribute */ + ret = lttng_load_session_attr_set_overwrite(session_attr, the_opt_force); + if (ret) { + ERR("Force argument could not be applied"); + ret = CMD_ERROR; + goto end; + } + + /* Set the overrides attributes if any */ + if (the_opt_override_url) { + ret = lttng_load_session_attr_set_override_url(session_attr, + the_opt_override_url); + if (ret) { + ERR("Url override is invalid"); + goto end; + } + } + + if (the_opt_override_session_name) { + if (the_opt_load_all) { + ERR("Options --all and --override-name cannot be used simultaneously"); + ret = CMD_ERROR; + goto end; + } + ret = lttng_load_session_attr_set_override_session_name(session_attr, + the_opt_override_session_name); + if (ret) { + ERR("Failed to set session name override"); + ret = CMD_ERROR; + goto end; + } + } + + ret = lttng_load_session(session_attr); + if (ret) { + ERR("%s", lttng_strerror(ret)); + success = 0; + ret = CMD_ERROR; + } else { + if (the_opt_load_all) { + MSG("All sessions have been loaded successfully"); + } else if (the_session_name) { + ret = config_init((char *) the_session_name); + if (ret < 0) { + WARN("Could not set %s as the default session", + the_session_name); + } + MSG("Session %s has been loaded successfully", the_session_name); + } else { + MSG("Session has been loaded successfully"); + } + + if (the_opt_override_session_name) { + MSG("Session name overridden with %s", + the_opt_override_session_name); + } + + if (the_opt_override_url) { + MSG("Session output url overridden with %s", the_opt_override_url); + } + success = 1; + ret = CMD_SUCCESS; + } + + /* Mi Printing and closing */ + if (lttng_opt_mi) { + /* Mi print */ + ret = mi_load_print(the_session_name); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Close output element */ + ret = mi_lttng_writer_close_element(the_writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(the_writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(the_writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } +end: + if (the_writer && mi_lttng_writer_destroy(the_writer)) { + ERR("Failed to destroy mi lttng writer"); + } + + lttng_load_session_attr_destroy(session_attr); + free(input_path); + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/metadata.c b/src/bin/lttng/commands/metadata.c deleted file mode 100644 index 35aea004e..000000000 --- a/src/bin/lttng/commands/metadata.c +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (C) 2015 Julien Desfossez - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include - -#include - -#include "../command.h" - -static char *opt_session_name; -static char *session_name = NULL; - -static int metadata_regenerate(int argc, const char **argv); - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, - OPT_LIST_COMMANDS, -}; - -static struct mi_writer *writer; - -static struct poptOption long_options[] = { - /* { longName, shortName, argInfo, argPtr, value, descrip, argDesc, } */ - { "help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0, }, - { "session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, - { "list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, 0, 0, }, - { "list-commands", 0, POPT_ARG_NONE, NULL, OPT_LIST_COMMANDS}, - { 0, 0, 0, 0, 0, 0, 0, }, -}; - -static struct cmd_struct actions[] = { - { "regenerate", metadata_regenerate }, - { NULL, NULL } /* Array closure */ -}; - -/* - * Count and return the number of arguments in argv. - */ -static int count_arguments(const char **argv) -{ - int i = 0; - - LTTNG_ASSERT(argv); - - while (argv[i] != NULL) { - i++; - } - - return i; -} - -static int metadata_regenerate(int argc, const char **argv) -{ - int ret; - - if (argc > 1) { - ret = CMD_UNDEFINED; - goto end; - } - ret = lttng_regenerate_metadata(session_name); - if (ret == 0) { - MSG("Metadata successfully regenerated for session %s", session_name); - } else { - ERR("%s", lttng_strerror(ret)); - } - -end: - return ret; -} - -static int handle_command(const char **argv) -{ - struct cmd_struct *cmd; - int ret = CMD_SUCCESS, i = 0, argc, command_ret = CMD_SUCCESS; - - if (argv == NULL) { - ERR("No action specified for metadata command."); - ret = CMD_ERROR; - goto end; - } - - argc = count_arguments(argv); - LTTNG_ASSERT(argc >= 1); - - cmd = &actions[i]; - while (cmd->func != NULL) { - /* Find command */ - if (strcmp(argv[0], cmd->name) == 0) { - if (lttng_opt_mi) { - /* Action element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_metadata_action); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Name of the action */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_name, argv[0]); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - command_ret = cmd->func(argc, argv); - if (lttng_opt_mi) { - /* Close output and action element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - goto end; - } - - cmd = &actions[i++]; - } - - ret = CMD_UNDEFINED; - -end: - /* Overwrite ret if an error occurred in cmd->func() */ - ret = command_ret ? command_ret : ret; - return ret; -} - -/* - * Metadata command handling. - */ -int cmd_metadata(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; - static poptContext pc; - - if (argc < 1) { - SHOW_HELP(); - ret = CMD_ERROR; - goto end; - } - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_metadata); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - case OPT_LIST_COMMANDS: - list_commands(actions, stdout); - goto end; - default: - SHOW_HELP(); - ret = CMD_UNDEFINED; - goto end; - } - } - - if (!opt_session_name) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = CMD_ERROR; - goto end; - } - } else { - session_name = opt_session_name; - } - - command_ret = handle_command(poptGetArgs(pc)); - if (command_ret) { - success = 0; - } - - if (lttng_opt_mi) { - /* Close output element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - -end: - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; - } - - if (!opt_session_name) { - free(session_name); - } - - /* Overwrite ret if an error occurred during handle_command() */ - ret = command_ret ? command_ret : ret; - - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/metadata.cpp b/src/bin/lttng/commands/metadata.cpp new file mode 100644 index 000000000..35aea004e --- /dev/null +++ b/src/bin/lttng/commands/metadata.cpp @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2015 Julien Desfossez + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include + +#include + +#include "../command.h" + +static char *opt_session_name; +static char *session_name = NULL; + +static int metadata_regenerate(int argc, const char **argv); + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, + OPT_LIST_COMMANDS, +}; + +static struct mi_writer *writer; + +static struct poptOption long_options[] = { + /* { longName, shortName, argInfo, argPtr, value, descrip, argDesc, } */ + { "help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0, }, + { "session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, + { "list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, 0, 0, }, + { "list-commands", 0, POPT_ARG_NONE, NULL, OPT_LIST_COMMANDS}, + { 0, 0, 0, 0, 0, 0, 0, }, +}; + +static struct cmd_struct actions[] = { + { "regenerate", metadata_regenerate }, + { NULL, NULL } /* Array closure */ +}; + +/* + * Count and return the number of arguments in argv. + */ +static int count_arguments(const char **argv) +{ + int i = 0; + + LTTNG_ASSERT(argv); + + while (argv[i] != NULL) { + i++; + } + + return i; +} + +static int metadata_regenerate(int argc, const char **argv) +{ + int ret; + + if (argc > 1) { + ret = CMD_UNDEFINED; + goto end; + } + ret = lttng_regenerate_metadata(session_name); + if (ret == 0) { + MSG("Metadata successfully regenerated for session %s", session_name); + } else { + ERR("%s", lttng_strerror(ret)); + } + +end: + return ret; +} + +static int handle_command(const char **argv) +{ + struct cmd_struct *cmd; + int ret = CMD_SUCCESS, i = 0, argc, command_ret = CMD_SUCCESS; + + if (argv == NULL) { + ERR("No action specified for metadata command."); + ret = CMD_ERROR; + goto end; + } + + argc = count_arguments(argv); + LTTNG_ASSERT(argc >= 1); + + cmd = &actions[i]; + while (cmd->func != NULL) { + /* Find command */ + if (strcmp(argv[0], cmd->name) == 0) { + if (lttng_opt_mi) { + /* Action element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_metadata_action); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Name of the action */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_name, argv[0]); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + command_ret = cmd->func(argc, argv); + if (lttng_opt_mi) { + /* Close output and action element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + goto end; + } + + cmd = &actions[i++]; + } + + ret = CMD_UNDEFINED; + +end: + /* Overwrite ret if an error occurred in cmd->func() */ + ret = command_ret ? command_ret : ret; + return ret; +} + +/* + * Metadata command handling. + */ +int cmd_metadata(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; + static poptContext pc; + + if (argc < 1) { + SHOW_HELP(); + ret = CMD_ERROR; + goto end; + } + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_metadata); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + case OPT_LIST_COMMANDS: + list_commands(actions, stdout); + goto end; + default: + SHOW_HELP(); + ret = CMD_UNDEFINED; + goto end; + } + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = CMD_ERROR; + goto end; + } + } else { + session_name = opt_session_name; + } + + command_ret = handle_command(poptGetArgs(pc)); + if (command_ret) { + success = 0; + } + + if (lttng_opt_mi) { + /* Close output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + +end: + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; + } + + if (!opt_session_name) { + free(session_name); + } + + /* Overwrite ret if an error occurred during handle_command() */ + ret = command_ret ? command_ret : ret; + + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/regenerate.c b/src/bin/lttng/commands/regenerate.c deleted file mode 100644 index 0e904f8ab..000000000 --- a/src/bin/lttng/commands/regenerate.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (C) 2015 Julien Desfossez - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include - -#include - -#include "../command.h" - -static char *opt_session_name; -static char *session_name = NULL; - -static int regenerate_metadata(int argc, const char **argv); -static int regenerate_statedump(int argc, const char **argv); - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, - OPT_LIST_COMMANDS, -}; - -static struct mi_writer *writer; - -static struct poptOption long_options[] = { - /* { longName, shortName, argInfo, argPtr, value, descrip, argDesc, } */ - { "help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0, }, - { "session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, - { "list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, 0, 0, }, - { "list-commands", 0, POPT_ARG_NONE, NULL, OPT_LIST_COMMANDS}, - { 0, 0, 0, 0, 0, 0, 0, }, -}; - -static struct cmd_struct actions[] = { - { "metadata", regenerate_metadata }, - { "statedump", regenerate_statedump }, - { NULL, NULL } /* Array closure */ -}; - -/* - * Count and return the number of arguments in argv. - */ -static int count_arguments(const char **argv) -{ - int i = 0; - - LTTNG_ASSERT(argv); - - while (argv[i] != NULL) { - i++; - } - - return i; -} - -static int regenerate_metadata(int argc, const char **argv) -{ - int ret; - - if (argc > 1) { - ret = CMD_UNDEFINED; - goto end; - } - ret = lttng_regenerate_metadata(session_name); - if (ret == 0) { - MSG("Metadata successfully regenerated for session %s", session_name); - } else { - ERR("%s", lttng_strerror(ret)); - } - -end: - return ret; -} - -static int regenerate_statedump(int argc, const char **argv) -{ - int ret; - - if (argc > 1) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - ret = lttng_regenerate_statedump(session_name); - if (ret == 0) { - MSG("State dump successfully regenerated for session %s", session_name); - } - -end: - return ret; -} - -static int handle_command(const char **argv) -{ - struct cmd_struct *cmd; - int ret = CMD_SUCCESS, i = 0, argc, command_ret = CMD_SUCCESS; - - if (argv == NULL) { - ERR("No object specified for regenerate command."); - command_ret = CMD_ERROR; - goto end; - } - - argc = count_arguments(argv); - LTTNG_ASSERT(argc >= 1); - - cmd = &actions[i]; - while (cmd->func != NULL) { - /* Find command */ - if (strcmp(argv[0], cmd->name) == 0) { - if (lttng_opt_mi) { - /* Action element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_regenerate_action); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Name of the action */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_name, argv[0]); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - command_ret = cmd->func(argc, argv); - if (lttng_opt_mi) { - /* Close output and action element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - goto end; - } - - cmd = &actions[i++]; - } - - ret = CMD_UNDEFINED; - -end: - /* Overwrite ret if an error occurred in cmd->func() */ - ret = command_ret ? command_ret : ret; - return ret; -} - -/* - * regenerate command handling. - */ -int cmd_regenerate(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; - static poptContext pc; - - if (argc < 1) { - SHOW_HELP(); - ret = CMD_ERROR; - goto end; - } - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_regenerate); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - case OPT_LIST_COMMANDS: - list_commands(actions, stdout); - goto end; - default: - SHOW_HELP(); - ret = CMD_UNDEFINED; - goto end; - } - } - - if (!opt_session_name) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = CMD_ERROR; - goto end; - } - } else { - session_name = opt_session_name; - } - - command_ret = handle_command(poptGetArgs(pc)); - if (command_ret) { - success = 0; - } - - if (lttng_opt_mi) { - /* Close output element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - -end: - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; - } - - if (!opt_session_name) { - free(session_name); - } - - /* Overwrite ret if an error occurred during handle_command() */ - ret = command_ret ? command_ret : ret; - - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/regenerate.cpp b/src/bin/lttng/commands/regenerate.cpp new file mode 100644 index 000000000..0e904f8ab --- /dev/null +++ b/src/bin/lttng/commands/regenerate.cpp @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2015 Julien Desfossez + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include + +#include + +#include "../command.h" + +static char *opt_session_name; +static char *session_name = NULL; + +static int regenerate_metadata(int argc, const char **argv); +static int regenerate_statedump(int argc, const char **argv); + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, + OPT_LIST_COMMANDS, +}; + +static struct mi_writer *writer; + +static struct poptOption long_options[] = { + /* { longName, shortName, argInfo, argPtr, value, descrip, argDesc, } */ + { "help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0, }, + { "session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, + { "list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, 0, 0, }, + { "list-commands", 0, POPT_ARG_NONE, NULL, OPT_LIST_COMMANDS}, + { 0, 0, 0, 0, 0, 0, 0, }, +}; + +static struct cmd_struct actions[] = { + { "metadata", regenerate_metadata }, + { "statedump", regenerate_statedump }, + { NULL, NULL } /* Array closure */ +}; + +/* + * Count and return the number of arguments in argv. + */ +static int count_arguments(const char **argv) +{ + int i = 0; + + LTTNG_ASSERT(argv); + + while (argv[i] != NULL) { + i++; + } + + return i; +} + +static int regenerate_metadata(int argc, const char **argv) +{ + int ret; + + if (argc > 1) { + ret = CMD_UNDEFINED; + goto end; + } + ret = lttng_regenerate_metadata(session_name); + if (ret == 0) { + MSG("Metadata successfully regenerated for session %s", session_name); + } else { + ERR("%s", lttng_strerror(ret)); + } + +end: + return ret; +} + +static int regenerate_statedump(int argc, const char **argv) +{ + int ret; + + if (argc > 1) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + ret = lttng_regenerate_statedump(session_name); + if (ret == 0) { + MSG("State dump successfully regenerated for session %s", session_name); + } + +end: + return ret; +} + +static int handle_command(const char **argv) +{ + struct cmd_struct *cmd; + int ret = CMD_SUCCESS, i = 0, argc, command_ret = CMD_SUCCESS; + + if (argv == NULL) { + ERR("No object specified for regenerate command."); + command_ret = CMD_ERROR; + goto end; + } + + argc = count_arguments(argv); + LTTNG_ASSERT(argc >= 1); + + cmd = &actions[i]; + while (cmd->func != NULL) { + /* Find command */ + if (strcmp(argv[0], cmd->name) == 0) { + if (lttng_opt_mi) { + /* Action element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_regenerate_action); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Name of the action */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_name, argv[0]); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + command_ret = cmd->func(argc, argv); + if (lttng_opt_mi) { + /* Close output and action element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + goto end; + } + + cmd = &actions[i++]; + } + + ret = CMD_UNDEFINED; + +end: + /* Overwrite ret if an error occurred in cmd->func() */ + ret = command_ret ? command_ret : ret; + return ret; +} + +/* + * regenerate command handling. + */ +int cmd_regenerate(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; + static poptContext pc; + + if (argc < 1) { + SHOW_HELP(); + ret = CMD_ERROR; + goto end; + } + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_regenerate); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + case OPT_LIST_COMMANDS: + list_commands(actions, stdout); + goto end; + default: + SHOW_HELP(); + ret = CMD_UNDEFINED; + goto end; + } + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = CMD_ERROR; + goto end; + } + } else { + session_name = opt_session_name; + } + + command_ret = handle_command(poptGetArgs(pc)); + if (command_ret) { + success = 0; + } + + if (lttng_opt_mi) { + /* Close output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + +end: + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; + } + + if (!opt_session_name) { + free(session_name); + } + + /* Overwrite ret if an error occurred during handle_command() */ + ret = command_ret ? command_ret : ret; + + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/remove_trigger.c b/src/bin/lttng/commands/remove_trigger.c deleted file mode 100644 index 2ac237c3a..000000000 --- a/src/bin/lttng/commands/remove_trigger.c +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (C) 2021 Simon Marchi - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include "../command.h" -#include "common/argpar/argpar.h" -#include "common/mi-lttng.h" -#include -#include - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP, - OPT_LIST_OPTIONS, - OPT_OWNER_UID, -}; - -static const -struct argpar_opt_descr remove_trigger_options[] = { - { OPT_HELP, 'h', "help", false }, - { OPT_LIST_OPTIONS, '\0', "list-options", false }, - { OPT_OWNER_UID, '\0', "owner-uid", true }, - ARGPAR_OPT_DESCR_SENTINEL, -}; - -static -bool assign_string(char **dest, const char *src, const char *opt_name) -{ - bool ret; - - if (*dest) { - ERR("Duplicate option '%s' given.", opt_name); - goto error; - } - - *dest = strdup(src); - if (!*dest) { - ERR("Failed to allocate '%s' string.", opt_name); - goto error; - } - - ret = true; - goto end; - -error: - ret = false; - -end: - return ret; -} - -int cmd_remove_trigger(int argc, const char **argv) -{ - enum lttng_error_code ret_code; - int ret; - struct argpar_parse_ret argpar_parse_ret = {}; - const char *name = NULL; - int i; - struct lttng_triggers *triggers = NULL; - unsigned int triggers_count; - enum lttng_trigger_status trigger_status; - const struct lttng_trigger *trigger_to_remove = NULL; - char *owner_uid = NULL; - long long uid; - struct mi_writer *mi_writer = NULL; - - if (lttng_opt_mi) { - mi_writer = mi_lttng_writer_create( - fileno(stdout), lttng_opt_mi); - if (!mi_writer) { - ret = CMD_ERROR; - goto error; - } - - /* Open command element. */ - ret = mi_lttng_writer_command_open(mi_writer, - mi_lttng_element_command_remove_trigger); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - /* Open output element. */ - ret = mi_lttng_writer_open_element( - mi_writer, mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - - argpar_parse_ret = argpar_parse(argc - 1, argv + 1, - remove_trigger_options, true); - if (!argpar_parse_ret.items) { - ERR("%s", argpar_parse_ret.error); - goto error; - } - - for (i = 0; i < argpar_parse_ret.items->n_items; i++) { - const struct argpar_item *item = - argpar_parse_ret.items->items[i]; - - if (item->type == ARGPAR_ITEM_TYPE_OPT) { - const struct argpar_item_opt *item_opt = - (const struct argpar_item_opt *) item; - - switch (item_opt->descr->id) { - case OPT_HELP: - SHOW_HELP(); - ret = 0; - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options_argpar(stdout, - remove_trigger_options); - ret = 0; - goto end; - case OPT_OWNER_UID: - { - if (!assign_string(&owner_uid, item_opt->arg, - "--owner-uid")) { - goto error; - } - break; - } - default: - abort(); - } - } else { - const struct argpar_item_non_opt *item_non_opt = - (const struct argpar_item_non_opt *) item; - - if (name) { - ERR("Unexpected argument '%s'", item_non_opt->arg); - goto error; - } - - name = item_non_opt->arg; - } - } - - if (!name) { - ERR("Missing `name` argument."); - goto error; - } - - if (owner_uid) { - char *end; - - errno = 0; - uid = strtol(owner_uid, &end, 10); - if (end == owner_uid || *end != '\0' || errno != 0) { - ERR("Failed to parse `%s` as an integer.", owner_uid); - } - } else { - uid = geteuid(); - } - - ret = lttng_list_triggers(&triggers); - if (ret != LTTNG_OK) { - ERR("Failed to get the list of triggers."); - goto error; - } - - trigger_status = lttng_triggers_get_count(triggers, &triggers_count); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - for (i = 0; i < triggers_count; i++) { - const struct lttng_trigger *trigger; - const char *trigger_name; - uid_t trigger_uid; - - trigger = lttng_triggers_get_at_index(triggers, i); - trigger_status = lttng_trigger_get_name(trigger, &trigger_name); - switch (trigger_status) { - case LTTNG_TRIGGER_STATUS_OK: - break; - case LTTNG_TRIGGER_STATUS_UNSET: - /* Don't compare against anonymous triggers. */ - continue; - default: - abort(); - } - - trigger_status = lttng_trigger_get_owner_uid( - trigger, &trigger_uid); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - if (trigger_uid == uid && strcmp(trigger_name, name) == 0) { - trigger_to_remove = trigger; - break; - } - } - - if (!trigger_to_remove) { - ERR("Couldn't find trigger with name `%s`.", name); - goto error; - } - - ret = lttng_unregister_trigger(trigger_to_remove); - if (ret != 0) { - ERR("Failed to unregister trigger `%s`.", name); - goto error; - } - - if (lttng_opt_mi) { - ret_code = lttng_trigger_mi_serialize( - trigger_to_remove, mi_writer, NULL); - if (ret_code != LTTNG_OK) { - goto error; - } - } - MSG("Removed trigger `%s`.", name); - - ret = 0; - goto end; - -error: - ret = 1; - -end: - /* Mi closing. */ - if (lttng_opt_mi && mi_writer) { - /* Close output element. */ - int mi_ret = mi_lttng_writer_close_element(mi_writer); - if (mi_ret) { - ret = 1; - goto cleanup; - } - - mi_ret = mi_lttng_writer_write_element_bool(mi_writer, - mi_lttng_element_command_success, ret ? 0 : 1); - if (mi_ret) { - ret = 1; - goto cleanup; - } - - /* Command element close. */ - mi_ret = mi_lttng_writer_command_close(mi_writer); - if (mi_ret) { - ret = 1; - goto cleanup; - } - } - -cleanup: - argpar_parse_ret_fini(&argpar_parse_ret); - lttng_triggers_destroy(triggers); - free(owner_uid); - - if (mi_writer && mi_lttng_writer_destroy(mi_writer)) { - /* Preserve original error code. */ - ret = ret ? ret : CMD_ERROR; - } - return ret; -} diff --git a/src/bin/lttng/commands/remove_trigger.cpp b/src/bin/lttng/commands/remove_trigger.cpp new file mode 100644 index 000000000..2ac237c3a --- /dev/null +++ b/src/bin/lttng/commands/remove_trigger.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2021 Simon Marchi + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "../command.h" +#include "common/argpar/argpar.h" +#include "common/mi-lttng.h" +#include +#include + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP, + OPT_LIST_OPTIONS, + OPT_OWNER_UID, +}; + +static const +struct argpar_opt_descr remove_trigger_options[] = { + { OPT_HELP, 'h', "help", false }, + { OPT_LIST_OPTIONS, '\0', "list-options", false }, + { OPT_OWNER_UID, '\0', "owner-uid", true }, + ARGPAR_OPT_DESCR_SENTINEL, +}; + +static +bool assign_string(char **dest, const char *src, const char *opt_name) +{ + bool ret; + + if (*dest) { + ERR("Duplicate option '%s' given.", opt_name); + goto error; + } + + *dest = strdup(src); + if (!*dest) { + ERR("Failed to allocate '%s' string.", opt_name); + goto error; + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +int cmd_remove_trigger(int argc, const char **argv) +{ + enum lttng_error_code ret_code; + int ret; + struct argpar_parse_ret argpar_parse_ret = {}; + const char *name = NULL; + int i; + struct lttng_triggers *triggers = NULL; + unsigned int triggers_count; + enum lttng_trigger_status trigger_status; + const struct lttng_trigger *trigger_to_remove = NULL; + char *owner_uid = NULL; + long long uid; + struct mi_writer *mi_writer = NULL; + + if (lttng_opt_mi) { + mi_writer = mi_lttng_writer_create( + fileno(stdout), lttng_opt_mi); + if (!mi_writer) { + ret = CMD_ERROR; + goto error; + } + + /* Open command element. */ + ret = mi_lttng_writer_command_open(mi_writer, + mi_lttng_element_command_remove_trigger); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* Open output element. */ + ret = mi_lttng_writer_open_element( + mi_writer, mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + + argpar_parse_ret = argpar_parse(argc - 1, argv + 1, + remove_trigger_options, true); + if (!argpar_parse_ret.items) { + ERR("%s", argpar_parse_ret.error); + goto error; + } + + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + const struct argpar_item *item = + argpar_parse_ret.items->items[i]; + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + const struct argpar_item_opt *item_opt = + (const struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = 0; + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options_argpar(stdout, + remove_trigger_options); + ret = 0; + goto end; + case OPT_OWNER_UID: + { + if (!assign_string(&owner_uid, item_opt->arg, + "--owner-uid")) { + goto error; + } + break; + } + default: + abort(); + } + } else { + const struct argpar_item_non_opt *item_non_opt = + (const struct argpar_item_non_opt *) item; + + if (name) { + ERR("Unexpected argument '%s'", item_non_opt->arg); + goto error; + } + + name = item_non_opt->arg; + } + } + + if (!name) { + ERR("Missing `name` argument."); + goto error; + } + + if (owner_uid) { + char *end; + + errno = 0; + uid = strtol(owner_uid, &end, 10); + if (end == owner_uid || *end != '\0' || errno != 0) { + ERR("Failed to parse `%s` as an integer.", owner_uid); + } + } else { + uid = geteuid(); + } + + ret = lttng_list_triggers(&triggers); + if (ret != LTTNG_OK) { + ERR("Failed to get the list of triggers."); + goto error; + } + + trigger_status = lttng_triggers_get_count(triggers, &triggers_count); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + for (i = 0; i < triggers_count; i++) { + const struct lttng_trigger *trigger; + const char *trigger_name; + uid_t trigger_uid; + + trigger = lttng_triggers_get_at_index(triggers, i); + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + switch (trigger_status) { + case LTTNG_TRIGGER_STATUS_OK: + break; + case LTTNG_TRIGGER_STATUS_UNSET: + /* Don't compare against anonymous triggers. */ + continue; + default: + abort(); + } + + trigger_status = lttng_trigger_get_owner_uid( + trigger, &trigger_uid); + LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + if (trigger_uid == uid && strcmp(trigger_name, name) == 0) { + trigger_to_remove = trigger; + break; + } + } + + if (!trigger_to_remove) { + ERR("Couldn't find trigger with name `%s`.", name); + goto error; + } + + ret = lttng_unregister_trigger(trigger_to_remove); + if (ret != 0) { + ERR("Failed to unregister trigger `%s`.", name); + goto error; + } + + if (lttng_opt_mi) { + ret_code = lttng_trigger_mi_serialize( + trigger_to_remove, mi_writer, NULL); + if (ret_code != LTTNG_OK) { + goto error; + } + } + MSG("Removed trigger `%s`.", name); + + ret = 0; + goto end; + +error: + ret = 1; + +end: + /* Mi closing. */ + if (lttng_opt_mi && mi_writer) { + /* Close output element. */ + int mi_ret = mi_lttng_writer_close_element(mi_writer); + if (mi_ret) { + ret = 1; + goto cleanup; + } + + mi_ret = mi_lttng_writer_write_element_bool(mi_writer, + mi_lttng_element_command_success, ret ? 0 : 1); + if (mi_ret) { + ret = 1; + goto cleanup; + } + + /* Command element close. */ + mi_ret = mi_lttng_writer_command_close(mi_writer); + if (mi_ret) { + ret = 1; + goto cleanup; + } + } + +cleanup: + argpar_parse_ret_fini(&argpar_parse_ret); + lttng_triggers_destroy(triggers); + free(owner_uid); + + if (mi_writer && mi_lttng_writer_destroy(mi_writer)) { + /* Preserve original error code. */ + ret = ret ? ret : CMD_ERROR; + } + return ret; +} diff --git a/src/bin/lttng/commands/rotate.c b/src/bin/lttng/commands/rotate.c deleted file mode 100644 index aa0923cc2..000000000 --- a/src/bin/lttng/commands/rotate.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright (C) 2017 Julien Desfossez - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "../command.h" -#include - -static char *opt_session_name; -static int opt_no_wait; -static struct mi_writer *writer; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {"no-wait", 'n', POPT_ARG_VAL, &opt_no_wait, 1, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -static int rotate_tracing(char *session_name) -{ - int ret; - enum cmd_error_code cmd_ret = CMD_SUCCESS; - struct lttng_rotation_handle *handle = NULL; - enum lttng_rotation_status rotation_status; - enum lttng_rotation_state rotation_state = LTTNG_ROTATION_STATE_ONGOING; - const struct lttng_trace_archive_location *location = NULL; - bool print_location = true; - - DBG("Rotating the output files of session %s", session_name); - - ret = lttng_rotate_session(session_name, NULL, &handle); - if (ret < 0) { - switch (-ret) { - case LTTNG_ERR_SESSION_NOT_STARTED: - WARN("Tracing session %s not started yet", session_name); - cmd_ret = CMD_WARNING; - goto end; - default: - ERR("%s", lttng_strerror(ret)); - goto error; - } - } - - if (opt_no_wait) { - rotation_state = LTTNG_ROTATION_STATE_ONGOING; - goto skip_wait; - } - - _MSG("Waiting for rotation to complete"); - ret = fflush(stdout); - if (ret) { - PERROR("fflush"); - goto error; - } - - do { - rotation_status = lttng_rotation_handle_get_state(handle, - &rotation_state); - if (rotation_status != LTTNG_ROTATION_STATUS_OK) { - MSG(""); - ERR("Failed to query the state of the rotation."); - goto error; - } - - if (rotation_state == LTTNG_ROTATION_STATE_ONGOING) { - ret = usleep(DEFAULT_DATA_AVAILABILITY_WAIT_TIME_US); - if (ret) { - PERROR("\nusleep"); - goto error; - } - _MSG("."); - - ret = fflush(stdout); - if (ret) { - PERROR("\nfflush"); - goto error; - } - } - } while (rotation_state == LTTNG_ROTATION_STATE_ONGOING); - MSG(""); - -skip_wait: - switch (rotation_state) { - case LTTNG_ROTATION_STATE_COMPLETED: - rotation_status = lttng_rotation_handle_get_archive_location( - handle, &location); - if (rotation_status != LTTNG_ROTATION_STATUS_OK) { - ERR("Failed to retrieve the rotation's completed chunk archive location."); - cmd_ret = CMD_ERROR; - } - break; - case LTTNG_ROTATION_STATE_EXPIRED: - break; - case LTTNG_ROTATION_STATE_ERROR: - ERR("Failed to retrieve rotation state."); - goto error; - case LTTNG_ROTATION_STATE_ONGOING: - MSG("Rotation ongoing for session %s", session_name); - print_location = false; - break; - default: - ERR("Unexpected rotation state encountered."); - goto error; - } - - if (!lttng_opt_mi && print_location) { - ret = print_trace_archive_location(location, - session_name); - } else if (lttng_opt_mi) { - ret = mi_lttng_rotate(writer, session_name, rotation_state, - location); - } - - if (ret < 0) { - cmd_ret = CMD_ERROR; - } - -end: - lttng_rotation_handle_destroy(handle); - return cmd_ret; -error: - cmd_ret = CMD_ERROR; - goto end; -} - -/* - * cmd_rotate - * - * The 'rotate ' first level command - */ -int cmd_rotate(int argc, const char **argv) -{ - int opt, ret; - enum cmd_error_code cmd_ret = CMD_SUCCESS; - int popt_ret; - static poptContext pc; - char *session_name = NULL; - bool free_session_name = false; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - popt_ret = poptReadDefaultConfig(pc, 0); - if (popt_ret) { - ERR("poptReadDefaultConfig"); - goto error; - } - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - default: - cmd_ret = CMD_UNDEFINED; - goto end; - } - } - - opt_session_name = (char*) poptGetArg(pc); - - if (!opt_session_name) { - session_name = get_session_name(); - if (!session_name) { - goto error; - } - free_session_name = true; - } else { - session_name = opt_session_name; - } - - /* Mi check */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - goto error; - } - - /* Open rotate command */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_rotate); - if (ret) { - goto error; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - goto error; - } - } - - cmd_ret = rotate_tracing(session_name); - - /* Mi closing */ - if (lttng_opt_mi) { - /* Close output element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto error; - } - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, - cmd_ret == CMD_SUCCESS); - if (ret) { - goto error; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - goto error; - } - } - - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - goto error; - } -end: - poptFreeContext(pc); - if (free_session_name) { - free(session_name); - } - - return cmd_ret; -error: - cmd_ret = CMD_ERROR; - goto end; -} diff --git a/src/bin/lttng/commands/rotate.cpp b/src/bin/lttng/commands/rotate.cpp new file mode 100644 index 000000000..63cf3882a --- /dev/null +++ b/src/bin/lttng/commands/rotate.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2017 Julien Desfossez + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../command.h" +#include + +static char *opt_session_name; +static int opt_no_wait; +static struct mi_writer *writer; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {"no-wait", 'n', POPT_ARG_VAL, &opt_no_wait, 1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +static int rotate_tracing(char *session_name) +{ + int ret; + enum cmd_error_code cmd_ret = CMD_SUCCESS; + struct lttng_rotation_handle *handle = NULL; + enum lttng_rotation_status rotation_status; + enum lttng_rotation_state rotation_state = LTTNG_ROTATION_STATE_ONGOING; + const struct lttng_trace_archive_location *location = NULL; + bool print_location = true; + + DBG("Rotating the output files of session %s", session_name); + + ret = lttng_rotate_session(session_name, NULL, &handle); + if (ret < 0) { + switch (-ret) { + case LTTNG_ERR_SESSION_NOT_STARTED: + WARN("Tracing session %s not started yet", session_name); + cmd_ret = CMD_WARNING; + goto end; + default: + ERR("%s", lttng_strerror(ret)); + goto error; + } + } + + if (opt_no_wait) { + rotation_state = LTTNG_ROTATION_STATE_ONGOING; + goto skip_wait; + } + + _MSG("Waiting for rotation to complete"); + ret = fflush(stdout); + if (ret) { + PERROR("fflush"); + goto error; + } + + do { + rotation_status = lttng_rotation_handle_get_state(handle, + &rotation_state); + if (rotation_status != LTTNG_ROTATION_STATUS_OK) { + MSG(""); + ERR("Failed to query the state of the rotation."); + goto error; + } + + if (rotation_state == LTTNG_ROTATION_STATE_ONGOING) { + ret = usleep(DEFAULT_DATA_AVAILABILITY_WAIT_TIME_US); + if (ret) { + PERROR("\nusleep"); + goto error; + } + _MSG("."); + + ret = fflush(stdout); + if (ret) { + PERROR("\nfflush"); + goto error; + } + } + } while (rotation_state == LTTNG_ROTATION_STATE_ONGOING); + MSG(""); + +skip_wait: + switch (rotation_state) { + case LTTNG_ROTATION_STATE_COMPLETED: + rotation_status = lttng_rotation_handle_get_archive_location( + handle, &location); + if (rotation_status != LTTNG_ROTATION_STATUS_OK) { + ERR("Failed to retrieve the rotation's completed chunk archive location."); + cmd_ret = CMD_ERROR; + } + break; + case LTTNG_ROTATION_STATE_EXPIRED: + break; + case LTTNG_ROTATION_STATE_ERROR: + ERR("Failed to retrieve rotation state."); + goto error; + case LTTNG_ROTATION_STATE_ONGOING: + MSG("Rotation ongoing for session %s", session_name); + print_location = false; + break; + default: + ERR("Unexpected rotation state encountered."); + goto error; + } + + if (!lttng_opt_mi && print_location) { + ret = print_trace_archive_location(location, + session_name); + } else if (lttng_opt_mi) { + ret = mi_lttng_rotate(writer, session_name, rotation_state, + location); + } + + if (ret < 0) { + cmd_ret = CMD_ERROR; + } + +end: + lttng_rotation_handle_destroy(handle); + return cmd_ret; +error: + cmd_ret = CMD_ERROR; + goto end; +} + +/* + * cmd_rotate + * + * The 'rotate ' first level command + */ +int cmd_rotate(int argc, const char **argv) +{ + int opt, ret; + enum cmd_error_code cmd_ret = CMD_SUCCESS; + int popt_ret; + static poptContext pc; + char *session_name = NULL; + bool free_session_name = false; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + popt_ret = poptReadDefaultConfig(pc, 0); + if (popt_ret) { + ERR("poptReadDefaultConfig"); + goto error; + } + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + default: + cmd_ret = CMD_UNDEFINED; + goto end; + } + } + + opt_session_name = (char*) poptGetArg(pc); + + if (!opt_session_name) { + session_name = get_session_name(); + if (!session_name) { + goto error; + } + free_session_name = true; + } else { + session_name = opt_session_name; + } + + /* Mi check */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + goto error; + } + + /* Open rotate command */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_rotate); + if (ret) { + goto error; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + goto error; + } + } + + cmd_ret = (cmd_error_code) rotate_tracing(session_name); + + /* Mi closing */ + if (lttng_opt_mi) { + /* Close output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto error; + } + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, + cmd_ret == CMD_SUCCESS); + if (ret) { + goto error; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + goto error; + } + } + + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + goto error; + } +end: + poptFreeContext(pc); + if (free_session_name) { + free(session_name); + } + + return cmd_ret; +error: + cmd_ret = CMD_ERROR; + goto end; +} diff --git a/src/bin/lttng/commands/save.c b/src/bin/lttng/commands/save.c deleted file mode 100644 index 4231922ad..000000000 --- a/src/bin/lttng/commands/save.c +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (C) 2013 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include - -#include - -#include "../command.h" -#include - -static char *opt_output_path; -static bool opt_force; -static bool opt_save_all; -static struct mi_writer *writer; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_ALL, - OPT_FORCE, - OPT_LIST_OPTIONS, -}; - -static struct poptOption save_opts[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL}, - {"all", 'a', POPT_ARG_NONE, NULL, OPT_ALL, NULL, NULL}, - {"output-path", 'o', POPT_ARG_STRING, &opt_output_path, 0, NULL, NULL}, - {"force", 'f', POPT_ARG_NONE, NULL, OPT_FORCE, NULL, NULL}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {0, 0, 0, 0, 0, 0, 0} -}; - -static int mi_partial_session(const char *session_name) -{ - int ret; - LTTNG_ASSERT(writer); - LTTNG_ASSERT(session_name); - - /* Open session element */ - ret = mi_lttng_writer_open_element(writer, config_element_session); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_write_element_string(writer, config_element_name, - session_name); - if (ret) { - goto end; - } - - /* Closing session element */ - ret = mi_lttng_writer_close_element(writer); -end: - return ret; -} - -/* - * Mi print of save command - */ -static int mi_save_print(const char *session_name) -{ - int ret; - LTTNG_ASSERT(writer); - - if (opt_save_all) { - /* We use a wildcard to represent all sessions */ - session_name = "*"; - } - - /* Print save element */ - ret = mi_lttng_writer_open_element(writer, mi_lttng_element_save); - if (ret) { - goto end; - } - - /* Print session element */ - ret = mi_partial_session(session_name); - if (ret) { - goto end; - } - - /* Path element */ - if (opt_output_path) { - ret = mi_lttng_writer_write_element_string(writer, config_element_path, - opt_output_path); - if (ret) { - goto end; - } - } - - /* Close save element */ - ret = mi_lttng_writer_close_element(writer); -end: - return ret; -} - -/* - * The 'save ' first level command - */ -int cmd_save(int argc, const char **argv) -{ - int ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success; - int opt; - const char *session_name = NULL, *leftover = NULL; - poptContext pc; - struct lttng_save_session_attr *attr; - - pc = poptGetContext(NULL, argc, argv, save_opts, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_ALL: - opt_save_all = true; - break; - case OPT_FORCE: - opt_force = true; - break; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, save_opts); - goto end; - default: - ret = CMD_UNDEFINED; - goto end; - } - } - - if (!opt_save_all) { - session_name = poptGetArg(pc); - if (session_name) { - DBG2("Session name: %s", session_name); - } else { - /* default to opt_save_all */ - opt_save_all = true; - } - } - - leftover = poptGetArg(pc); - if (leftover) { - ERR("Unknown argument: %s", leftover); - ret = CMD_ERROR; - goto end; - } - - attr = lttng_save_session_attr_create(); - if (!attr) { - ret = CMD_FATAL; - goto end_destroy; - } - - if (lttng_save_session_attr_set_session_name(attr, session_name)) { - ret = CMD_ERROR; - goto end_destroy; - } - - if (lttng_save_session_attr_set_overwrite(attr, opt_force)) { - ret = CMD_ERROR; - goto end_destroy; - } - - if (lttng_save_session_attr_set_output_url(attr, opt_output_path)) { - ret = CMD_ERROR; - goto end_destroy; - } - - /* Mi check */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - ret = -LTTNG_ERR_NOMEM; - goto end_destroy; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_save); - if (ret) { - ret = CMD_ERROR; - goto end_destroy; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end_destroy; - } - } - - command_ret = lttng_save_session(attr); - if (command_ret < 0) { - ERR("%s", lttng_strerror(command_ret)); - success = 0; - } else { - /* Inform the user of what just happened on success. */ - if (session_name && opt_output_path) { - MSG("Session %s saved successfully in %s.", session_name, - opt_output_path); - } else if (session_name && !opt_output_path) { - MSG("Session %s saved successfully.", session_name); - } else if (!session_name && opt_output_path) { - MSG("All sessions have been saved successfully in %s.", - opt_output_path); - } else { - MSG("All sessions have been saved successfully."); - } - success = 1; - } - - /* Mi Printing and closing */ - if (lttng_opt_mi) { - /* Mi print */ - ret = mi_save_print(session_name); - if (ret) { - ret = CMD_ERROR; - goto end_destroy; - } - - /* Close output element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto end_destroy; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - ret = CMD_ERROR; - goto end_destroy; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - ret = CMD_ERROR; - goto end_destroy; - } - } -end_destroy: - lttng_save_session_attr_destroy(attr); -end: - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; - } - - /* Overwrite ret if command failed */ - ret = command_ret ? -command_ret : ret; - - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/save.cpp b/src/bin/lttng/commands/save.cpp new file mode 100644 index 000000000..4231922ad --- /dev/null +++ b/src/bin/lttng/commands/save.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2013 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include + +#include + +#include "../command.h" +#include + +static char *opt_output_path; +static bool opt_force; +static bool opt_save_all; +static struct mi_writer *writer; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_ALL, + OPT_FORCE, + OPT_LIST_OPTIONS, +}; + +static struct poptOption save_opts[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL}, + {"all", 'a', POPT_ARG_NONE, NULL, OPT_ALL, NULL, NULL}, + {"output-path", 'o', POPT_ARG_STRING, &opt_output_path, 0, NULL, NULL}, + {"force", 'f', POPT_ARG_NONE, NULL, OPT_FORCE, NULL, NULL}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {0, 0, 0, 0, 0, 0, 0} +}; + +static int mi_partial_session(const char *session_name) +{ + int ret; + LTTNG_ASSERT(writer); + LTTNG_ASSERT(session_name); + + /* Open session element */ + ret = mi_lttng_writer_open_element(writer, config_element_session); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_string(writer, config_element_name, + session_name); + if (ret) { + goto end; + } + + /* Closing session element */ + ret = mi_lttng_writer_close_element(writer); +end: + return ret; +} + +/* + * Mi print of save command + */ +static int mi_save_print(const char *session_name) +{ + int ret; + LTTNG_ASSERT(writer); + + if (opt_save_all) { + /* We use a wildcard to represent all sessions */ + session_name = "*"; + } + + /* Print save element */ + ret = mi_lttng_writer_open_element(writer, mi_lttng_element_save); + if (ret) { + goto end; + } + + /* Print session element */ + ret = mi_partial_session(session_name); + if (ret) { + goto end; + } + + /* Path element */ + if (opt_output_path) { + ret = mi_lttng_writer_write_element_string(writer, config_element_path, + opt_output_path); + if (ret) { + goto end; + } + } + + /* Close save element */ + ret = mi_lttng_writer_close_element(writer); +end: + return ret; +} + +/* + * The 'save ' first level command + */ +int cmd_save(int argc, const char **argv) +{ + int ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success; + int opt; + const char *session_name = NULL, *leftover = NULL; + poptContext pc; + struct lttng_save_session_attr *attr; + + pc = poptGetContext(NULL, argc, argv, save_opts, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_ALL: + opt_save_all = true; + break; + case OPT_FORCE: + opt_force = true; + break; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, save_opts); + goto end; + default: + ret = CMD_UNDEFINED; + goto end; + } + } + + if (!opt_save_all) { + session_name = poptGetArg(pc); + if (session_name) { + DBG2("Session name: %s", session_name); + } else { + /* default to opt_save_all */ + opt_save_all = true; + } + } + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + ret = CMD_ERROR; + goto end; + } + + attr = lttng_save_session_attr_create(); + if (!attr) { + ret = CMD_FATAL; + goto end_destroy; + } + + if (lttng_save_session_attr_set_session_name(attr, session_name)) { + ret = CMD_ERROR; + goto end_destroy; + } + + if (lttng_save_session_attr_set_overwrite(attr, opt_force)) { + ret = CMD_ERROR; + goto end_destroy; + } + + if (lttng_save_session_attr_set_output_url(attr, opt_output_path)) { + ret = CMD_ERROR; + goto end_destroy; + } + + /* Mi check */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end_destroy; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_save); + if (ret) { + ret = CMD_ERROR; + goto end_destroy; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end_destroy; + } + } + + command_ret = lttng_save_session(attr); + if (command_ret < 0) { + ERR("%s", lttng_strerror(command_ret)); + success = 0; + } else { + /* Inform the user of what just happened on success. */ + if (session_name && opt_output_path) { + MSG("Session %s saved successfully in %s.", session_name, + opt_output_path); + } else if (session_name && !opt_output_path) { + MSG("Session %s saved successfully.", session_name); + } else if (!session_name && opt_output_path) { + MSG("All sessions have been saved successfully in %s.", + opt_output_path); + } else { + MSG("All sessions have been saved successfully."); + } + success = 1; + } + + /* Mi Printing and closing */ + if (lttng_opt_mi) { + /* Mi print */ + ret = mi_save_print(session_name); + if (ret) { + ret = CMD_ERROR; + goto end_destroy; + } + + /* Close output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end_destroy; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end_destroy; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + goto end_destroy; + } + } +end_destroy: + lttng_save_session_attr_destroy(attr); +end: + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; + } + + /* Overwrite ret if command failed */ + ret = command_ret ? -command_ret : ret; + + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/set_session.c b/src/bin/lttng/commands/set_session.c deleted file mode 100644 index fb42d14d5..000000000 --- a/src/bin/lttng/commands/set_session.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "../command.h" - -static char *opt_session_name; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, -}; - -static struct mi_writer *writer; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * Print the necessary mi for a session and name. - */ -static int mi_print(char *session_name) -{ - int ret; - - LTTNG_ASSERT(writer); - LTTNG_ASSERT(session_name); - - /* - * Open a sessions element - * This is purely for validation purpose - */ - ret = mi_lttng_sessions_open(writer); - if (ret) { - goto end; - } - - /* Open a session element */ - ret = mi_lttng_writer_open_element(writer, config_element_session); - if (ret) { - goto end; - } - - /* Session name */ - ret = mi_lttng_writer_write_element_string(writer , config_element_name, - session_name); - if (ret) { - goto end; - } - - /* Close session and sessions element */ - ret = mi_lttng_close_multi_element(writer, 2); - if (ret) { - goto end; - } -end: - return ret; -} - -/* - * set_session - */ -static int set_session(void) -{ - int ret = CMD_SUCCESS; - int count, i; - unsigned int session_found = 0; - struct lttng_session *sessions; - - if (opt_session_name && strlen(opt_session_name) > NAME_MAX) { - ERR("Session name too long. Length must be lower or equal to %d", - NAME_MAX); - ret = CMD_ERROR; - goto end; - } - - count = lttng_list_sessions(&sessions); - if (count < 0) { - ret = CMD_ERROR; - ERR("%s", lttng_strerror(count)); - goto end; - } - - for (i = 0; i < count; i++) { - if (strncmp(sessions[i].name, opt_session_name, NAME_MAX) == 0) { - session_found = 1; - break; - } - } - - if (!session_found) { - ERR("Session '%s' not found", opt_session_name); - ret = CMD_ERROR; - goto error; - } - - ret = config_init(opt_session_name); - if (ret < 0) { - ERR("Unable to set session name"); - ret = CMD_ERROR; - goto error; - } - - MSG("Session set to %s", opt_session_name); - if (lttng_opt_mi) { - ret = mi_print(opt_session_name); - if (ret) { - ret = CMD_ERROR; - goto error; - } - } - - ret = CMD_SUCCESS; - -error: - free(sessions); -end: - return ret; -} - -/* - * cmd_set_session - */ -int cmd_set_session(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; - static poptContext pc; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - default: - ret = CMD_UNDEFINED; - goto end; - } - } - - opt_session_name = (char *) poptGetArg(pc); - if (opt_session_name == NULL) { - ERR("Missing session name"); - ret = CMD_ERROR; - goto end; - } - - /* Mi check */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_set_session); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - command_ret = set_session(); - if (command_ret) { - success = 0; - } - - /* Mi closing */ - if (lttng_opt_mi) { - /* Close output element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - -end: - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - ret = ret ? ret : LTTNG_ERR_MI_IO_FAIL; - } - - /* Overwrite ret if an error occurred during set_session() */ - ret = command_ret ? command_ret : ret; - - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/set_session.cpp b/src/bin/lttng/commands/set_session.cpp new file mode 100644 index 000000000..fb42d14d5 --- /dev/null +++ b/src/bin/lttng/commands/set_session.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2011 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../command.h" + +static char *opt_session_name; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, +}; + +static struct mi_writer *writer; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * Print the necessary mi for a session and name. + */ +static int mi_print(char *session_name) +{ + int ret; + + LTTNG_ASSERT(writer); + LTTNG_ASSERT(session_name); + + /* + * Open a sessions element + * This is purely for validation purpose + */ + ret = mi_lttng_sessions_open(writer); + if (ret) { + goto end; + } + + /* Open a session element */ + ret = mi_lttng_writer_open_element(writer, config_element_session); + if (ret) { + goto end; + } + + /* Session name */ + ret = mi_lttng_writer_write_element_string(writer , config_element_name, + session_name); + if (ret) { + goto end; + } + + /* Close session and sessions element */ + ret = mi_lttng_close_multi_element(writer, 2); + if (ret) { + goto end; + } +end: + return ret; +} + +/* + * set_session + */ +static int set_session(void) +{ + int ret = CMD_SUCCESS; + int count, i; + unsigned int session_found = 0; + struct lttng_session *sessions; + + if (opt_session_name && strlen(opt_session_name) > NAME_MAX) { + ERR("Session name too long. Length must be lower or equal to %d", + NAME_MAX); + ret = CMD_ERROR; + goto end; + } + + count = lttng_list_sessions(&sessions); + if (count < 0) { + ret = CMD_ERROR; + ERR("%s", lttng_strerror(count)); + goto end; + } + + for (i = 0; i < count; i++) { + if (strncmp(sessions[i].name, opt_session_name, NAME_MAX) == 0) { + session_found = 1; + break; + } + } + + if (!session_found) { + ERR("Session '%s' not found", opt_session_name); + ret = CMD_ERROR; + goto error; + } + + ret = config_init(opt_session_name); + if (ret < 0) { + ERR("Unable to set session name"); + ret = CMD_ERROR; + goto error; + } + + MSG("Session set to %s", opt_session_name); + if (lttng_opt_mi) { + ret = mi_print(opt_session_name); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + + ret = CMD_SUCCESS; + +error: + free(sessions); +end: + return ret; +} + +/* + * cmd_set_session + */ +int cmd_set_session(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; + static poptContext pc; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + default: + ret = CMD_UNDEFINED; + goto end; + } + } + + opt_session_name = (char *) poptGetArg(pc); + if (opt_session_name == NULL) { + ERR("Missing session name"); + ret = CMD_ERROR; + goto end; + } + + /* Mi check */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_set_session); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + command_ret = set_session(); + if (command_ret) { + success = 0; + } + + /* Mi closing */ + if (lttng_opt_mi) { + /* Close output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + +end: + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : LTTNG_ERR_MI_IO_FAIL; + } + + /* Overwrite ret if an error occurred during set_session() */ + ret = command_ret ? command_ret : ret; + + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/snapshot.c b/src/bin/lttng/commands/snapshot.c deleted file mode 100644 index c37e09a7d..000000000 --- a/src/bin/lttng/commands/snapshot.c +++ /dev/null @@ -1,694 +0,0 @@ -/* - * Copyright (C) 2013 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "../command.h" - -static const char *opt_session_name; -static const char *opt_output_name; -static const char *opt_data_url; -static const char *opt_ctrl_url; -static const char *current_session_name; -static uint64_t opt_max_size; - -/* Stub for the cmd struct actions. */ -static int cmd_add_output(int argc, const char **argv); -static int cmd_del_output(int argc, const char **argv); -static int cmd_list_output(int argc, const char **argv); -static int cmd_record(int argc, const char **argv); - -static const char *indent4 = " "; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, - OPT_MAX_SIZE, - OPT_LIST_COMMANDS, -}; - -static struct mi_writer *writer; - -static struct poptOption snapshot_opts[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, - {"ctrl-url", 'C', POPT_ARG_STRING, &opt_ctrl_url, 0, 0, 0}, - {"data-url", 'D', POPT_ARG_STRING, &opt_data_url, 0, 0, 0}, - {"name", 'n', POPT_ARG_STRING, &opt_output_name, 0, 0, 0}, - {"max-size", 'm', POPT_ARG_STRING, 0, OPT_MAX_SIZE, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {"list-commands", 0, POPT_ARG_NONE, NULL, OPT_LIST_COMMANDS}, - {0, 0, 0, 0, 0, 0, 0} -}; - -static struct cmd_struct actions[] = { - { "add-output", cmd_add_output }, - { "del-output", cmd_del_output }, - { "list-output", cmd_list_output }, - { "record", cmd_record }, - { NULL, NULL } /* Array closure */ -}; - -/* - * Count and return the number of arguments in argv. - */ -static int count_arguments(const char **argv) -{ - int i = 0; - - LTTNG_ASSERT(argv); - - while (argv[i] != NULL) { - i++; - } - - return i; -} - -/* - * Create a snapshot output object from arguments using the given URL. - * - * Return a newly allocated object or NULL on error. - */ -static struct lttng_snapshot_output *create_output_from_args(const char *url) -{ - int ret = 0; - struct lttng_snapshot_output *output = NULL; - - output = lttng_snapshot_output_create(); - if (!output) { - goto error_create; - } - - if (url) { - ret = lttng_snapshot_output_set_ctrl_url(url, output); - if (ret < 0) { - goto error; - } - } else if (opt_ctrl_url) { - ret = lttng_snapshot_output_set_ctrl_url(opt_ctrl_url, output); - if (ret < 0) { - goto error; - } - } - - if (opt_data_url) { - ret = lttng_snapshot_output_set_data_url(opt_data_url, output); - if (ret < 0) { - goto error; - } - } - - if (opt_max_size) { - ret = lttng_snapshot_output_set_size(opt_max_size, output); - if (ret < 0) { - goto error; - } - } - - if (opt_output_name) { - ret = lttng_snapshot_output_set_name(opt_output_name, output); - if (ret < 0) { - goto error; - } - } - - return output; - -error: - lttng_snapshot_output_destroy(output); -error_create: - return NULL; -} - -static int list_output(void) -{ - int ret, output_seen = 0; - struct lttng_snapshot_output *s_iter; - struct lttng_snapshot_output_list *list; - - ret = lttng_snapshot_list_output(current_session_name, &list); - if (ret < 0) { - goto error; - } - - MSG("Snapshot output list for session %s", current_session_name); - - if (lttng_opt_mi) { - ret = mi_lttng_snapshot_output_session_name(writer, - current_session_name); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - while ((s_iter = lttng_snapshot_output_list_get_next(list)) != NULL) { - if (lttng_snapshot_output_get_maxsize(s_iter)) { - MSG("%s[%" PRIu32 "] %s: %s (max size: %" PRIu64 " bytes)", indent4, - lttng_snapshot_output_get_id(s_iter), - lttng_snapshot_output_get_name(s_iter), - lttng_snapshot_output_get_ctrl_url(s_iter), - lttng_snapshot_output_get_maxsize(s_iter)); - } else { - MSG("%s[%" PRIu32 "] %s: %s", indent4, - lttng_snapshot_output_get_id(s_iter), - lttng_snapshot_output_get_name(s_iter), - lttng_snapshot_output_get_ctrl_url(s_iter)); - } - output_seen = 1; - if (lttng_opt_mi) { - ret = mi_lttng_snapshot_list_output(writer, s_iter); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - } - - if (lttng_opt_mi) { - /* Close snapshot snapshots element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Close snapshot session element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - } - } -end: - lttng_snapshot_output_list_destroy(list); - - if (!output_seen) { - MSG("%sNone", indent4); - } - -error: - return ret; -} - -/* - * Delete output by ID. - */ -static int del_output(uint32_t id, const char *name) -{ - int ret; - struct lttng_snapshot_output *output = NULL; - - output = lttng_snapshot_output_create(); - if (!output) { - ret = CMD_FATAL; - goto error; - } - - if (name) { - ret = lttng_snapshot_output_set_name(name, output); - } else if (id != UINT32_MAX) { - ret = lttng_snapshot_output_set_id(id, output); - } else { - ret = CMD_ERROR; - goto error; - } - if (ret < 0) { - ret = CMD_FATAL; - goto error; - } - - ret = lttng_snapshot_del_output(current_session_name, output); - if (ret < 0) { - goto error; - } - - if (id != UINT32_MAX) { - MSG("Snapshot output id %" PRIu32 " successfully deleted for session %s", - id, current_session_name); - } else { - MSG("Snapshot output %s successfully deleted for session %s", - name, current_session_name); - } - - if (lttng_opt_mi) { - ret = mi_lttng_snapshot_del_output(writer, id, name, - current_session_name); - if (ret) { - ret = CMD_ERROR; - } - } - -error: - lttng_snapshot_output_destroy(output); - return ret; -} - -/* - * Add output from the user URL. - */ -static int add_output(const char *url) -{ - int ret; - struct lttng_snapshot_output *output = NULL; - char name[NAME_MAX]; - const char *n_ptr; - - if (!url && (!opt_data_url || !opt_ctrl_url)) { - ret = CMD_ERROR; - goto error; - } - - output = create_output_from_args(url); - if (!output) { - ret = CMD_FATAL; - goto error; - } - - /* This call, if successful, populates the id of the output object. */ - ret = lttng_snapshot_add_output(current_session_name, output); - if (ret < 0) { - goto error; - } - - n_ptr = lttng_snapshot_output_get_name(output); - if (*n_ptr == '\0') { - int pret; - pret = snprintf(name, sizeof(name), DEFAULT_SNAPSHOT_NAME "-%" PRIu32, - lttng_snapshot_output_get_id(output)); - if (pret < 0) { - PERROR("snprintf add output name"); - } - n_ptr = name; - } - - MSG("Snapshot output successfully added for session %s", - current_session_name); - if (opt_max_size) { - MSG(" [%" PRIu32 "] %s: %s (max size: %" PRIu64 " bytes)", - lttng_snapshot_output_get_id(output), n_ptr, - lttng_snapshot_output_get_ctrl_url(output), - lttng_snapshot_output_get_maxsize(output)); - } else { - MSG(" [%" PRIu32 "] %s: %s", - lttng_snapshot_output_get_id(output), n_ptr, - lttng_snapshot_output_get_ctrl_url(output)); - } - if (lttng_opt_mi) { - ret = mi_lttng_snapshot_add_output(writer, current_session_name, - n_ptr, output); - if (ret) { - ret = CMD_ERROR; - } - } -error: - lttng_snapshot_output_destroy(output); - return ret; -} - -static int cmd_add_output(int argc, const char **argv) -{ - int ret; - - if (argc < 2 && (!opt_data_url || !opt_ctrl_url)) { - ERR("An output destination must be specified to add a snapshot output."); - ret = CMD_ERROR; - goto end; - } - - ret = add_output(argv[1]); - if (ret < 0) { - switch (-ret) { - case LTTNG_ERR_SNAPSHOT_UNSUPPORTED: - ERR("Session \"%s\" contains a channel that is incompatible with the snapshot functionality.\nMake sure all channels are configured in 'mmap' output mode.", - current_session_name); - ret = CMD_ERROR; - break; - default: - break; - } - } - -end: - return ret; -} - -static int cmd_del_output(int argc, const char **argv) -{ - int ret; - char *name; - long id; - - if (argc < 2) { - ERR("A snapshot output name or id must be provided to delete a snapshot output."); - ret = CMD_ERROR; - goto end; - } - - errno = 0; - id = strtol(argv[1], &name, 10); - if (id == 0 && (errno == 0 || errno == EINVAL)) { - ret = del_output(UINT32_MAX, name); - } else if (errno == 0 && *name == '\0') { - ret = del_output(id, NULL); - } else { - ERR("Argument %s not recognized", argv[1]); - ret = -1; - goto end; - } - -end: - return ret; -} - -static int cmd_list_output(int argc, const char **argv) -{ - int ret; - - ret = list_output(); - - return ret; -} - -/* - * Do a snapshot record with the URL if one is given. - */ -static int record(const char *url) -{ - int ret; - struct lttng_snapshot_output *output = NULL; - - output = create_output_from_args(url); - if (!output) { - ret = CMD_FATAL; - goto error; - } - - ret = lttng_snapshot_record(current_session_name, output, 0); - if (ret < 0) { - if (ret == -LTTNG_ERR_MAX_SIZE_INVALID) { - ERR("Invalid snapshot size. Cannot fit at least one packet per stream."); - } - goto error; - } - - MSG("Snapshot recorded successfully for session %s", current_session_name); - - if (url) { - MSG("Snapshot written at: %s", url); - } else if (opt_ctrl_url) { - MSG("Snapshot written to ctrl: %s, data: %s", opt_ctrl_url, - opt_data_url); - } - - if (lttng_opt_mi) { - ret = mi_lttng_snapshot_record(writer, current_session_name, url, - opt_ctrl_url, opt_data_url); - if (ret) { - ret = CMD_ERROR; - } - } - -error: - lttng_snapshot_output_destroy(output); - return ret; -} - -static int cmd_record(int argc, const char **argv) -{ - int ret; - - if (argc == 2) { - ret = record(argv[1]); - } else { - ret = record(NULL); - } - - return ret; -} - -static enum cmd_error_code handle_command(const char **argv) -{ - int mi_ret, i = 0, argc; - enum cmd_error_code cmd_ret; - struct cmd_struct *cmd; - - if (!argv) { - ERR("No action specified for snapshot command."); - cmd_ret = CMD_ERROR; - goto end; - } - - if ((!opt_ctrl_url && opt_data_url) || - (opt_ctrl_url && !opt_data_url)) { - ERR("URLs must be specified for both data and control"); - cmd_ret = CMD_ERROR; - goto end; - } - - argc = count_arguments(argv); - /* popt should have passed NULL if no arguments are present. */ - LTTNG_ASSERT(argc > 0); - - cmd = &actions[i]; - while (cmd->func != NULL) { - /* Find command */ - if (strcmp(argv[0], cmd->name) == 0) { - int result; - - if (lttng_opt_mi) { - /* Action element */ - mi_ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_action); - if (mi_ret) { - cmd_ret = CMD_ERROR; - goto end; - } - - /* Name of the action */ - mi_ret = mi_lttng_writer_write_element_string(writer, - config_element_name, argv[0]); - if (mi_ret) { - cmd_ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - mi_ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (mi_ret) { - cmd_ret = CMD_ERROR; - goto end; - } - } - - result = cmd->func(argc, argv); - if (result) { - switch (result) { - case CMD_ERROR: - case CMD_UNDEFINED: - case CMD_FATAL: - case CMD_WARNING: - case CMD_UNSUPPORTED: - /* - * Sub-commands mix lttng_error_codes - * and cmd_error_codes. This should be - * cleaned-up, but in the meantime this - * hack works since the values of the - * two enums do not intersect. - */ - cmd_ret = result; - break; - case -LTTNG_ERR_SNAPSHOT_NODATA: - WARN("%s", lttng_strerror(result)); - - /* A warning is fine since the user has no control on - * whether or not applications (or the kernel) have - * produced any event between the start of the tracing - * session and the recording of the snapshot. MI wise - * the command is not a success since nothing was - * recorded. - */ - cmd_ret = CMD_SUCCESS; - break; - default: - ERR("%s", lttng_strerror(result)); - cmd_ret = CMD_ERROR; - break; - } - } else { - cmd_ret = CMD_SUCCESS; - } - - - if (lttng_opt_mi) { - /* Close output and action element */ - mi_ret = mi_lttng_close_multi_element(writer, 2); - if (mi_ret) { - cmd_ret = CMD_ERROR; - goto end; - } - } - goto end; - } - i++; - cmd = &actions[i]; - } - - cmd_ret = CMD_UNDEFINED; - -end: - return cmd_ret; -} -/* - * The 'snapshot ' first level command - */ -int cmd_snapshot(int argc, const char **argv) -{ - int opt; - int mi_ret; - enum cmd_error_code cmd_ret = CMD_SUCCESS; - char *session_name = NULL; - static poptContext pc; - - pc = poptGetContext(NULL, argc, argv, snapshot_opts, 0); - poptReadDefaultConfig(pc, 0); - - /* Mi check */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - cmd_ret = CMD_ERROR; - goto end; - } - - /* Open command element */ - mi_ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_snapshot); - if (mi_ret) { - cmd_ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - mi_ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (mi_ret) { - cmd_ret = CMD_ERROR; - goto end; - } - } - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - { - int ret; - - /* SHOW_HELP assigns to ret. */ - SHOW_HELP(); - cmd_ret = ret; - goto end; - } - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, snapshot_opts); - goto end; - case OPT_LIST_COMMANDS: - list_commands(actions, stdout); - goto end; - case OPT_MAX_SIZE: - { - uint64_t val; - const char *max_size_arg = poptGetOptArg(pc); - - if (utils_parse_size_suffix((char *) max_size_arg, &val) < 0) { - ERR("Unable to handle max-size value %s", - max_size_arg); - cmd_ret = CMD_ERROR; - goto end; - } - - opt_max_size = val; - - break; - } - default: - cmd_ret = CMD_UNDEFINED; - goto end; - } - } - - if (!opt_session_name) { - session_name = get_session_name(); - if (session_name == NULL) { - cmd_ret = CMD_ERROR; - goto end; - } - current_session_name = session_name; - } else { - current_session_name = opt_session_name; - } - - cmd_ret = handle_command(poptGetArgs(pc)); - - if (lttng_opt_mi) { - /* Close output element */ - mi_ret = mi_lttng_writer_close_element(writer); - if (mi_ret) { - cmd_ret = CMD_ERROR; - goto end; - } - - /* Success ? */ - mi_ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, - cmd_ret == CMD_SUCCESS); - if (mi_ret) { - cmd_ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - mi_ret = mi_lttng_writer_command_close(writer); - if (mi_ret) { - cmd_ret = CMD_ERROR; - goto end; - } - } - -end: - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - cmd_ret = CMD_ERROR; - } - - if (!opt_session_name) { - free(session_name); - } - - poptFreeContext(pc); - return cmd_ret; -} diff --git a/src/bin/lttng/commands/snapshot.cpp b/src/bin/lttng/commands/snapshot.cpp new file mode 100644 index 000000000..8e19fa3d8 --- /dev/null +++ b/src/bin/lttng/commands/snapshot.cpp @@ -0,0 +1,694 @@ +/* + * Copyright (C) 2013 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../command.h" + +static const char *opt_session_name; +static const char *opt_output_name; +static const char *opt_data_url; +static const char *opt_ctrl_url; +static const char *current_session_name; +static uint64_t opt_max_size; + +/* Stub for the cmd struct actions. */ +static int cmd_add_output(int argc, const char **argv); +static int cmd_del_output(int argc, const char **argv); +static int cmd_list_output(int argc, const char **argv); +static int cmd_record(int argc, const char **argv); + +static const char *indent4 = " "; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, + OPT_MAX_SIZE, + OPT_LIST_COMMANDS, +}; + +static struct mi_writer *writer; + +static struct poptOption snapshot_opts[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, + {"ctrl-url", 'C', POPT_ARG_STRING, &opt_ctrl_url, 0, 0, 0}, + {"data-url", 'D', POPT_ARG_STRING, &opt_data_url, 0, 0, 0}, + {"name", 'n', POPT_ARG_STRING, &opt_output_name, 0, 0, 0}, + {"max-size", 'm', POPT_ARG_STRING, 0, OPT_MAX_SIZE, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {"list-commands", 0, POPT_ARG_NONE, NULL, OPT_LIST_COMMANDS}, + {0, 0, 0, 0, 0, 0, 0} +}; + +static struct cmd_struct actions[] = { + { "add-output", cmd_add_output }, + { "del-output", cmd_del_output }, + { "list-output", cmd_list_output }, + { "record", cmd_record }, + { NULL, NULL } /* Array closure */ +}; + +/* + * Count and return the number of arguments in argv. + */ +static int count_arguments(const char **argv) +{ + int i = 0; + + LTTNG_ASSERT(argv); + + while (argv[i] != NULL) { + i++; + } + + return i; +} + +/* + * Create a snapshot output object from arguments using the given URL. + * + * Return a newly allocated object or NULL on error. + */ +static struct lttng_snapshot_output *create_output_from_args(const char *url) +{ + int ret = 0; + struct lttng_snapshot_output *output = NULL; + + output = lttng_snapshot_output_create(); + if (!output) { + goto error_create; + } + + if (url) { + ret = lttng_snapshot_output_set_ctrl_url(url, output); + if (ret < 0) { + goto error; + } + } else if (opt_ctrl_url) { + ret = lttng_snapshot_output_set_ctrl_url(opt_ctrl_url, output); + if (ret < 0) { + goto error; + } + } + + if (opt_data_url) { + ret = lttng_snapshot_output_set_data_url(opt_data_url, output); + if (ret < 0) { + goto error; + } + } + + if (opt_max_size) { + ret = lttng_snapshot_output_set_size(opt_max_size, output); + if (ret < 0) { + goto error; + } + } + + if (opt_output_name) { + ret = lttng_snapshot_output_set_name(opt_output_name, output); + if (ret < 0) { + goto error; + } + } + + return output; + +error: + lttng_snapshot_output_destroy(output); +error_create: + return NULL; +} + +static int list_output(void) +{ + int ret, output_seen = 0; + struct lttng_snapshot_output *s_iter; + struct lttng_snapshot_output_list *list; + + ret = lttng_snapshot_list_output(current_session_name, &list); + if (ret < 0) { + goto error; + } + + MSG("Snapshot output list for session %s", current_session_name); + + if (lttng_opt_mi) { + ret = mi_lttng_snapshot_output_session_name(writer, + current_session_name); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + while ((s_iter = lttng_snapshot_output_list_get_next(list)) != NULL) { + if (lttng_snapshot_output_get_maxsize(s_iter)) { + MSG("%s[%" PRIu32 "] %s: %s (max size: %" PRIu64 " bytes)", indent4, + lttng_snapshot_output_get_id(s_iter), + lttng_snapshot_output_get_name(s_iter), + lttng_snapshot_output_get_ctrl_url(s_iter), + lttng_snapshot_output_get_maxsize(s_iter)); + } else { + MSG("%s[%" PRIu32 "] %s: %s", indent4, + lttng_snapshot_output_get_id(s_iter), + lttng_snapshot_output_get_name(s_iter), + lttng_snapshot_output_get_ctrl_url(s_iter)); + } + output_seen = 1; + if (lttng_opt_mi) { + ret = mi_lttng_snapshot_list_output(writer, s_iter); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + } + + if (lttng_opt_mi) { + /* Close snapshot snapshots element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Close snapshot session element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + } + } +end: + lttng_snapshot_output_list_destroy(list); + + if (!output_seen) { + MSG("%sNone", indent4); + } + +error: + return ret; +} + +/* + * Delete output by ID. + */ +static int del_output(uint32_t id, const char *name) +{ + int ret; + struct lttng_snapshot_output *output = NULL; + + output = lttng_snapshot_output_create(); + if (!output) { + ret = CMD_FATAL; + goto error; + } + + if (name) { + ret = lttng_snapshot_output_set_name(name, output); + } else if (id != UINT32_MAX) { + ret = lttng_snapshot_output_set_id(id, output); + } else { + ret = CMD_ERROR; + goto error; + } + if (ret < 0) { + ret = CMD_FATAL; + goto error; + } + + ret = lttng_snapshot_del_output(current_session_name, output); + if (ret < 0) { + goto error; + } + + if (id != UINT32_MAX) { + MSG("Snapshot output id %" PRIu32 " successfully deleted for session %s", + id, current_session_name); + } else { + MSG("Snapshot output %s successfully deleted for session %s", + name, current_session_name); + } + + if (lttng_opt_mi) { + ret = mi_lttng_snapshot_del_output(writer, id, name, + current_session_name); + if (ret) { + ret = CMD_ERROR; + } + } + +error: + lttng_snapshot_output_destroy(output); + return ret; +} + +/* + * Add output from the user URL. + */ +static int add_output(const char *url) +{ + int ret; + struct lttng_snapshot_output *output = NULL; + char name[NAME_MAX]; + const char *n_ptr; + + if (!url && (!opt_data_url || !opt_ctrl_url)) { + ret = CMD_ERROR; + goto error; + } + + output = create_output_from_args(url); + if (!output) { + ret = CMD_FATAL; + goto error; + } + + /* This call, if successful, populates the id of the output object. */ + ret = lttng_snapshot_add_output(current_session_name, output); + if (ret < 0) { + goto error; + } + + n_ptr = lttng_snapshot_output_get_name(output); + if (*n_ptr == '\0') { + int pret; + pret = snprintf(name, sizeof(name), DEFAULT_SNAPSHOT_NAME "-%" PRIu32, + lttng_snapshot_output_get_id(output)); + if (pret < 0) { + PERROR("snprintf add output name"); + } + n_ptr = name; + } + + MSG("Snapshot output successfully added for session %s", + current_session_name); + if (opt_max_size) { + MSG(" [%" PRIu32 "] %s: %s (max size: %" PRIu64 " bytes)", + lttng_snapshot_output_get_id(output), n_ptr, + lttng_snapshot_output_get_ctrl_url(output), + lttng_snapshot_output_get_maxsize(output)); + } else { + MSG(" [%" PRIu32 "] %s: %s", + lttng_snapshot_output_get_id(output), n_ptr, + lttng_snapshot_output_get_ctrl_url(output)); + } + if (lttng_opt_mi) { + ret = mi_lttng_snapshot_add_output(writer, current_session_name, + n_ptr, output); + if (ret) { + ret = CMD_ERROR; + } + } +error: + lttng_snapshot_output_destroy(output); + return ret; +} + +static int cmd_add_output(int argc, const char **argv) +{ + int ret; + + if (argc < 2 && (!opt_data_url || !opt_ctrl_url)) { + ERR("An output destination must be specified to add a snapshot output."); + ret = CMD_ERROR; + goto end; + } + + ret = add_output(argv[1]); + if (ret < 0) { + switch (-ret) { + case LTTNG_ERR_SNAPSHOT_UNSUPPORTED: + ERR("Session \"%s\" contains a channel that is incompatible with the snapshot functionality.\nMake sure all channels are configured in 'mmap' output mode.", + current_session_name); + ret = CMD_ERROR; + break; + default: + break; + } + } + +end: + return ret; +} + +static int cmd_del_output(int argc, const char **argv) +{ + int ret; + char *name; + long id; + + if (argc < 2) { + ERR("A snapshot output name or id must be provided to delete a snapshot output."); + ret = CMD_ERROR; + goto end; + } + + errno = 0; + id = strtol(argv[1], &name, 10); + if (id == 0 && (errno == 0 || errno == EINVAL)) { + ret = del_output(UINT32_MAX, name); + } else if (errno == 0 && *name == '\0') { + ret = del_output(id, NULL); + } else { + ERR("Argument %s not recognized", argv[1]); + ret = -1; + goto end; + } + +end: + return ret; +} + +static int cmd_list_output(int argc, const char **argv) +{ + int ret; + + ret = list_output(); + + return ret; +} + +/* + * Do a snapshot record with the URL if one is given. + */ +static int record(const char *url) +{ + int ret; + struct lttng_snapshot_output *output = NULL; + + output = create_output_from_args(url); + if (!output) { + ret = CMD_FATAL; + goto error; + } + + ret = lttng_snapshot_record(current_session_name, output, 0); + if (ret < 0) { + if (ret == -LTTNG_ERR_MAX_SIZE_INVALID) { + ERR("Invalid snapshot size. Cannot fit at least one packet per stream."); + } + goto error; + } + + MSG("Snapshot recorded successfully for session %s", current_session_name); + + if (url) { + MSG("Snapshot written at: %s", url); + } else if (opt_ctrl_url) { + MSG("Snapshot written to ctrl: %s, data: %s", opt_ctrl_url, + opt_data_url); + } + + if (lttng_opt_mi) { + ret = mi_lttng_snapshot_record(writer, current_session_name, url, + opt_ctrl_url, opt_data_url); + if (ret) { + ret = CMD_ERROR; + } + } + +error: + lttng_snapshot_output_destroy(output); + return ret; +} + +static int cmd_record(int argc, const char **argv) +{ + int ret; + + if (argc == 2) { + ret = record(argv[1]); + } else { + ret = record(NULL); + } + + return ret; +} + +static enum cmd_error_code handle_command(const char **argv) +{ + int mi_ret, i = 0, argc; + enum cmd_error_code cmd_ret; + struct cmd_struct *cmd; + + if (!argv) { + ERR("No action specified for snapshot command."); + cmd_ret = CMD_ERROR; + goto end; + } + + if ((!opt_ctrl_url && opt_data_url) || + (opt_ctrl_url && !opt_data_url)) { + ERR("URLs must be specified for both data and control"); + cmd_ret = CMD_ERROR; + goto end; + } + + argc = count_arguments(argv); + /* popt should have passed NULL if no arguments are present. */ + LTTNG_ASSERT(argc > 0); + + cmd = &actions[i]; + while (cmd->func != NULL) { + /* Find command */ + if (strcmp(argv[0], cmd->name) == 0) { + int result; + + if (lttng_opt_mi) { + /* Action element */ + mi_ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_action); + if (mi_ret) { + cmd_ret = CMD_ERROR; + goto end; + } + + /* Name of the action */ + mi_ret = mi_lttng_writer_write_element_string(writer, + config_element_name, argv[0]); + if (mi_ret) { + cmd_ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + mi_ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (mi_ret) { + cmd_ret = CMD_ERROR; + goto end; + } + } + + result = cmd->func(argc, argv); + if (result) { + switch (result) { + case CMD_ERROR: + case CMD_UNDEFINED: + case CMD_FATAL: + case CMD_WARNING: + case CMD_UNSUPPORTED: + /* + * Sub-commands mix lttng_error_codes + * and cmd_error_codes. This should be + * cleaned-up, but in the meantime this + * hack works since the values of the + * two enums do not intersect. + */ + cmd_ret = (cmd_error_code) result; + break; + case -LTTNG_ERR_SNAPSHOT_NODATA: + WARN("%s", lttng_strerror(result)); + + /* A warning is fine since the user has no control on + * whether or not applications (or the kernel) have + * produced any event between the start of the tracing + * session and the recording of the snapshot. MI wise + * the command is not a success since nothing was + * recorded. + */ + cmd_ret = CMD_SUCCESS; + break; + default: + ERR("%s", lttng_strerror(result)); + cmd_ret = CMD_ERROR; + break; + } + } else { + cmd_ret = CMD_SUCCESS; + } + + + if (lttng_opt_mi) { + /* Close output and action element */ + mi_ret = mi_lttng_close_multi_element(writer, 2); + if (mi_ret) { + cmd_ret = CMD_ERROR; + goto end; + } + } + goto end; + } + i++; + cmd = &actions[i]; + } + + cmd_ret = CMD_UNDEFINED; + +end: + return cmd_ret; +} +/* + * The 'snapshot ' first level command + */ +int cmd_snapshot(int argc, const char **argv) +{ + int opt; + int mi_ret; + enum cmd_error_code cmd_ret = CMD_SUCCESS; + char *session_name = NULL; + static poptContext pc; + + pc = poptGetContext(NULL, argc, argv, snapshot_opts, 0); + poptReadDefaultConfig(pc, 0); + + /* Mi check */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + cmd_ret = CMD_ERROR; + goto end; + } + + /* Open command element */ + mi_ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_snapshot); + if (mi_ret) { + cmd_ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + mi_ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (mi_ret) { + cmd_ret = CMD_ERROR; + goto end; + } + } + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + { + int ret; + + /* SHOW_HELP assigns to ret. */ + SHOW_HELP(); + cmd_ret = (cmd_error_code) ret; + goto end; + } + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, snapshot_opts); + goto end; + case OPT_LIST_COMMANDS: + list_commands(actions, stdout); + goto end; + case OPT_MAX_SIZE: + { + uint64_t val; + const char *max_size_arg = poptGetOptArg(pc); + + if (utils_parse_size_suffix((char *) max_size_arg, &val) < 0) { + ERR("Unable to handle max-size value %s", + max_size_arg); + cmd_ret = CMD_ERROR; + goto end; + } + + opt_max_size = val; + + break; + } + default: + cmd_ret = CMD_UNDEFINED; + goto end; + } + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + cmd_ret = CMD_ERROR; + goto end; + } + current_session_name = session_name; + } else { + current_session_name = opt_session_name; + } + + cmd_ret = handle_command(poptGetArgs(pc)); + + if (lttng_opt_mi) { + /* Close output element */ + mi_ret = mi_lttng_writer_close_element(writer); + if (mi_ret) { + cmd_ret = CMD_ERROR; + goto end; + } + + /* Success ? */ + mi_ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, + cmd_ret == CMD_SUCCESS); + if (mi_ret) { + cmd_ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + mi_ret = mi_lttng_writer_command_close(writer); + if (mi_ret) { + cmd_ret = CMD_ERROR; + goto end; + } + } + +end: + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + cmd_ret = CMD_ERROR; + } + + if (!opt_session_name) { + free(session_name); + } + + poptFreeContext(pc); + return cmd_ret; +} diff --git a/src/bin/lttng/commands/start.c b/src/bin/lttng/commands/start.c deleted file mode 100644 index 7082e097d..000000000 --- a/src/bin/lttng/commands/start.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "../command.h" - - -static char *opt_session_name; -static struct mi_writer *writer; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {0, 0, 0, 0, 0, 0, 0} -}; - -static int mi_print_session(char *session_name, int enabled) -{ - int ret; - - /* Open session element */ - ret = mi_lttng_writer_open_element(writer, config_element_session); - if (ret) { - goto end; - } - - /* Print session name element */ - ret = mi_lttng_writer_write_element_string(writer, config_element_name, - session_name); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_write_element_bool(writer, config_element_enabled, - enabled); - if (ret) { - goto end; - } - - /* Close session element */ - ret = mi_lttng_writer_close_element(writer); - -end: - return ret; -} - -/* - * start_tracing - * - * Start tracing for all trace of the session. - */ -static int start_tracing(void) -{ - int ret; - char *session_name; - - if (opt_session_name == NULL) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = CMD_ERROR; - goto error; - } - } else { - session_name = opt_session_name; - } - - DBG("Starting tracing for session %s", session_name); - - ret = lttng_start_tracing(session_name); - if (ret < 0) { - switch (-ret) { - case LTTNG_ERR_TRACE_ALREADY_STARTED: - WARN("Tracing already started for session %s", session_name); - break; - default: - ERR("%s", lttng_strerror(ret)); - break; - } - goto free_name; - } - - ret = CMD_SUCCESS; - - MSG("Tracing started for session %s", session_name); - if (lttng_opt_mi) { - ret = mi_print_session(session_name, 1); - if (ret) { - ret = CMD_ERROR; - goto free_name; - } - } - -free_name: - if (opt_session_name == NULL) { - free(session_name); - } -error: - return ret; -} - -/* - * cmd_start - * - * The 'start ' first level command - */ -int cmd_start(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; - static poptContext pc; - const char *leftover = NULL; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - default: - ret = CMD_UNDEFINED; - goto end; - } - } - - opt_session_name = (char*) poptGetArg(pc); - - leftover = poptGetArg(pc); - if (leftover) { - ERR("Unknown argument: %s", leftover); - ret = CMD_ERROR; - goto end; - } - - /* Mi check */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_start); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* - * Open sessions element - * For validation purpose - */ - ret = mi_lttng_writer_open_element(writer, - config_element_sessions); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - command_ret = start_tracing(); - if (command_ret) { - success = 0; - } - - /* Mi closing */ - if (lttng_opt_mi) { - /* Close sessions and output element */ - ret = mi_lttng_close_multi_element(writer, 2); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - -end: - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; - } - - /* Overwrite ret if an error occurred with start_tracing */ - ret = command_ret ? command_ret : ret; - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/start.cpp b/src/bin/lttng/commands/start.cpp new file mode 100644 index 000000000..7082e097d --- /dev/null +++ b/src/bin/lttng/commands/start.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2011 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../command.h" + + +static char *opt_session_name; +static struct mi_writer *writer; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {0, 0, 0, 0, 0, 0, 0} +}; + +static int mi_print_session(char *session_name, int enabled) +{ + int ret; + + /* Open session element */ + ret = mi_lttng_writer_open_element(writer, config_element_session); + if (ret) { + goto end; + } + + /* Print session name element */ + ret = mi_lttng_writer_write_element_string(writer, config_element_name, + session_name); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_bool(writer, config_element_enabled, + enabled); + if (ret) { + goto end; + } + + /* Close session element */ + ret = mi_lttng_writer_close_element(writer); + +end: + return ret; +} + +/* + * start_tracing + * + * Start tracing for all trace of the session. + */ +static int start_tracing(void) +{ + int ret; + char *session_name; + + if (opt_session_name == NULL) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = CMD_ERROR; + goto error; + } + } else { + session_name = opt_session_name; + } + + DBG("Starting tracing for session %s", session_name); + + ret = lttng_start_tracing(session_name); + if (ret < 0) { + switch (-ret) { + case LTTNG_ERR_TRACE_ALREADY_STARTED: + WARN("Tracing already started for session %s", session_name); + break; + default: + ERR("%s", lttng_strerror(ret)); + break; + } + goto free_name; + } + + ret = CMD_SUCCESS; + + MSG("Tracing started for session %s", session_name); + if (lttng_opt_mi) { + ret = mi_print_session(session_name, 1); + if (ret) { + ret = CMD_ERROR; + goto free_name; + } + } + +free_name: + if (opt_session_name == NULL) { + free(session_name); + } +error: + return ret; +} + +/* + * cmd_start + * + * The 'start ' first level command + */ +int cmd_start(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; + static poptContext pc; + const char *leftover = NULL; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + default: + ret = CMD_UNDEFINED; + goto end; + } + } + + opt_session_name = (char*) poptGetArg(pc); + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + ret = CMD_ERROR; + goto end; + } + + /* Mi check */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_start); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* + * Open sessions element + * For validation purpose + */ + ret = mi_lttng_writer_open_element(writer, + config_element_sessions); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + command_ret = start_tracing(); + if (command_ret) { + success = 0; + } + + /* Mi closing */ + if (lttng_opt_mi) { + /* Close sessions and output element */ + ret = mi_lttng_close_multi_element(writer, 2); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + +end: + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; + } + + /* Overwrite ret if an error occurred with start_tracing */ + ret = command_ret ? command_ret : ret; + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/status.c b/src/bin/lttng/commands/status.c deleted file mode 100644 index 23e8c03d3..000000000 --- a/src/bin/lttng/commands/status.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2015 Philippe Proulx - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include "../command.h" -#include "../utils.h" -#include - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {0, 0, 0, 0, 0, 0, 0} -}; - -static int status(void) -{ - const char *argv[2]; - int ret = CMD_SUCCESS; - char *session_name = NULL; - - session_name = get_session_name(); - if (!session_name) { - ret = CMD_ERROR; - goto end; - } - - argv[0] = "list"; - argv[1] = session_name; - ret = cmd_list(2, argv); -end: - free(session_name); - return ret; -} - -/* - * The 'status ' first level command - */ -int cmd_status(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS; - static poptContext pc; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - default: - ret = CMD_UNDEFINED; - goto end; - } - } - - if (poptPeekArg(pc) != NULL) { - ERR("This command does not accept positional arguments.\n"); - ret = CMD_UNDEFINED; - goto end; - } - - ret = status(); -end: - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/status.cpp b/src/bin/lttng/commands/status.cpp new file mode 100644 index 000000000..23e8c03d3 --- /dev/null +++ b/src/bin/lttng/commands/status.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2015 Philippe Proulx + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include "../command.h" +#include "../utils.h" +#include + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {0, 0, 0, 0, 0, 0, 0} +}; + +static int status(void) +{ + const char *argv[2]; + int ret = CMD_SUCCESS; + char *session_name = NULL; + + session_name = get_session_name(); + if (!session_name) { + ret = CMD_ERROR; + goto end; + } + + argv[0] = "list"; + argv[1] = session_name; + ret = cmd_list(2, argv); +end: + free(session_name); + return ret; +} + +/* + * The 'status ' first level command + */ +int cmd_status(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS; + static poptContext pc; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + default: + ret = CMD_UNDEFINED; + goto end; + } + } + + if (poptPeekArg(pc) != NULL) { + ERR("This command does not accept positional arguments.\n"); + ret = CMD_UNDEFINED; + goto end; + } + + ret = status(); +end: + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/stop.c b/src/bin/lttng/commands/stop.c deleted file mode 100644 index d69da40ab..000000000 --- a/src/bin/lttng/commands/stop.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "../command.h" - -static char *opt_session_name; -static int opt_no_wait; -static struct mi_writer *writer; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {"no-wait", 'n', POPT_ARG_VAL, &opt_no_wait, 1, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * Mi print of partial session - */ -static int mi_print_session(char *session_name, int enabled) -{ - int ret; - LTTNG_ASSERT(writer); - LTTNG_ASSERT(session_name); - - /* Open session element */ - ret = mi_lttng_writer_open_element(writer, config_element_session); - if (ret) { - goto end; - } - - /* Print session name element */ - ret = mi_lttng_writer_write_element_string(writer, config_element_name, - session_name); - if (ret) { - goto end; - } - - /* Is enabled ? */ - ret = mi_lttng_writer_write_element_bool(writer, config_element_enabled, - enabled); - if (ret) { - goto end; - } - - /* Close session element */ - ret = mi_lttng_writer_close_element(writer); - -end: - return ret; -} - -/* - * Start tracing for all trace of the session. - */ -static int stop_tracing(void) -{ - int ret; - char *session_name; - - if (opt_session_name == NULL) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = CMD_ERROR; - goto error; - } - } else { - session_name = opt_session_name; - } - - ret = lttng_stop_tracing_no_wait(session_name); - if (ret < 0) { - switch (-ret) { - case LTTNG_ERR_TRACE_ALREADY_STOPPED: - WARN("Tracing already stopped for session %s", session_name); - break; - default: - ERR("%s", lttng_strerror(ret)); - break; - } - goto free_name; - } - - if (!opt_no_wait) { - _MSG("Waiting for data availability"); - fflush(stdout); - do { - ret = lttng_data_pending(session_name); - if (ret < 0) { - /* Return the data available call error. */ - goto free_name; - } - - /* - * Data sleep time before retrying (in usec). Don't sleep if the call - * returned value indicates availability. - */ - if (ret) { - usleep(DEFAULT_DATA_AVAILABILITY_WAIT_TIME_US); - _MSG("."); - fflush(stdout); - } - } while (ret != 0); - MSG(""); - } - - ret = CMD_SUCCESS; - - print_session_stats(session_name); - MSG("Tracing stopped for session %s", session_name); - if (lttng_opt_mi) { - ret = mi_print_session(session_name, 0); - if (ret) { - goto free_name; - } - } - -free_name: - if (opt_session_name == NULL) { - free(session_name); - } - -error: - return ret; -} - -/* - * cmd_stop - * - * The 'stop ' first level command - */ -int cmd_stop(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; - static poptContext pc; - const char *leftover = NULL; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - default: - ret = CMD_UNDEFINED; - goto end; - } - } - - /* Mi check */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_stop); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* - * Open sessions element - * For validation - */ - ret = mi_lttng_writer_open_element(writer, - config_element_sessions); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - - opt_session_name = (char*) poptGetArg(pc); - - leftover = poptGetArg(pc); - if (leftover) { - ERR("Unknown argument: %s", leftover); - ret = CMD_ERROR; - goto end; - } - - command_ret = stop_tracing(); - if (command_ret) { - success = 0; - } - - /* Mi closing */ - if (lttng_opt_mi) { - /* Close sessions and output element */ - ret = mi_lttng_close_multi_element(writer, 2); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - ret = CMD_ERROR; - goto end; - } - } - -end: - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; - } - - /* Overwrite ret if an error occurred in stop_tracing() */ - ret = command_ret ? command_ret : ret; - - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/stop.cpp b/src/bin/lttng/commands/stop.cpp new file mode 100644 index 000000000..d69da40ab --- /dev/null +++ b/src/bin/lttng/commands/stop.cpp @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2011 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../command.h" + +static char *opt_session_name; +static int opt_no_wait; +static struct mi_writer *writer; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {"no-wait", 'n', POPT_ARG_VAL, &opt_no_wait, 1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * Mi print of partial session + */ +static int mi_print_session(char *session_name, int enabled) +{ + int ret; + LTTNG_ASSERT(writer); + LTTNG_ASSERT(session_name); + + /* Open session element */ + ret = mi_lttng_writer_open_element(writer, config_element_session); + if (ret) { + goto end; + } + + /* Print session name element */ + ret = mi_lttng_writer_write_element_string(writer, config_element_name, + session_name); + if (ret) { + goto end; + } + + /* Is enabled ? */ + ret = mi_lttng_writer_write_element_bool(writer, config_element_enabled, + enabled); + if (ret) { + goto end; + } + + /* Close session element */ + ret = mi_lttng_writer_close_element(writer); + +end: + return ret; +} + +/* + * Start tracing for all trace of the session. + */ +static int stop_tracing(void) +{ + int ret; + char *session_name; + + if (opt_session_name == NULL) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = CMD_ERROR; + goto error; + } + } else { + session_name = opt_session_name; + } + + ret = lttng_stop_tracing_no_wait(session_name); + if (ret < 0) { + switch (-ret) { + case LTTNG_ERR_TRACE_ALREADY_STOPPED: + WARN("Tracing already stopped for session %s", session_name); + break; + default: + ERR("%s", lttng_strerror(ret)); + break; + } + goto free_name; + } + + if (!opt_no_wait) { + _MSG("Waiting for data availability"); + fflush(stdout); + do { + ret = lttng_data_pending(session_name); + if (ret < 0) { + /* Return the data available call error. */ + goto free_name; + } + + /* + * Data sleep time before retrying (in usec). Don't sleep if the call + * returned value indicates availability. + */ + if (ret) { + usleep(DEFAULT_DATA_AVAILABILITY_WAIT_TIME_US); + _MSG("."); + fflush(stdout); + } + } while (ret != 0); + MSG(""); + } + + ret = CMD_SUCCESS; + + print_session_stats(session_name); + MSG("Tracing stopped for session %s", session_name); + if (lttng_opt_mi) { + ret = mi_print_session(session_name, 0); + if (ret) { + goto free_name; + } + } + +free_name: + if (opt_session_name == NULL) { + free(session_name); + } + +error: + return ret; +} + +/* + * cmd_stop + * + * The 'stop ' first level command + */ +int cmd_stop(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; + static poptContext pc; + const char *leftover = NULL; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + default: + ret = CMD_UNDEFINED; + goto end; + } + } + + /* Mi check */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_stop); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* + * Open sessions element + * For validation + */ + ret = mi_lttng_writer_open_element(writer, + config_element_sessions); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + + opt_session_name = (char*) poptGetArg(pc); + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + ret = CMD_ERROR; + goto end; + } + + command_ret = stop_tracing(); + if (command_ret) { + success = 0; + } + + /* Mi closing */ + if (lttng_opt_mi) { + /* Close sessions and output element */ + ret = mi_lttng_close_multi_element(writer, 2); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + +end: + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; + } + + /* Overwrite ret if an error occurred in stop_tracing() */ + ret = command_ret ? command_ret : ret; + + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/track-untrack.c b/src/bin/lttng/commands/track-untrack.c deleted file mode 100644 index f175e2a86..000000000 --- a/src/bin/lttng/commands/track-untrack.c +++ /dev/null @@ -1,819 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * Copyright (C) 2015 Mathieu Desnoyers - * Copyright (C) 2020 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include - -#include "../command.h" - -struct process_attr_command_args { - enum lttng_process_attr process_attr; - /* Present in the user's command. */ - bool requested; - bool all; - struct lttng_dynamic_pointer_array string_args; -}; - -enum cmd_type { - CMD_TRACK, - CMD_UNTRACK, -}; - -/* Offset OPT_ values by one since libpopt gives '0' a special meaning. */ -enum { - OPT_PID = LTTNG_PROCESS_ATTR_PROCESS_ID + 1, - OPT_VPID = LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID + 1, - OPT_UID = LTTNG_PROCESS_ATTR_USER_ID + 1, - OPT_VUID = LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID + 1, - OPT_GID = LTTNG_PROCESS_ATTR_GROUP_ID + 1, - OPT_VGID = LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID + 1, - OPT_HELP, - OPT_LIST_OPTIONS, - OPT_SESSION, - OPT_ALL, -}; - -static char *opt_session_name; -static int opt_kernel; -static int opt_userspace; -static char *opt_str_arg; - -static struct poptOption long_options[] = { - /* { longName, shortName, argInfo, argPtr, value, descrip, argDesc, } */ - { "help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0, }, - { "session", 's', POPT_ARG_STRING, &opt_session_name, OPT_SESSION, 0, 0, }, - { "kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0, }, - { "userspace", 'u', POPT_ARG_VAL, &opt_userspace, 1, 0, 0, }, - { "pid", 'p', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_str_arg, OPT_PID, 0, 0, }, - { "vpid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_str_arg, OPT_VPID, 0, 0, }, - { "uid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_str_arg, OPT_UID, 0, 0, }, - { "vuid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_str_arg, OPT_VUID, 0, 0, }, - { "gid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_str_arg, OPT_GID, 0, 0, }, - { "vgid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_str_arg, OPT_VGID, 0, 0, }, - { "all", 'a', POPT_ARG_NONE, 0, OPT_ALL, 0, 0, }, - { "list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, 0, 0, }, - { 0, 0, 0, 0, 0, 0, 0, }, -}; - -static struct process_attr_command_args - process_attr_commands[LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID + 1]; - -static void process_attr_command_init(struct process_attr_command_args *cmd, - enum lttng_process_attr process_attr) -{ - cmd->process_attr = process_attr; - cmd->all = false; - lttng_dynamic_pointer_array_init(&cmd->string_args, NULL); -} - -static void process_attr_command_fini(struct process_attr_command_args *cmd) -{ - lttng_dynamic_pointer_array_reset(&cmd->string_args); -} - -static const char *get_capitalized_process_attr_str(enum lttng_process_attr process_attr) -{ - switch (process_attr) { - case LTTNG_PROCESS_ATTR_PROCESS_ID: - return "Process ID"; - case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: - return "Virtual process ID"; - case LTTNG_PROCESS_ATTR_USER_ID: - return "User ID"; - case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: - return "Virtual user ID"; - case LTTNG_PROCESS_ATTR_GROUP_ID: - return "Group ID"; - case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: - return "Virtual group ID"; - default: - return "Unknown"; - } - return NULL; -} - -static bool ust_process_attr_supported(enum lttng_process_attr *process_attr) -{ - bool supported; - - switch (*process_attr) { - case LTTNG_PROCESS_ATTR_PROCESS_ID: - *process_attr = LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID; - /* fall-through. */ - case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: - case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: - case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: - supported = true; - break; - default: - ERR("The %s process attribute cannot be tracked in the user space domain.", - lttng_process_attr_to_string(*process_attr)); - supported = false; - break; - } - return supported; -} - -static const char *get_mi_element_command(enum cmd_type cmd_type) -{ - switch (cmd_type) { - case CMD_TRACK: - return mi_lttng_element_command_track; - case CMD_UNTRACK: - return mi_lttng_element_command_untrack; - default: - abort(); - } -} - -static enum cmd_error_code run_command_all(enum cmd_type cmd_type, - const char *session_name, - enum lttng_domain_type domain_type, - enum lttng_process_attr process_attr, - struct mi_writer *writer) -{ - struct lttng_process_attr_tracker_handle *tracker_handle = NULL; - const enum lttng_error_code handle_ret_code = - lttng_session_get_tracker_handle(session_name, - domain_type, process_attr, - &tracker_handle); - enum cmd_error_code cmd_ret = CMD_SUCCESS; - enum lttng_process_attr_tracker_handle_status status; - - if (writer) { - const int ret = mi_lttng_all_process_attribute_value( - writer, process_attr, true); - if (ret) { - cmd_ret = CMD_FATAL; - goto end; - } - } - - if (handle_ret_code != LTTNG_OK) { - ERR("Session `%s` does not exist", session_name); - cmd_ret = CMD_FATAL; - goto end; - } - - status = lttng_process_attr_tracker_handle_set_tracking_policy( - tracker_handle, - cmd_type == CMD_TRACK ? - LTTNG_TRACKING_POLICY_INCLUDE_ALL : - LTTNG_TRACKING_POLICY_EXCLUDE_ALL); - switch (status) { - case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK: - if (cmd_type == CMD_TRACK) { - MSG("%s tracking policy set to `include all`", - get_capitalized_process_attr_str(process_attr)); - } else { - MSG("%s tracking policy set to `exclude all`", - get_capitalized_process_attr_str(process_attr)); - } - break; - case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_SESSION_DOES_NOT_EXIST: - ERR("%s", lttng_strerror(-LTTNG_ERR_SESS_NOT_FOUND)); - break; - default: - ERR("Unknown error encountered while setting tracking policy of %s tracker to `%s`", - lttng_process_attr_to_string(process_attr), - cmd_type == CMD_TRACK ? "include all" : - "exclude all"); - cmd_ret = CMD_FATAL; - break; - } -end: - if (writer) { - int ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_success, - cmd_ret == CMD_SUCCESS); - - if (ret) { - cmd_ret = CMD_FATAL; - } else { - ret = mi_lttng_writer_close_element(writer); - cmd_ret = ret == 0 ? cmd_ret : CMD_FATAL; - } - } - lttng_process_attr_tracker_handle_destroy(tracker_handle); - return cmd_ret; -} - -static enum cmd_error_code run_command_string(enum cmd_type cmd_type, - const char *session_name, - enum lttng_domain_type domain_type, - enum lttng_process_attr process_attr, - const char *_args, - struct mi_writer *writer) -{ - struct lttng_process_attr_tracker_handle *tracker_handle; - const enum lttng_error_code handle_ret_code = - lttng_session_get_tracker_handle(session_name, - domain_type, process_attr, - &tracker_handle); - enum cmd_error_code cmd_ret = CMD_SUCCESS; - const char *one_value_str; - char *args = strdup(_args); - char *iter = args; - bool policy_set = false; - - if (!args) { - ERR("%s", lttng_strerror(-LTTNG_ERR_NOMEM)); - cmd_ret = CMD_FATAL; - goto end; - } - - if (handle_ret_code != LTTNG_OK) { - ERR("%s", lttng_strerror(-handle_ret_code)); - cmd_ret = CMD_FATAL; - goto end; - } - - while ((one_value_str = strtok_r(iter, ",", &iter)) != NULL) { - const bool is_numerical_argument = isdigit(one_value_str[0]); - enum lttng_process_attr_tracker_handle_status status; - enum lttng_tracking_policy policy; - int ret; - char *prettified_arg; - - if (!policy_set) { - status = lttng_process_attr_tracker_handle_get_tracking_policy( - tracker_handle, &policy); - if (status != LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK) { - break; - } - - if (policy != LTTNG_TRACKING_POLICY_INCLUDE_SET) { - status = lttng_process_attr_tracker_handle_set_tracking_policy( - tracker_handle, - LTTNG_TRACKING_POLICY_INCLUDE_SET); - if (status != LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK) { - break; - } - } - policy_set = true; - } - - if (is_numerical_argument) { - const unsigned long one_value_int = - strtoul(one_value_str, NULL, 10); - - if (writer) { - ret = mi_lttng_integral_process_attribute_value( - writer, process_attr, - (int64_t) one_value_int, true); - if (ret) { - cmd_ret = CMD_FATAL; - goto end; - } - } - - switch (process_attr) { - case LTTNG_PROCESS_ATTR_PROCESS_ID: - status = cmd_type == CMD_TRACK ? - lttng_process_attr_process_id_tracker_handle_add_pid( - tracker_handle, - (pid_t) one_value_int) : - lttng_process_attr_process_id_tracker_handle_remove_pid( - tracker_handle, - (pid_t) one_value_int); - break; - case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: - status = cmd_type == CMD_TRACK ? - lttng_process_attr_virtual_process_id_tracker_handle_add_pid( - tracker_handle, - (pid_t) one_value_int) : - lttng_process_attr_virtual_process_id_tracker_handle_remove_pid( - tracker_handle, - (pid_t) one_value_int); - break; - case LTTNG_PROCESS_ATTR_USER_ID: - status = cmd_type == CMD_TRACK ? - lttng_process_attr_user_id_tracker_handle_add_uid( - tracker_handle, - (uid_t) one_value_int) : - lttng_process_attr_user_id_tracker_handle_remove_uid( - tracker_handle, - (uid_t) one_value_int); - break; - case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: - status = cmd_type == CMD_TRACK ? - lttng_process_attr_virtual_user_id_tracker_handle_add_uid( - tracker_handle, - (uid_t) one_value_int) : - lttng_process_attr_virtual_user_id_tracker_handle_remove_uid( - tracker_handle, - (uid_t) one_value_int); - break; - case LTTNG_PROCESS_ATTR_GROUP_ID: - status = cmd_type == CMD_TRACK ? - lttng_process_attr_group_id_tracker_handle_add_gid( - tracker_handle, - (gid_t) one_value_int) : - lttng_process_attr_group_id_tracker_handle_remove_gid( - tracker_handle, - (gid_t) one_value_int); - break; - case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: - status = cmd_type == CMD_TRACK ? - lttng_process_attr_virtual_group_id_tracker_handle_add_gid( - tracker_handle, - (gid_t) one_value_int) : - lttng_process_attr_virtual_group_id_tracker_handle_remove_gid( - tracker_handle, - (gid_t) one_value_int); - break; - default: - abort(); - } - - } else { - if (writer) { - ret = mi_lttng_string_process_attribute_value( - writer, process_attr, - one_value_str, true); - if (ret) { - cmd_ret = CMD_FATAL; - goto end; - } - } - - switch (process_attr) { - case LTTNG_PROCESS_ATTR_USER_ID: - status = cmd_type == CMD_TRACK ? - lttng_process_attr_user_id_tracker_handle_add_user_name( - tracker_handle, - one_value_str) : - lttng_process_attr_user_id_tracker_handle_remove_user_name( - tracker_handle, - one_value_str); - break; - case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: - status = cmd_type == CMD_TRACK ? - lttng_process_attr_virtual_user_id_tracker_handle_add_user_name( - tracker_handle, - one_value_str) : - lttng_process_attr_virtual_user_id_tracker_handle_remove_user_name( - tracker_handle, - one_value_str); - break; - case LTTNG_PROCESS_ATTR_GROUP_ID: - status = cmd_type == CMD_TRACK ? - lttng_process_attr_group_id_tracker_handle_add_group_name( - tracker_handle, - one_value_str) : - lttng_process_attr_group_id_tracker_handle_remove_group_name( - tracker_handle, - one_value_str); - break; - case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: - status = cmd_type == CMD_TRACK ? - lttng_process_attr_virtual_group_id_tracker_handle_add_group_name( - tracker_handle, - one_value_str) : - lttng_process_attr_virtual_group_id_tracker_handle_remove_group_name( - tracker_handle, - one_value_str); - break; - default: - ERR("%s is not a valid %s value; expected an integer", - one_value_str, - lttng_process_attr_to_string( - process_attr)); - cmd_ret = CMD_FATAL; - goto end; - } - } - - ret = asprintf(&prettified_arg, - is_numerical_argument ? "%s" : "`%s`", - one_value_str); - if (ret < 0) { - PERROR("Failed to format argument `%s`", one_value_str); - cmd_ret = CMD_FATAL; - goto end; - } - - switch (status) { - case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK: - if (cmd_type == CMD_TRACK) { - MSG("Added %s to the %s tracker inclusion set", - one_value_str, - lttng_process_attr_to_string( - process_attr)); - } else { - MSG("Removed %s from the %s tracker inclusion set", - one_value_str, - lttng_process_attr_to_string( - process_attr)); - } - break; - case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_SESSION_DOES_NOT_EXIST: - ERR("Session `%s` not found", session_name); - break; - case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_EXISTS: - WARN("%s is already in the %s inclusion set", - prettified_arg, - lttng_process_attr_to_string( - process_attr)); - break; - case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_MISSING: - WARN("%s is not in the %s the inclusion set", - prettified_arg, - lttng_process_attr_to_string( - process_attr)); - break; - case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_USER_NOT_FOUND: - ERR("User %s was not found", prettified_arg); - cmd_ret = CMD_ERROR; - break; - case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_GROUP_NOT_FOUND: - ERR("Group %s was not found", prettified_arg); - cmd_ret = CMD_ERROR; - break; - default: - ERR("Unknown error encountered while %s %s %s %s tracker's inclusion set", - cmd_type == CMD_TRACK ? "adding" : - "removing", - lttng_process_attr_to_string( - process_attr), - prettified_arg, - cmd_type == CMD_TRACK ? "to" : "from"); - cmd_ret = CMD_FATAL; - break; - } - free(prettified_arg); - - if (writer) { - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_success, - cmd_ret == CMD_SUCCESS); - - if (ret) { - cmd_ret = CMD_FATAL; - } else { - ret = mi_lttng_writer_close_element(writer); - cmd_ret = ret == 0 ? cmd_ret : CMD_FATAL; - } - } - } -end: - free(args); - lttng_process_attr_tracker_handle_destroy(tracker_handle); - return cmd_ret; -} - -static enum cmd_error_code run_command(enum cmd_type cmd_type, - const char *session_name, - const struct process_attr_command_args *command_args, - struct mi_writer *writer) -{ - const enum lttng_domain_type domain_type = - opt_kernel ? LTTNG_DOMAIN_KERNEL : LTTNG_DOMAIN_UST; - enum cmd_error_code cmd_ret = CMD_SUCCESS; - unsigned int i; - const unsigned int string_arg_count = - lttng_dynamic_pointer_array_get_count( - &command_args->string_args); - enum lttng_process_attr process_attr = command_args->process_attr; - - if (opt_userspace) { - /* - * Check that this process attribute can be tracked - * in the user space domain. Backward-compatibility - * changes are be applied to process_attr as needed. - */ - if (!ust_process_attr_supported(&process_attr)) { - cmd_ret = CMD_ERROR; - goto end; - } - } - - if (writer) { - /* Open tracker and trackers elements */ - const int ret = mi_lttng_process_attribute_tracker_open( - writer, process_attr); - if (ret) { - cmd_ret = CMD_FATAL; - goto end; - } - } - - if (command_args->all) { - cmd_ret = run_command_all(cmd_type, session_name, domain_type, - process_attr, writer); - } else { - bool error_occurred = false; - - for (i = 0; i < string_arg_count; i++) { - const char *arg = lttng_dynamic_pointer_array_get_pointer( - &command_args->string_args, i); - - cmd_ret = run_command_string(cmd_type, session_name, - domain_type, process_attr, arg, writer); - if (cmd_ret != CMD_SUCCESS) { - error_occurred = true; - if (cmd_ret == CMD_FATAL) { - break; - } - goto end; - } - } - if (error_occurred) { - cmd_ret = CMD_ERROR; - } - } - - if (writer) { - /* Close tracker and trackers elements */ - const int ret = mi_lttng_close_multi_element( - writer, 2); - if (ret) { - cmd_ret = CMD_FATAL; - goto end; - } - } -end: - return cmd_ret; -} - -/* - * Add/remove tracker to/from session. - */ -static int cmd_track_untrack(enum cmd_type cmd_type, - int argc, - const char **argv, - const char *help_msg) -{ - int opt, ret = 0; - bool sub_command_failed = false; - bool opt_all = false; - unsigned int selected_process_attr_tracker_count = 0; - const unsigned int command_count = - sizeof(process_attr_commands) / - sizeof(struct process_attr_command_args); - enum cmd_error_code command_ret = CMD_SUCCESS; - static poptContext pc; - char *session_name = NULL; - const char *leftover = NULL; - struct mi_writer *writer = NULL; - size_t i; - - for (i = 0; i < command_count; i++) { - process_attr_command_init(&process_attr_commands[i], i); - } - - if (argc < 1) { - command_ret = CMD_ERROR; - goto end; - } - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - case OPT_SESSION: - break; - case OPT_PID: - case OPT_VPID: - case OPT_UID: - case OPT_VUID: - case OPT_GID: - case OPT_VGID: - /* See OPT_ enum declaration comment. */ - opt--; - selected_process_attr_tracker_count++; - process_attr_commands[opt].requested = true; - if (!opt_str_arg) { - continue; - } - ret = lttng_dynamic_pointer_array_add_pointer( - &process_attr_commands[opt].string_args, - opt_str_arg); - if (ret) { - ERR("Allocation failed while parsing command arguments"); - command_ret = CMD_ERROR; - goto end; - } - break; - case OPT_ALL: - opt_all = true; - break; - default: - command_ret = CMD_UNDEFINED; - goto end; - } - } - - ret = print_missing_or_multiple_domains( - opt_kernel + opt_userspace, false); - if (ret) { - command_ret = CMD_ERROR; - goto end; - } - - if (selected_process_attr_tracker_count == 0) { - ERR("At least one process attribute must be specified"); - command_ret = CMD_ERROR; - goto end; - } - if (opt_all) { - /* - * Only one process attribute tracker was specified; find it - * and set it in 'all' mode. - */ - for (i = 0; i < command_count; i++) { - if (!process_attr_commands[i].requested) { - continue; - } - process_attr_commands[i].all = true; - if (lttng_dynamic_pointer_array_get_count( - &process_attr_commands[i] - .string_args)) { - ERR("The --all option cannot be used with a list of process attribute values"); - command_ret = CMD_ERROR; - goto end; - } - } - } else { - for (i = 0; i < command_count; i++) { - if (!process_attr_commands[i].requested) { - continue; - } - if (lttng_dynamic_pointer_array_get_count( - &process_attr_commands[i] - .string_args) == 0) { - ERR("No process attribute value specified for %s tracker", - get_capitalized_process_attr_str( - process_attr_commands[i] - .process_attr)); - command_ret = CMD_ERROR; - goto end; - } - } - } - - if (!opt_session_name) { - session_name = get_session_name(); - if (session_name == NULL) { - command_ret = CMD_ERROR; - goto end; - } - } else { - session_name = opt_session_name; - } - - leftover = poptGetArg(pc); - if (leftover) { - ERR("Unknown argument: %s", leftover); - command_ret = CMD_ERROR; - goto end; - } - - /* Mi check */ - if (lttng_opt_mi) { - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - command_ret = CMD_ERROR; - goto end; - } - } - - if (writer) { - /* Open command element */ - ret = mi_lttng_writer_command_open(writer, - get_mi_element_command(cmd_type)); - if (ret) { - command_ret = CMD_ERROR; - goto end; - } - - /* Open output element */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - command_ret = CMD_ERROR; - goto end; - } - - ret = mi_lttng_trackers_open(writer); - if (ret) { - goto end; - } - } - - /* Execute sub-commands. */ - for (i = 0; i < command_count; i++) { - if (!process_attr_commands[i].requested) { - continue; - } - command_ret = run_command(cmd_type, session_name, - &process_attr_commands[i], writer); - if (command_ret != CMD_SUCCESS) { - sub_command_failed = true; - if (command_ret == CMD_FATAL) { - break; - } - } - } - - /* Mi closing */ - if (writer) { - /* Close trackers and output elements */ - ret = mi_lttng_close_multi_element(writer, 2); - if (ret) { - command_ret = CMD_ERROR; - goto end; - } - - /* Success ? */ - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, - !sub_command_failed); - if (ret) { - command_ret = CMD_ERROR; - goto end; - } - - /* Command element close */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - command_ret = CMD_ERROR; - goto end; - } - } - -end: - if (!opt_session_name) { - free(session_name); - } - - /* Mi clean-up */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - command_ret = CMD_ERROR; - } - - for (i = 0; i < command_count; i++) { - process_attr_command_fini(&process_attr_commands[i]); - } - - poptFreeContext(pc); - return (int) command_ret; -} - -int cmd_track(int argc, const char **argv) -{ - static const char *help_msg = -#ifdef LTTNG_EMBED_HELP -#include -#else - NULL -#endif - ; - - return cmd_track_untrack(CMD_TRACK, argc, argv, help_msg); -} - -int cmd_untrack(int argc, const char **argv) -{ - static const char *help_msg = -#ifdef LTTNG_EMBED_HELP -#include -#else - NULL -#endif - ; - - return cmd_track_untrack(CMD_UNTRACK, argc, argv, help_msg); -} diff --git a/src/bin/lttng/commands/track-untrack.cpp b/src/bin/lttng/commands/track-untrack.cpp new file mode 100644 index 000000000..7db44883b --- /dev/null +++ b/src/bin/lttng/commands/track-untrack.cpp @@ -0,0 +1,819 @@ +/* + * Copyright (C) 2011 David Goulet + * Copyright (C) 2015 Mathieu Desnoyers + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include "../command.h" + +struct process_attr_command_args { + enum lttng_process_attr process_attr; + /* Present in the user's command. */ + bool requested; + bool all; + struct lttng_dynamic_pointer_array string_args; +}; + +enum cmd_type { + CMD_TRACK, + CMD_UNTRACK, +}; + +/* Offset OPT_ values by one since libpopt gives '0' a special meaning. */ +enum { + OPT_PID = LTTNG_PROCESS_ATTR_PROCESS_ID + 1, + OPT_VPID = LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID + 1, + OPT_UID = LTTNG_PROCESS_ATTR_USER_ID + 1, + OPT_VUID = LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID + 1, + OPT_GID = LTTNG_PROCESS_ATTR_GROUP_ID + 1, + OPT_VGID = LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID + 1, + OPT_HELP, + OPT_LIST_OPTIONS, + OPT_SESSION, + OPT_ALL, +}; + +static char *opt_session_name; +static int opt_kernel; +static int opt_userspace; +static char *opt_str_arg; + +static struct poptOption long_options[] = { + /* { longName, shortName, argInfo, argPtr, value, descrip, argDesc, } */ + { "help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0, }, + { "session", 's', POPT_ARG_STRING, &opt_session_name, OPT_SESSION, 0, 0, }, + { "kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0, }, + { "userspace", 'u', POPT_ARG_VAL, &opt_userspace, 1, 0, 0, }, + { "pid", 'p', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_str_arg, OPT_PID, 0, 0, }, + { "vpid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_str_arg, OPT_VPID, 0, 0, }, + { "uid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_str_arg, OPT_UID, 0, 0, }, + { "vuid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_str_arg, OPT_VUID, 0, 0, }, + { "gid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_str_arg, OPT_GID, 0, 0, }, + { "vgid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_str_arg, OPT_VGID, 0, 0, }, + { "all", 'a', POPT_ARG_NONE, 0, OPT_ALL, 0, 0, }, + { "list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, 0, 0, }, + { 0, 0, 0, 0, 0, 0, 0, }, +}; + +static struct process_attr_command_args + process_attr_commands[LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID + 1]; + +static void process_attr_command_init(struct process_attr_command_args *cmd, + enum lttng_process_attr process_attr) +{ + cmd->process_attr = process_attr; + cmd->all = false; + lttng_dynamic_pointer_array_init(&cmd->string_args, NULL); +} + +static void process_attr_command_fini(struct process_attr_command_args *cmd) +{ + lttng_dynamic_pointer_array_reset(&cmd->string_args); +} + +static const char *get_capitalized_process_attr_str(enum lttng_process_attr process_attr) +{ + switch (process_attr) { + case LTTNG_PROCESS_ATTR_PROCESS_ID: + return "Process ID"; + case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: + return "Virtual process ID"; + case LTTNG_PROCESS_ATTR_USER_ID: + return "User ID"; + case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: + return "Virtual user ID"; + case LTTNG_PROCESS_ATTR_GROUP_ID: + return "Group ID"; + case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: + return "Virtual group ID"; + default: + return "Unknown"; + } + return NULL; +} + +static bool ust_process_attr_supported(enum lttng_process_attr *process_attr) +{ + bool supported; + + switch (*process_attr) { + case LTTNG_PROCESS_ATTR_PROCESS_ID: + *process_attr = LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID; + /* fall-through. */ + case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: + case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: + case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: + supported = true; + break; + default: + ERR("The %s process attribute cannot be tracked in the user space domain.", + lttng_process_attr_to_string(*process_attr)); + supported = false; + break; + } + return supported; +} + +static const char *get_mi_element_command(enum cmd_type cmd_type) +{ + switch (cmd_type) { + case CMD_TRACK: + return mi_lttng_element_command_track; + case CMD_UNTRACK: + return mi_lttng_element_command_untrack; + default: + abort(); + } +} + +static enum cmd_error_code run_command_all(enum cmd_type cmd_type, + const char *session_name, + enum lttng_domain_type domain_type, + enum lttng_process_attr process_attr, + struct mi_writer *writer) +{ + struct lttng_process_attr_tracker_handle *tracker_handle = NULL; + const enum lttng_error_code handle_ret_code = + lttng_session_get_tracker_handle(session_name, + domain_type, process_attr, + &tracker_handle); + enum cmd_error_code cmd_ret = CMD_SUCCESS; + enum lttng_process_attr_tracker_handle_status status; + + if (writer) { + const int ret = mi_lttng_all_process_attribute_value( + writer, process_attr, true); + if (ret) { + cmd_ret = CMD_FATAL; + goto end; + } + } + + if (handle_ret_code != LTTNG_OK) { + ERR("Session `%s` does not exist", session_name); + cmd_ret = CMD_FATAL; + goto end; + } + + status = lttng_process_attr_tracker_handle_set_tracking_policy( + tracker_handle, + cmd_type == CMD_TRACK ? + LTTNG_TRACKING_POLICY_INCLUDE_ALL : + LTTNG_TRACKING_POLICY_EXCLUDE_ALL); + switch (status) { + case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK: + if (cmd_type == CMD_TRACK) { + MSG("%s tracking policy set to `include all`", + get_capitalized_process_attr_str(process_attr)); + } else { + MSG("%s tracking policy set to `exclude all`", + get_capitalized_process_attr_str(process_attr)); + } + break; + case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_SESSION_DOES_NOT_EXIST: + ERR("%s", lttng_strerror(-LTTNG_ERR_SESS_NOT_FOUND)); + break; + default: + ERR("Unknown error encountered while setting tracking policy of %s tracker to `%s`", + lttng_process_attr_to_string(process_attr), + cmd_type == CMD_TRACK ? "include all" : + "exclude all"); + cmd_ret = CMD_FATAL; + break; + } +end: + if (writer) { + int ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_success, + cmd_ret == CMD_SUCCESS); + + if (ret) { + cmd_ret = CMD_FATAL; + } else { + ret = mi_lttng_writer_close_element(writer); + cmd_ret = ret == 0 ? cmd_ret : CMD_FATAL; + } + } + lttng_process_attr_tracker_handle_destroy(tracker_handle); + return cmd_ret; +} + +static enum cmd_error_code run_command_string(enum cmd_type cmd_type, + const char *session_name, + enum lttng_domain_type domain_type, + enum lttng_process_attr process_attr, + const char *_args, + struct mi_writer *writer) +{ + struct lttng_process_attr_tracker_handle *tracker_handle; + const enum lttng_error_code handle_ret_code = + lttng_session_get_tracker_handle(session_name, + domain_type, process_attr, + &tracker_handle); + enum cmd_error_code cmd_ret = CMD_SUCCESS; + const char *one_value_str; + char *args = strdup(_args); + char *iter = args; + bool policy_set = false; + + if (!args) { + ERR("%s", lttng_strerror(-LTTNG_ERR_NOMEM)); + cmd_ret = CMD_FATAL; + goto end; + } + + if (handle_ret_code != LTTNG_OK) { + ERR("%s", lttng_strerror(-handle_ret_code)); + cmd_ret = CMD_FATAL; + goto end; + } + + while ((one_value_str = strtok_r(iter, ",", &iter)) != NULL) { + const bool is_numerical_argument = isdigit(one_value_str[0]); + enum lttng_process_attr_tracker_handle_status status; + enum lttng_tracking_policy policy; + int ret; + char *prettified_arg; + + if (!policy_set) { + status = lttng_process_attr_tracker_handle_get_tracking_policy( + tracker_handle, &policy); + if (status != LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK) { + break; + } + + if (policy != LTTNG_TRACKING_POLICY_INCLUDE_SET) { + status = lttng_process_attr_tracker_handle_set_tracking_policy( + tracker_handle, + LTTNG_TRACKING_POLICY_INCLUDE_SET); + if (status != LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK) { + break; + } + } + policy_set = true; + } + + if (is_numerical_argument) { + const unsigned long one_value_int = + strtoul(one_value_str, NULL, 10); + + if (writer) { + ret = mi_lttng_integral_process_attribute_value( + writer, process_attr, + (int64_t) one_value_int, true); + if (ret) { + cmd_ret = CMD_FATAL; + goto end; + } + } + + switch (process_attr) { + case LTTNG_PROCESS_ATTR_PROCESS_ID: + status = cmd_type == CMD_TRACK ? + lttng_process_attr_process_id_tracker_handle_add_pid( + tracker_handle, + (pid_t) one_value_int) : + lttng_process_attr_process_id_tracker_handle_remove_pid( + tracker_handle, + (pid_t) one_value_int); + break; + case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: + status = cmd_type == CMD_TRACK ? + lttng_process_attr_virtual_process_id_tracker_handle_add_pid( + tracker_handle, + (pid_t) one_value_int) : + lttng_process_attr_virtual_process_id_tracker_handle_remove_pid( + tracker_handle, + (pid_t) one_value_int); + break; + case LTTNG_PROCESS_ATTR_USER_ID: + status = cmd_type == CMD_TRACK ? + lttng_process_attr_user_id_tracker_handle_add_uid( + tracker_handle, + (uid_t) one_value_int) : + lttng_process_attr_user_id_tracker_handle_remove_uid( + tracker_handle, + (uid_t) one_value_int); + break; + case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: + status = cmd_type == CMD_TRACK ? + lttng_process_attr_virtual_user_id_tracker_handle_add_uid( + tracker_handle, + (uid_t) one_value_int) : + lttng_process_attr_virtual_user_id_tracker_handle_remove_uid( + tracker_handle, + (uid_t) one_value_int); + break; + case LTTNG_PROCESS_ATTR_GROUP_ID: + status = cmd_type == CMD_TRACK ? + lttng_process_attr_group_id_tracker_handle_add_gid( + tracker_handle, + (gid_t) one_value_int) : + lttng_process_attr_group_id_tracker_handle_remove_gid( + tracker_handle, + (gid_t) one_value_int); + break; + case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: + status = cmd_type == CMD_TRACK ? + lttng_process_attr_virtual_group_id_tracker_handle_add_gid( + tracker_handle, + (gid_t) one_value_int) : + lttng_process_attr_virtual_group_id_tracker_handle_remove_gid( + tracker_handle, + (gid_t) one_value_int); + break; + default: + abort(); + } + + } else { + if (writer) { + ret = mi_lttng_string_process_attribute_value( + writer, process_attr, + one_value_str, true); + if (ret) { + cmd_ret = CMD_FATAL; + goto end; + } + } + + switch (process_attr) { + case LTTNG_PROCESS_ATTR_USER_ID: + status = cmd_type == CMD_TRACK ? + lttng_process_attr_user_id_tracker_handle_add_user_name( + tracker_handle, + one_value_str) : + lttng_process_attr_user_id_tracker_handle_remove_user_name( + tracker_handle, + one_value_str); + break; + case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: + status = cmd_type == CMD_TRACK ? + lttng_process_attr_virtual_user_id_tracker_handle_add_user_name( + tracker_handle, + one_value_str) : + lttng_process_attr_virtual_user_id_tracker_handle_remove_user_name( + tracker_handle, + one_value_str); + break; + case LTTNG_PROCESS_ATTR_GROUP_ID: + status = cmd_type == CMD_TRACK ? + lttng_process_attr_group_id_tracker_handle_add_group_name( + tracker_handle, + one_value_str) : + lttng_process_attr_group_id_tracker_handle_remove_group_name( + tracker_handle, + one_value_str); + break; + case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: + status = cmd_type == CMD_TRACK ? + lttng_process_attr_virtual_group_id_tracker_handle_add_group_name( + tracker_handle, + one_value_str) : + lttng_process_attr_virtual_group_id_tracker_handle_remove_group_name( + tracker_handle, + one_value_str); + break; + default: + ERR("%s is not a valid %s value; expected an integer", + one_value_str, + lttng_process_attr_to_string( + process_attr)); + cmd_ret = CMD_FATAL; + goto end; + } + } + + ret = asprintf(&prettified_arg, + is_numerical_argument ? "%s" : "`%s`", + one_value_str); + if (ret < 0) { + PERROR("Failed to format argument `%s`", one_value_str); + cmd_ret = CMD_FATAL; + goto end; + } + + switch (status) { + case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK: + if (cmd_type == CMD_TRACK) { + MSG("Added %s to the %s tracker inclusion set", + one_value_str, + lttng_process_attr_to_string( + process_attr)); + } else { + MSG("Removed %s from the %s tracker inclusion set", + one_value_str, + lttng_process_attr_to_string( + process_attr)); + } + break; + case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_SESSION_DOES_NOT_EXIST: + ERR("Session `%s` not found", session_name); + break; + case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_EXISTS: + WARN("%s is already in the %s inclusion set", + prettified_arg, + lttng_process_attr_to_string( + process_attr)); + break; + case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_MISSING: + WARN("%s is not in the %s the inclusion set", + prettified_arg, + lttng_process_attr_to_string( + process_attr)); + break; + case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_USER_NOT_FOUND: + ERR("User %s was not found", prettified_arg); + cmd_ret = CMD_ERROR; + break; + case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_GROUP_NOT_FOUND: + ERR("Group %s was not found", prettified_arg); + cmd_ret = CMD_ERROR; + break; + default: + ERR("Unknown error encountered while %s %s %s %s tracker's inclusion set", + cmd_type == CMD_TRACK ? "adding" : + "removing", + lttng_process_attr_to_string( + process_attr), + prettified_arg, + cmd_type == CMD_TRACK ? "to" : "from"); + cmd_ret = CMD_FATAL; + break; + } + free(prettified_arg); + + if (writer) { + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_success, + cmd_ret == CMD_SUCCESS); + + if (ret) { + cmd_ret = CMD_FATAL; + } else { + ret = mi_lttng_writer_close_element(writer); + cmd_ret = ret == 0 ? cmd_ret : CMD_FATAL; + } + } + } +end: + free(args); + lttng_process_attr_tracker_handle_destroy(tracker_handle); + return cmd_ret; +} + +static enum cmd_error_code run_command(enum cmd_type cmd_type, + const char *session_name, + const struct process_attr_command_args *command_args, + struct mi_writer *writer) +{ + const enum lttng_domain_type domain_type = + opt_kernel ? LTTNG_DOMAIN_KERNEL : LTTNG_DOMAIN_UST; + enum cmd_error_code cmd_ret = CMD_SUCCESS; + unsigned int i; + const unsigned int string_arg_count = + lttng_dynamic_pointer_array_get_count( + &command_args->string_args); + enum lttng_process_attr process_attr = command_args->process_attr; + + if (opt_userspace) { + /* + * Check that this process attribute can be tracked + * in the user space domain. Backward-compatibility + * changes are be applied to process_attr as needed. + */ + if (!ust_process_attr_supported(&process_attr)) { + cmd_ret = CMD_ERROR; + goto end; + } + } + + if (writer) { + /* Open tracker and trackers elements */ + const int ret = mi_lttng_process_attribute_tracker_open( + writer, process_attr); + if (ret) { + cmd_ret = CMD_FATAL; + goto end; + } + } + + if (command_args->all) { + cmd_ret = run_command_all(cmd_type, session_name, domain_type, + process_attr, writer); + } else { + bool error_occurred = false; + + for (i = 0; i < string_arg_count; i++) { + const char *arg = (const char *) lttng_dynamic_pointer_array_get_pointer( + &command_args->string_args, i); + + cmd_ret = run_command_string(cmd_type, session_name, + domain_type, process_attr, arg, writer); + if (cmd_ret != CMD_SUCCESS) { + error_occurred = true; + if (cmd_ret == CMD_FATAL) { + break; + } + goto end; + } + } + if (error_occurred) { + cmd_ret = CMD_ERROR; + } + } + + if (writer) { + /* Close tracker and trackers elements */ + const int ret = mi_lttng_close_multi_element( + writer, 2); + if (ret) { + cmd_ret = CMD_FATAL; + goto end; + } + } +end: + return cmd_ret; +} + +/* + * Add/remove tracker to/from session. + */ +static int cmd_track_untrack(enum cmd_type cmd_type, + int argc, + const char **argv, + const char *help_msg) +{ + int opt, ret = 0; + bool sub_command_failed = false; + bool opt_all = false; + unsigned int selected_process_attr_tracker_count = 0; + const unsigned int command_count = + sizeof(process_attr_commands) / + sizeof(struct process_attr_command_args); + enum cmd_error_code command_ret = CMD_SUCCESS; + static poptContext pc; + char *session_name = NULL; + const char *leftover = NULL; + struct mi_writer *writer = NULL; + size_t i; + + for (i = 0; i < command_count; i++) { + process_attr_command_init(&process_attr_commands[i], (lttng_process_attr) i); + } + + if (argc < 1) { + command_ret = CMD_ERROR; + goto end; + } + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + case OPT_SESSION: + break; + case OPT_PID: + case OPT_VPID: + case OPT_UID: + case OPT_VUID: + case OPT_GID: + case OPT_VGID: + /* See OPT_ enum declaration comment. */ + opt--; + selected_process_attr_tracker_count++; + process_attr_commands[opt].requested = true; + if (!opt_str_arg) { + continue; + } + ret = lttng_dynamic_pointer_array_add_pointer( + &process_attr_commands[opt].string_args, + opt_str_arg); + if (ret) { + ERR("Allocation failed while parsing command arguments"); + command_ret = CMD_ERROR; + goto end; + } + break; + case OPT_ALL: + opt_all = true; + break; + default: + command_ret = CMD_UNDEFINED; + goto end; + } + } + + ret = print_missing_or_multiple_domains( + opt_kernel + opt_userspace, false); + if (ret) { + command_ret = CMD_ERROR; + goto end; + } + + if (selected_process_attr_tracker_count == 0) { + ERR("At least one process attribute must be specified"); + command_ret = CMD_ERROR; + goto end; + } + if (opt_all) { + /* + * Only one process attribute tracker was specified; find it + * and set it in 'all' mode. + */ + for (i = 0; i < command_count; i++) { + if (!process_attr_commands[i].requested) { + continue; + } + process_attr_commands[i].all = true; + if (lttng_dynamic_pointer_array_get_count( + &process_attr_commands[i] + .string_args)) { + ERR("The --all option cannot be used with a list of process attribute values"); + command_ret = CMD_ERROR; + goto end; + } + } + } else { + for (i = 0; i < command_count; i++) { + if (!process_attr_commands[i].requested) { + continue; + } + if (lttng_dynamic_pointer_array_get_count( + &process_attr_commands[i] + .string_args) == 0) { + ERR("No process attribute value specified for %s tracker", + get_capitalized_process_attr_str( + process_attr_commands[i] + .process_attr)); + command_ret = CMD_ERROR; + goto end; + } + } + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + command_ret = CMD_ERROR; + goto end; + } + } else { + session_name = opt_session_name; + } + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + command_ret = CMD_ERROR; + goto end; + } + + /* Mi check */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + command_ret = CMD_ERROR; + goto end; + } + } + + if (writer) { + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + get_mi_element_command(cmd_type)); + if (ret) { + command_ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + command_ret = CMD_ERROR; + goto end; + } + + ret = mi_lttng_trackers_open(writer); + if (ret) { + goto end; + } + } + + /* Execute sub-commands. */ + for (i = 0; i < command_count; i++) { + if (!process_attr_commands[i].requested) { + continue; + } + command_ret = run_command(cmd_type, session_name, + &process_attr_commands[i], writer); + if (command_ret != CMD_SUCCESS) { + sub_command_failed = true; + if (command_ret == CMD_FATAL) { + break; + } + } + } + + /* Mi closing */ + if (writer) { + /* Close trackers and output elements */ + ret = mi_lttng_close_multi_element(writer, 2); + if (ret) { + command_ret = CMD_ERROR; + goto end; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, + !sub_command_failed); + if (ret) { + command_ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + command_ret = CMD_ERROR; + goto end; + } + } + +end: + if (!opt_session_name) { + free(session_name); + } + + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + command_ret = CMD_ERROR; + } + + for (i = 0; i < command_count; i++) { + process_attr_command_fini(&process_attr_commands[i]); + } + + poptFreeContext(pc); + return (int) command_ret; +} + +int cmd_track(int argc, const char **argv) +{ + static const char *help_msg = +#ifdef LTTNG_EMBED_HELP +#include +#else + NULL +#endif + ; + + return cmd_track_untrack(CMD_TRACK, argc, argv, help_msg); +} + +int cmd_untrack(int argc, const char **argv) +{ + static const char *help_msg = +#ifdef LTTNG_EMBED_HELP +#include +#else + NULL +#endif + ; + + return cmd_track_untrack(CMD_UNTRACK, argc, argv, help_msg); +} diff --git a/src/bin/lttng/commands/version.c b/src/bin/lttng/commands/version.c deleted file mode 100644 index 9f606bf44..000000000 --- a/src/bin/lttng/commands/version.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "../command.h" -#include "version.h" - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, -}; - -static const char *lttng_license = "lttng is free software and under the GPL license and part LGPL"; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * create_version - */ -static void create_version(struct mi_lttng_version *version) -{ - strncpy(version->version, VERSION, NAME_MAX); - version->version_major = VERSION_MAJOR; - version->version_minor = VERSION_MINOR; - version->version_patchlevel = VERSION_PATCHLEVEL; - strncpy(version->version_commit, GIT_VERSION, NAME_MAX); - strncpy(version->version_name, VERSION_NAME, NAME_MAX); - strncpy(version->package_url, PACKAGE_URL, NAME_MAX); -} - -/* - * Print the machine interface output of this command. - */ -static int print_mi(void) -{ - int ret = CMD_SUCCESS; - struct mi_writer *writer = NULL; - struct mi_lttng_version version; - - create_version(&version); - - writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); - if (!writer) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - /* Open the command element */ - ret = mi_lttng_writer_command_open(writer, - mi_lttng_element_command_version); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - /* Beginning of output */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_output); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - /* Print the machine interface of version */ - ret = mi_lttng_version(writer, &version, - VERSION_DESCRIPTION, lttng_license); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - /* Close the output element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - ret = CMD_ERROR; - goto error; - } - - /* Close the command */ - ret = mi_lttng_writer_command_close(writer); - if (ret) { - ret = CMD_ERROR; - } - -error: - /* Cleanup */ - if (writer && mi_lttng_writer_destroy(writer)) { - /* Preserve original error code */ - ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; - } - -end: - return ret; -} - -/* - * cmd_version - */ -int cmd_version(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS; - static poptContext pc; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - default: - ret = CMD_UNDEFINED; - goto end; - } - } - - if (lttng_opt_mi) { - ret = print_mi(); - } else { - MSG("lttng version " VERSION " - " VERSION_NAME "%s", - GIT_VERSION[0] == '\0' ? "" : " - " GIT_VERSION); - MSG("\n" VERSION_DESCRIPTION "\n"); - MSG("Web site: https://lttng.org"); - MSG("\n%s", lttng_license); - if (EXTRA_VERSION_NAME[0] != '\0') { - MSG("\nExtra version name: " EXTRA_VERSION_NAME); - } - if (EXTRA_VERSION_DESCRIPTION[0] != '\0') { - MSG("\nExtra version description:\n\t" EXTRA_VERSION_DESCRIPTION); - } - if (EXTRA_VERSION_PATCHES[0] != '\0') { - MSG("\nExtra version patches:\n\t" EXTRA_VERSION_PATCHES); - } - } - -end: - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/version.cpp b/src/bin/lttng/commands/version.cpp new file mode 100644 index 000000000..ccc7f6633 --- /dev/null +++ b/src/bin/lttng/commands/version.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2011 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../command.h" +#include "version.h" + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, +}; + +static const char *lttng_license = "lttng is free software and under the GPL license and part LGPL"; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * create_version + */ +static void create_version(struct mi_lttng_version_data *version) +{ + strncpy(version->version, VERSION, NAME_MAX); + version->version_major = VERSION_MAJOR; + version->version_minor = VERSION_MINOR; + version->version_patchlevel = VERSION_PATCHLEVEL; + strncpy(version->version_commit, GIT_VERSION, NAME_MAX); + strncpy(version->version_name, VERSION_NAME, NAME_MAX); + strncpy(version->package_url, PACKAGE_URL, NAME_MAX); +} + +/* + * Print the machine interface output of this command. + */ +static int print_mi(void) +{ + int ret = CMD_SUCCESS; + struct mi_writer *writer = NULL; + struct mi_lttng_version_data version; + + create_version(&version); + + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* Open the command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_version); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* Beginning of output */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* Print the machine interface of version */ + ret = mi_lttng_version(writer, &version, + VERSION_DESCRIPTION, lttng_license); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* Close the output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* Close the command */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + } + +error: + /* Cleanup */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; + } + +end: + return ret; +} + +/* + * cmd_version + */ +int cmd_version(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS; + static poptContext pc; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + default: + ret = CMD_UNDEFINED; + goto end; + } + } + + if (lttng_opt_mi) { + ret = print_mi(); + } else { + MSG("lttng version " VERSION " - " VERSION_NAME "%s", + GIT_VERSION[0] == '\0' ? "" : " - " GIT_VERSION); + MSG("\n" VERSION_DESCRIPTION "\n"); + MSG("Web site: https://lttng.org"); + MSG("\n%s", lttng_license); + if (EXTRA_VERSION_NAME[0] != '\0') { + MSG("\nExtra version name: " EXTRA_VERSION_NAME); + } + if (EXTRA_VERSION_DESCRIPTION[0] != '\0') { + MSG("\nExtra version description:\n\t" EXTRA_VERSION_DESCRIPTION); + } + if (EXTRA_VERSION_PATCHES[0] != '\0') { + MSG("\nExtra version patches:\n\t" EXTRA_VERSION_PATCHES); + } + } + +end: + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/commands/view.c b/src/bin/lttng/commands/view.c deleted file mode 100644 index a2623fe49..000000000 --- a/src/bin/lttng/commands/view.c +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include -#include "../command.h" - -static char *opt_session_name; -static char *opt_viewer; -static char *opt_trace_path; - -#ifdef LTTNG_EMBED_HELP -static const char help_msg[] = -#include -; -#endif - -enum { - OPT_HELP = 1, - OPT_LIST_OPTIONS, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, - {"viewer", 'e', POPT_ARG_STRING, &opt_viewer, 0, 0, 0}, - {"trace-path", 't', POPT_ARG_STRING, &opt_trace_path, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* Is the session we are trying to view is in live mode. */ -static int session_live_mode; - -/* - * Build the live path we need for the lttng live view. - */ -static char *build_live_path(char *session_name) -{ - int ret; - char *path = NULL; - char hostname[LTTNG_HOST_NAME_MAX]; - - ret = gethostname(hostname, sizeof(hostname)); - if (ret < 0) { - PERROR("gethostname"); - goto error; - } - - ret = asprintf(&path, "net://localhost/host/%s/%s", hostname, - session_name); - if (ret < 0) { - PERROR("asprintf live path"); - goto error; - } - -error: - return path; -} - -/* - * Exec viewer if found and use session name path. - */ -static int view_trace(void) -{ - int ret; - char *session_name, *trace_path = NULL; - struct lttng_session *sessions = NULL; - bool free_trace_path = false; - - /* - * Safety net. If lttng is suid at some point for *any* useless reasons, - * this prevent any bad execution of binaries. - */ - if (getuid() != 0) { - if (getuid() != geteuid()) { - ERR("UID does not match effective UID."); - ret = CMD_ERROR; - goto error; - } else if (getgid() != getegid()) { - ERR("GID does not match effective GID."); - ret = CMD_ERROR; - goto error; - } - } - - /* User define trace path override the session name */ - if (opt_trace_path) { - session_name = NULL; - } else if(opt_session_name == NULL) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = CMD_ERROR; - goto error; - } - } else { - session_name = opt_session_name; - } - - DBG("Viewing trace for session %s", session_name); - - if (session_name) { - int i, count, found = 0; - - /* Getting all sessions */ - count = lttng_list_sessions(&sessions); - if (count < 0) { - ERR("Unable to list sessions. Session name %s not found.", - session_name); - MSG("Is there a session daemon running?"); - ret = CMD_ERROR; - goto free_error; - } - - /* Find our session listed by the session daemon */ - for (i = 0; i < count; i++) { - if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) { - found = 1; - break; - } - } - - if (!found) { - MSG("Session name %s not found", session_name); - ret = CMD_ERROR; - goto free_sessions; - } - - session_live_mode = sessions[i].live_timer_interval; - - DBG("Session live mode set to %d", session_live_mode); - - if (sessions[i].enabled && !session_live_mode) { - WARN("Session %s is running. Please stop it before reading it.", - session_name); - ret = CMD_ERROR; - goto free_sessions; - } - - /* If the timer interval is set we are in live mode. */ - if (session_live_mode) { - trace_path = build_live_path(session_name); - if (!trace_path) { - ret = CMD_ERROR; - goto free_sessions; - } - free_trace_path = true; - } else { - /* Get file system session path. */ - trace_path = sessions[i].path; - } - } else { - trace_path = opt_trace_path; - } - - MSG("Trace directory: %s\n", trace_path); - - ret = spawn_viewer(trace_path, opt_viewer, session_live_mode); - if (ret < 0) { - /* Don't set ret so lttng can interpret the sessiond error. */ - goto free_sessions; - } - -free_sessions: - if (session_live_mode && free_trace_path) { - free(trace_path); - } - free(sessions); -free_error: - if (opt_session_name == NULL) { - free(session_name); - } -error: - return ret; -} - -/* - * The 'view ' first level command - */ -int cmd_view(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS; - static poptContext pc; - const char *leftover = NULL; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - if (lttng_opt_mi) { - WARN("mi does not apply to view command"); - } - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - SHOW_HELP(); - goto end; - case OPT_LIST_OPTIONS: - list_cmd_options(stdout, long_options); - goto end; - default: - ret = CMD_UNDEFINED; - goto end; - } - } - - opt_session_name = (char*) poptGetArg(pc); - - leftover = poptGetArg(pc); - if (leftover) { - ERR("Unknown argument: %s", leftover); - ret = CMD_ERROR; - goto end; - } - - ret = view_trace(); - -end: - poptFreeContext(pc); - return ret; -} diff --git a/src/bin/lttng/commands/view.cpp b/src/bin/lttng/commands/view.cpp new file mode 100644 index 000000000..a2623fe49 --- /dev/null +++ b/src/bin/lttng/commands/view.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2011 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include +#include "../command.h" + +static char *opt_session_name; +static char *opt_viewer; +static char *opt_trace_path; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {"viewer", 'e', POPT_ARG_STRING, &opt_viewer, 0, 0, 0}, + {"trace-path", 't', POPT_ARG_STRING, &opt_trace_path, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* Is the session we are trying to view is in live mode. */ +static int session_live_mode; + +/* + * Build the live path we need for the lttng live view. + */ +static char *build_live_path(char *session_name) +{ + int ret; + char *path = NULL; + char hostname[LTTNG_HOST_NAME_MAX]; + + ret = gethostname(hostname, sizeof(hostname)); + if (ret < 0) { + PERROR("gethostname"); + goto error; + } + + ret = asprintf(&path, "net://localhost/host/%s/%s", hostname, + session_name); + if (ret < 0) { + PERROR("asprintf live path"); + goto error; + } + +error: + return path; +} + +/* + * Exec viewer if found and use session name path. + */ +static int view_trace(void) +{ + int ret; + char *session_name, *trace_path = NULL; + struct lttng_session *sessions = NULL; + bool free_trace_path = false; + + /* + * Safety net. If lttng is suid at some point for *any* useless reasons, + * this prevent any bad execution of binaries. + */ + if (getuid() != 0) { + if (getuid() != geteuid()) { + ERR("UID does not match effective UID."); + ret = CMD_ERROR; + goto error; + } else if (getgid() != getegid()) { + ERR("GID does not match effective GID."); + ret = CMD_ERROR; + goto error; + } + } + + /* User define trace path override the session name */ + if (opt_trace_path) { + session_name = NULL; + } else if(opt_session_name == NULL) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = CMD_ERROR; + goto error; + } + } else { + session_name = opt_session_name; + } + + DBG("Viewing trace for session %s", session_name); + + if (session_name) { + int i, count, found = 0; + + /* Getting all sessions */ + count = lttng_list_sessions(&sessions); + if (count < 0) { + ERR("Unable to list sessions. Session name %s not found.", + session_name); + MSG("Is there a session daemon running?"); + ret = CMD_ERROR; + goto free_error; + } + + /* Find our session listed by the session daemon */ + for (i = 0; i < count; i++) { + if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) { + found = 1; + break; + } + } + + if (!found) { + MSG("Session name %s not found", session_name); + ret = CMD_ERROR; + goto free_sessions; + } + + session_live_mode = sessions[i].live_timer_interval; + + DBG("Session live mode set to %d", session_live_mode); + + if (sessions[i].enabled && !session_live_mode) { + WARN("Session %s is running. Please stop it before reading it.", + session_name); + ret = CMD_ERROR; + goto free_sessions; + } + + /* If the timer interval is set we are in live mode. */ + if (session_live_mode) { + trace_path = build_live_path(session_name); + if (!trace_path) { + ret = CMD_ERROR; + goto free_sessions; + } + free_trace_path = true; + } else { + /* Get file system session path. */ + trace_path = sessions[i].path; + } + } else { + trace_path = opt_trace_path; + } + + MSG("Trace directory: %s\n", trace_path); + + ret = spawn_viewer(trace_path, opt_viewer, session_live_mode); + if (ret < 0) { + /* Don't set ret so lttng can interpret the sessiond error. */ + goto free_sessions; + } + +free_sessions: + if (session_live_mode && free_trace_path) { + free(trace_path); + } + free(sessions); +free_error: + if (opt_session_name == NULL) { + free(session_name); + } +error: + return ret; +} + +/* + * The 'view ' first level command + */ +int cmd_view(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS; + static poptContext pc; + const char *leftover = NULL; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + if (lttng_opt_mi) { + WARN("mi does not apply to view command"); + } + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + default: + ret = CMD_UNDEFINED; + goto end; + } + } + + opt_session_name = (char*) poptGetArg(pc); + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + ret = CMD_ERROR; + goto end; + } + + ret = view_trace(); + +end: + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/conf.c b/src/bin/lttng/conf.c deleted file mode 100644 index 4079d6ed9..000000000 --- a/src/bin/lttng/conf.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "conf.h" - -/* - * Returns the path with '/CONFIG_FILENAME' added to it; - * path will be NULL if an error occurs. - */ -char *config_get_file_path(const char *path) -{ - int ret; - char *file_path; - - ret = asprintf(&file_path, "%s/%s", path, CONFIG_FILENAME); - if (ret < 0) { - ERR("Fail allocating config file path"); - file_path = NULL; - } - - return file_path; -} - -/* - * Returns an open FILE pointer to the config file; - * on error, NULL is returned. - */ -static FILE *open_config(const char *path, const char *mode) -{ - FILE *fp = NULL; - char *file_path; - - file_path = config_get_file_path(path); - if (file_path == NULL) { - goto error; - } - - fp = fopen(file_path, mode); - if (fp == NULL) { - goto error; - } - -error: - free(file_path); - return fp; -} - -/* - * Creates the empty config file at the path. - * On success, returns 0; - * on error, returns -1. - */ -static int create_config_file(const char *path) -{ - int ret; - FILE *fp; - - fp = open_config(path, "w+"); - if (fp == NULL) { - ERR("Unable to create config file"); - ret = -1; - goto error; - } - - ret = fclose(fp); - -error: - return ret; -} - -/* - * Append data to the config file in file_path - * On success, returns 0; - * on error, returns -1. - */ -static int write_config(const char *file_path, size_t size, char *data) -{ - FILE *fp; - size_t len; - int ret = 0; - - fp = open_config(file_path, "a"); - if (fp == NULL) { - ret = -1; - goto end; - } - - /* Write session name into config file */ - len = fwrite(data, size, 1, fp); - if (len != 1) { - ret = -1; - } - if (fclose(fp)) { - PERROR("close write_config"); - } -end: - return ret; -} - -/* - * Destroys directory config and file config. - */ -void config_destroy(const char *path) -{ - int ret; - char *config_path; - - config_path = config_get_file_path(path); - if (config_path == NULL) { - return; - } - - if (!config_exists(config_path)) { - goto end; - } - - DBG("Removing %s\n", config_path); - ret = remove(config_path); - if (ret < 0) { - PERROR("remove config file"); - } -end: - free(config_path); -} - -/* - * Destroys the default config - */ -void config_destroy_default(void) -{ - const char *path = utils_get_home_dir(); - if (path == NULL) { - return; - } - config_destroy(path); -} - -/* - * Returns 1 if config exists, 0 otherwise - */ -int config_exists(const char *path) -{ - int ret; - struct stat info; - - ret = stat(path, &info); - if (ret < 0) { - return 0; - } - return S_ISREG(info.st_mode) || S_ISDIR(info.st_mode); -} - -static -int _config_read_session_name(const char *path, char **name) -{ - int ret = 0; - FILE *fp; - char var[NAME_MAX], *session_name; - -#if (NAME_MAX == 255) -#define NAME_MAX_SCANF_IS_A_BROKEN_API "254" -#endif - - session_name = zmalloc(NAME_MAX); - if (session_name == NULL) { - ret = -ENOMEM; - ERR("Out of memory"); - goto error; - } - - fp = open_config(path, "r"); - if (fp == NULL) { - ret = -ENOENT; - goto error; - } - - while (!feof(fp)) { - if ((ret = fscanf(fp, "%" NAME_MAX_SCANF_IS_A_BROKEN_API - "[^'=']=%" NAME_MAX_SCANF_IS_A_BROKEN_API "s\n", - var, session_name)) != 2) { - if (ret == -1) { - ERR("Missing session=NAME in config file."); - goto error_close; - } - continue; - } - - if (strcmp(var, "session") == 0) { - goto found; - } - } - -error_close: - if (fclose(fp) < 0) { - PERROR("close config read session name"); - } -error: - free(session_name); - return ret; -found: - *name = session_name; - if (fclose(fp) < 0) { - PERROR("close config read session name found"); - } - return ret; -} - -/* - * Returns the session name from the config file. - * - * The caller is responsible for freeing the returned string. - * On error, NULL is returned. - */ -char *config_read_session_name(const char *path) -{ - int ret; - char *name = NULL; - - ret = _config_read_session_name(path, &name); - if (ret == -ENOENT) { - const char *home_dir = utils_get_home_dir(); - - ERR("Can't find valid lttng config %s/.lttngrc", home_dir); - MSG("Did you create a session? (lttng create )"); - } - - return name; -} - -/* - * Returns the session name from the config file. (no warnings/errors emitted) - * - * The caller is responsible for freeing the returned string. - * On error, NULL is returned. - */ -char *config_read_session_name_quiet(const char *path) -{ - char *name = NULL; - - (void) _config_read_session_name(path, &name); - return name; -} - -/* - * Write session name option to the config file. - * On success, returns 0; - * on error, returns -1. - */ -int config_add_session_name(const char *path, const char *name) -{ - int ret; - const char *attr = "session="; - /* Max name len accepted plus attribute's len and the NULL byte. */ - char session_name[NAME_MAX + strlen(attr) + 1]; - - /* - * With GNU C < 2.1, snprintf returns -1 if the target buffer is too small; - * With GNU C >= 2.1, snprintf returns the required size (excluding closing null) - */ - ret = snprintf(session_name, sizeof(session_name), "%s%s\n", attr, name); - if (ret < 0) { - ret = -1; - goto error; - } - ret = write_config(path, ret, session_name); -error: - return ret; -} - -/* - * Init configuration directory and file. - * On success, returns 0; - * on error, returns -1. - */ -int config_init(const char *session_name) -{ - int ret; - const char *path; - - path = utils_get_home_dir(); - if (path == NULL) { - ret = -1; - goto error; - } - - /* Create default config file */ - ret = create_config_file(path); - if (ret < 0) { - goto error; - } - - ret = config_add_session_name(path, session_name); - if (ret < 0) { - goto error; - } - - DBG("Init config session in %s", path); - -error: - return ret; -} diff --git a/src/bin/lttng/conf.cpp b/src/bin/lttng/conf.cpp new file mode 100644 index 000000000..e347c4b3a --- /dev/null +++ b/src/bin/lttng/conf.cpp @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2011 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "conf.h" + +/* + * Returns the path with '/CONFIG_FILENAME' added to it; + * path will be NULL if an error occurs. + */ +char *config_get_file_path(const char *path) +{ + int ret; + char *file_path; + + ret = asprintf(&file_path, "%s/%s", path, CONFIG_FILENAME); + if (ret < 0) { + ERR("Fail allocating config file path"); + file_path = NULL; + } + + return file_path; +} + +/* + * Returns an open FILE pointer to the config file; + * on error, NULL is returned. + */ +static FILE *open_config(const char *path, const char *mode) +{ + FILE *fp = NULL; + char *file_path; + + file_path = config_get_file_path(path); + if (file_path == NULL) { + goto error; + } + + fp = fopen(file_path, mode); + if (fp == NULL) { + goto error; + } + +error: + free(file_path); + return fp; +} + +/* + * Creates the empty config file at the path. + * On success, returns 0; + * on error, returns -1. + */ +static int create_config_file(const char *path) +{ + int ret; + FILE *fp; + + fp = open_config(path, "w+"); + if (fp == NULL) { + ERR("Unable to create config file"); + ret = -1; + goto error; + } + + ret = fclose(fp); + +error: + return ret; +} + +/* + * Append data to the config file in file_path + * On success, returns 0; + * on error, returns -1. + */ +static int write_config(const char *file_path, size_t size, char *data) +{ + FILE *fp; + size_t len; + int ret = 0; + + fp = open_config(file_path, "a"); + if (fp == NULL) { + ret = -1; + goto end; + } + + /* Write session name into config file */ + len = fwrite(data, size, 1, fp); + if (len != 1) { + ret = -1; + } + if (fclose(fp)) { + PERROR("close write_config"); + } +end: + return ret; +} + +/* + * Destroys directory config and file config. + */ +void config_destroy(const char *path) +{ + int ret; + char *config_path; + + config_path = config_get_file_path(path); + if (config_path == NULL) { + return; + } + + if (!config_exists(config_path)) { + goto end; + } + + DBG("Removing %s\n", config_path); + ret = remove(config_path); + if (ret < 0) { + PERROR("remove config file"); + } +end: + free(config_path); +} + +/* + * Destroys the default config + */ +void config_destroy_default(void) +{ + const char *path = utils_get_home_dir(); + if (path == NULL) { + return; + } + config_destroy(path); +} + +/* + * Returns 1 if config exists, 0 otherwise + */ +int config_exists(const char *path) +{ + int ret; + struct stat info; + + ret = stat(path, &info); + if (ret < 0) { + return 0; + } + return S_ISREG(info.st_mode) || S_ISDIR(info.st_mode); +} + +static +int _config_read_session_name(const char *path, char **name) +{ + int ret = 0; + FILE *fp; + char var[NAME_MAX], *session_name; + +#if (NAME_MAX == 255) +#define NAME_MAX_SCANF_IS_A_BROKEN_API "254" +#endif + + session_name = (char *) zmalloc(NAME_MAX); + if (session_name == NULL) { + ret = -ENOMEM; + ERR("Out of memory"); + goto error; + } + + fp = open_config(path, "r"); + if (fp == NULL) { + ret = -ENOENT; + goto error; + } + + while (!feof(fp)) { + if ((ret = fscanf(fp, "%" NAME_MAX_SCANF_IS_A_BROKEN_API + "[^'=']=%" NAME_MAX_SCANF_IS_A_BROKEN_API "s\n", + var, session_name)) != 2) { + if (ret == -1) { + ERR("Missing session=NAME in config file."); + goto error_close; + } + continue; + } + + if (strcmp(var, "session") == 0) { + goto found; + } + } + +error_close: + if (fclose(fp) < 0) { + PERROR("close config read session name"); + } +error: + free(session_name); + return ret; +found: + *name = session_name; + if (fclose(fp) < 0) { + PERROR("close config read session name found"); + } + return ret; +} + +/* + * Returns the session name from the config file. + * + * The caller is responsible for freeing the returned string. + * On error, NULL is returned. + */ +char *config_read_session_name(const char *path) +{ + int ret; + char *name = NULL; + + ret = _config_read_session_name(path, &name); + if (ret == -ENOENT) { + const char *home_dir = utils_get_home_dir(); + + ERR("Can't find valid lttng config %s/.lttngrc", home_dir); + MSG("Did you create a session? (lttng create )"); + } + + return name; +} + +/* + * Returns the session name from the config file. (no warnings/errors emitted) + * + * The caller is responsible for freeing the returned string. + * On error, NULL is returned. + */ +char *config_read_session_name_quiet(const char *path) +{ + char *name = NULL; + + (void) _config_read_session_name(path, &name); + return name; +} + +/* + * Write session name option to the config file. + * On success, returns 0; + * on error, returns -1. + */ +int config_add_session_name(const char *path, const char *name) +{ + int ret; + const char *attr = "session="; + /* Max name len accepted plus attribute's len and the NULL byte. */ + char session_name[NAME_MAX + strlen(attr) + 1]; + + /* + * With GNU C < 2.1, snprintf returns -1 if the target buffer is too small; + * With GNU C >= 2.1, snprintf returns the required size (excluding closing null) + */ + ret = snprintf(session_name, sizeof(session_name), "%s%s\n", attr, name); + if (ret < 0) { + ret = -1; + goto error; + } + ret = write_config(path, ret, session_name); +error: + return ret; +} + +/* + * Init configuration directory and file. + * On success, returns 0; + * on error, returns -1. + */ +int config_init(const char *session_name) +{ + int ret; + const char *path; + + path = utils_get_home_dir(); + if (path == NULL) { + ret = -1; + goto error; + } + + /* Create default config file */ + ret = create_config_file(path); + if (ret < 0) { + goto error; + } + + ret = config_add_session_name(path, session_name); + if (ret < 0) { + goto error; + } + + DBG("Init config session in %s", path); + +error: + return ret; +} diff --git a/src/bin/lttng/loglevel.c b/src/bin/lttng/loglevel.c deleted file mode 100644 index 054510e85..000000000 --- a/src/bin/lttng/loglevel.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright (C) 2021 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include "loglevel.h" -#include -#include -#include - -struct loglevel_name_value { - const char *name; - int value; -}; - -static -const struct loglevel_name_value loglevel_values[] = { - { .name = "EMERG", .value = LTTNG_LOGLEVEL_EMERG }, - { .name = "TRACE_EMERG", .value = LTTNG_LOGLEVEL_EMERG }, - { .name = "ALERT", .value = LTTNG_LOGLEVEL_ALERT }, - { .name = "TRACE_ALERT", .value = LTTNG_LOGLEVEL_ALERT }, - { .name = "CRIT", .value = LTTNG_LOGLEVEL_CRIT }, - { .name = "TRACE_CRIT", .value = LTTNG_LOGLEVEL_CRIT }, - { .name = "ERR", .value = LTTNG_LOGLEVEL_ERR }, - { .name = "TRACE_ERR", .value = LTTNG_LOGLEVEL_ERR }, - { .name = "WARNING", .value = LTTNG_LOGLEVEL_WARNING }, - { .name = "TRACE_WARNING", .value = LTTNG_LOGLEVEL_WARNING }, - { .name = "NOTICE", .value = LTTNG_LOGLEVEL_NOTICE }, - { .name = "TRACE_NOTICE", .value = LTTNG_LOGLEVEL_NOTICE }, - { .name = "INFO", .value = LTTNG_LOGLEVEL_INFO }, - { .name = "TRACE_INFO", .value = LTTNG_LOGLEVEL_INFO }, - { .name = "DEBUG_SYSTEM", .value = LTTNG_LOGLEVEL_DEBUG_SYSTEM }, - { .name = "TRACE_DEBUG_SYSTEM", .value = LTTNG_LOGLEVEL_DEBUG_SYSTEM }, - { .name = "SYSTEM", .value = LTTNG_LOGLEVEL_DEBUG_SYSTEM }, - { .name = "DEBUG_PROGRAM", .value = LTTNG_LOGLEVEL_DEBUG_PROGRAM }, - { .name = "TRACE_DEBUG_PROGRAM", .value = LTTNG_LOGLEVEL_DEBUG_PROGRAM }, - { .name = "PROGRAM", .value = LTTNG_LOGLEVEL_DEBUG_PROGRAM }, - { .name = "DEBUG_PROCESS", .value = LTTNG_LOGLEVEL_DEBUG_PROCESS }, - { .name = "TRACE_DEBUG_PROCESS", .value = LTTNG_LOGLEVEL_DEBUG_PROCESS }, - { .name = "PROCESS", .value = LTTNG_LOGLEVEL_DEBUG_PROCESS }, - { .name = "DEBUG_MODULE", .value = LTTNG_LOGLEVEL_DEBUG_MODULE }, - { .name = "TRACE_DEBUG_MODULE", .value = LTTNG_LOGLEVEL_DEBUG_MODULE }, - { .name = "MODULE", .value = LTTNG_LOGLEVEL_DEBUG_MODULE }, - { .name = "DEBUG_UNIT", .value = LTTNG_LOGLEVEL_DEBUG_UNIT }, - { .name = "TRACE_DEBUG_UNIT", .value = LTTNG_LOGLEVEL_DEBUG_UNIT }, - { .name = "UNIT", .value = LTTNG_LOGLEVEL_DEBUG_UNIT }, - { .name = "DEBUG_FUNCTION", .value = LTTNG_LOGLEVEL_DEBUG_FUNCTION }, - { .name = "TRACE_DEBUG_FUNCTION", .value = LTTNG_LOGLEVEL_DEBUG_FUNCTION }, - { .name = "FUNCTION", .value = LTTNG_LOGLEVEL_DEBUG_FUNCTION }, - { .name = "DEBUG_LINE", .value = LTTNG_LOGLEVEL_DEBUG_LINE }, - { .name = "TRACE_DEBUG_LINE", .value = LTTNG_LOGLEVEL_DEBUG_LINE }, - { .name = "LINE", .value = LTTNG_LOGLEVEL_DEBUG_LINE }, - { .name = "DEBUG", .value = LTTNG_LOGLEVEL_DEBUG }, - { .name = "TRACE_DEBUG", .value = LTTNG_LOGLEVEL_DEBUG }, -}; - -static -const struct loglevel_name_value loglevel_log4j_values[] = { - { .name = "OFF", .value = LTTNG_LOGLEVEL_LOG4J_OFF }, - { .name = "LOG4J_OFF", .value = LTTNG_LOGLEVEL_LOG4J_OFF }, - { .name = "FATAL", .value = LTTNG_LOGLEVEL_LOG4J_FATAL }, - { .name = "LOG4J_FATAL", .value = LTTNG_LOGLEVEL_LOG4J_FATAL }, - { .name = "ERROR", .value = LTTNG_LOGLEVEL_LOG4J_ERROR }, - { .name = "LOG4J_ERROR", .value = LTTNG_LOGLEVEL_LOG4J_ERROR }, - { .name = "WARN", .value = LTTNG_LOGLEVEL_LOG4J_WARN }, - { .name = "LOG4J_WARN", .value = LTTNG_LOGLEVEL_LOG4J_WARN }, - { .name = "INFO", .value = LTTNG_LOGLEVEL_LOG4J_INFO }, - { .name = "LOG4J_INFO", .value = LTTNG_LOGLEVEL_LOG4J_INFO }, - { .name = "DEBUG", .value = LTTNG_LOGLEVEL_LOG4J_DEBUG }, - { .name = "LOG4J_DEBUG", .value = LTTNG_LOGLEVEL_LOG4J_DEBUG }, - { .name = "TRACE", .value = LTTNG_LOGLEVEL_LOG4J_TRACE }, - { .name = "LOG4J_TRACE", .value = LTTNG_LOGLEVEL_LOG4J_TRACE }, - { .name = "ALL", .value = LTTNG_LOGLEVEL_LOG4J_ALL }, - { .name = "LOG4J_ALL", .value = LTTNG_LOGLEVEL_LOG4J_ALL }, -}; - -static -const struct loglevel_name_value loglevel_jul_values[] = { - { .name = "OFF", .value = LTTNG_LOGLEVEL_JUL_OFF }, - { .name = "JUL_OFF", .value = LTTNG_LOGLEVEL_JUL_OFF }, - { .name = "SEVERE", .value = LTTNG_LOGLEVEL_JUL_SEVERE }, - { .name = "JUL_SEVERE", .value = LTTNG_LOGLEVEL_JUL_SEVERE }, - { .name = "WARNING", .value = LTTNG_LOGLEVEL_JUL_WARNING }, - { .name = "JUL_WARNING", .value = LTTNG_LOGLEVEL_JUL_WARNING }, - { .name = "INFO", .value = LTTNG_LOGLEVEL_JUL_INFO }, - { .name = "JUL_INFO", .value = LTTNG_LOGLEVEL_JUL_INFO }, - { .name = "CONFIG", .value = LTTNG_LOGLEVEL_JUL_CONFIG }, - { .name = "JUL_CONFIG", .value = LTTNG_LOGLEVEL_JUL_CONFIG }, - { .name = "FINE", .value = LTTNG_LOGLEVEL_JUL_FINE }, - { .name = "JUL_FINE", .value = LTTNG_LOGLEVEL_JUL_FINE }, - { .name = "FINER", .value = LTTNG_LOGLEVEL_JUL_FINER }, - { .name = "JUL_FINER", .value = LTTNG_LOGLEVEL_JUL_FINER }, - { .name = "FINEST", .value = LTTNG_LOGLEVEL_JUL_FINEST }, - { .name = "JUL_FINEST", .value = LTTNG_LOGLEVEL_JUL_FINEST }, - { .name = "ALL", .value = LTTNG_LOGLEVEL_JUL_ALL }, - { .name = "JUL_ALL", .value = LTTNG_LOGLEVEL_JUL_ALL }, -}; - -static -const struct loglevel_name_value loglevel_python_values[] = { - { .name = "CRITICAL", .value = LTTNG_LOGLEVEL_PYTHON_CRITICAL }, - { .name = "PYTHON_CRITICAL", .value = LTTNG_LOGLEVEL_PYTHON_CRITICAL }, - { .name = "ERROR", .value = LTTNG_LOGLEVEL_PYTHON_ERROR }, - { .name = "PYTHON_ERROR", .value = LTTNG_LOGLEVEL_PYTHON_ERROR }, - { .name = "WARNING", .value = LTTNG_LOGLEVEL_PYTHON_WARNING }, - { .name = "PYTHON_WARNING", .value = LTTNG_LOGLEVEL_PYTHON_WARNING }, - { .name = "INFO", .value = LTTNG_LOGLEVEL_PYTHON_INFO }, - { .name = "PYTHON_INFO", .value = LTTNG_LOGLEVEL_PYTHON_INFO }, - { .name = "DEBUG", .value = LTTNG_LOGLEVEL_PYTHON_DEBUG }, - { .name = "PYTNON_DEBUG", .value = LTTNG_LOGLEVEL_PYTHON_DEBUG }, - { .name = "NOTSET", .value = LTTNG_LOGLEVEL_PYTHON_NOTSET }, - { .name = "PYTHON_NOTSET", .value = LTTNG_LOGLEVEL_PYTHON_NOTSET }, -}; - -static -bool string_equal_insensitive(const char *a, const char *b) -{ - return strcasecmp(a, b) == 0; -} - -static -int lookup_value_from_name(const struct loglevel_name_value values[], - size_t values_count, const char *name) -{ - size_t i; - int ret = -1; - - if (!name) { - goto end; - } - - for (i = 0; i < values_count; i++) { - if (string_equal_insensitive(values[i].name, name)) { - /* Match found. */ - ret = values[i].value; - goto end; - } - } - -end: - return ret; -} - -static bool loglevel_parse_range_string_common(const char *str, - const struct loglevel_name_value *nvs, - size_t nvs_count, - int *min, - int *max) -{ - bool ret; - int i; - const struct loglevel_name_value *nv; - - for (i = 0; i < nvs_count; i++) { - nv = &nvs[i]; - - if (strncmp(str, nv->name, strlen(nv->name)) == 0) { - break; - } - } - - if (i == nvs_count) { - goto error; - } - - *min = nv->value; - str += strlen(nv->name); - - if (*str == '\0') { - *max = nv->value; - ret = true; - goto end; - } - - if (strncmp(str, "..", strlen("..")) != 0) { - goto error; - } - - str += strlen(".."); - - if (*str == '\0') { - *max = LTTNG_LOGLEVEL_EMERG; - ret = true; - goto end; - } - - for (i = 0; i < nvs_count; i++) { - nv = &nvs[i]; - - if (strcmp(str, nv->name) == 0) { - break; - } - } - - if (i == nvs_count) { - goto error; - } - - *max = nv->value; - - ret = true; - goto end; - -error: - ret = false; - -end: - return ret; -} - -int loglevel_name_to_value(const char *name, enum lttng_loglevel *loglevel) -{ - int ret = lookup_value_from_name(loglevel_values, - ARRAY_SIZE(loglevel_values), name); - - if (ret >= 0) { - *loglevel = (typeof(*loglevel)) ret; - ret = 0; - } - - return ret; -} - -bool loglevel_parse_range_string(const char *str, - enum lttng_loglevel *min, - enum lttng_loglevel *max) -{ - int min_int, max_int; - bool ret = loglevel_parse_range_string_common(str, loglevel_values, - ARRAY_SIZE(loglevel_values), &min_int, &max_int); - - *min = min_int; - *max = max_int; - - return ret; -} - -int loglevel_log4j_name_to_value( - const char *name, enum lttng_loglevel_log4j *loglevel) -{ - int ret = lookup_value_from_name(loglevel_log4j_values, - ARRAY_SIZE(loglevel_log4j_values), - name); - - if (ret >= 0) { - *loglevel = (typeof(*loglevel)) ret; - ret = 0; - } - - return ret; -} - -bool loglevel_log4j_parse_range_string(const char *str, - enum lttng_loglevel_log4j *min, - enum lttng_loglevel_log4j *max) -{ - int min_int, max_int; - bool ret = loglevel_parse_range_string_common(str, - loglevel_log4j_values, - ARRAY_SIZE(loglevel_log4j_values), &min_int, &max_int); - - *min = min_int; - *max = max_int; - - return ret; -} - -int loglevel_jul_name_to_value( - const char *name, enum lttng_loglevel_jul *loglevel) -{ - int ret = lookup_value_from_name(loglevel_jul_values, - ARRAY_SIZE(loglevel_jul_values), - name); - - if (ret >= 0) { - *loglevel = (typeof(*loglevel)) ret; - ret = 0; - } - - return ret; -} - -bool loglevel_jul_parse_range_string(const char *str, - enum lttng_loglevel_jul *min, - enum lttng_loglevel_jul *max) -{ - int min_int, max_int; - bool ret = loglevel_parse_range_string_common(str, loglevel_jul_values, - ARRAY_SIZE(loglevel_jul_values), &min_int, &max_int); - - *min = min_int; - *max = max_int; - - return ret; -} - -int loglevel_python_name_to_value( - const char *name, enum lttng_loglevel_python *loglevel) -{ - int ret = lookup_value_from_name(loglevel_python_values, - ARRAY_SIZE(loglevel_python_values), - name); - - if (ret >= 0) { - *loglevel = (typeof(*loglevel)) ret; - ret = 0; - } - - return ret; -} - -bool loglevel_python_parse_range_string(const char *str, - enum lttng_loglevel_python *min, - enum lttng_loglevel_python *max) -{ - int min_int, max_int; - bool ret = loglevel_parse_range_string_common(str, - loglevel_python_values, - ARRAY_SIZE(loglevel_python_values), &min_int, &max_int); - - *min = min_int; - *max = max_int; - - return ret; -} - -static -const char *lookup_name_from_value(const struct loglevel_name_value values[], - size_t values_count, int loglevel) -{ - size_t i; - const char *name = NULL; - - for (i = 0; i < values_count; i++) { - if (values[i].value == loglevel) { - /* Match found. */ - name = values[i].name; - goto end; - } - } - -end: - return name; -} - -const char *loglevel_value_to_name(int loglevel) -{ - return lookup_name_from_value( - loglevel_values, ARRAY_SIZE(loglevel_values), loglevel); -} - -const char *loglevel_log4j_value_to_name(int loglevel) -{ - return lookup_name_from_value(loglevel_log4j_values, - ARRAY_SIZE(loglevel_log4j_values), loglevel); -} - -const char *loglevel_jul_value_to_name(int loglevel) -{ - return lookup_name_from_value(loglevel_jul_values, - ARRAY_SIZE(loglevel_jul_values), loglevel); -} - -const char *loglevel_python_value_to_name(int loglevel) -{ - return lookup_name_from_value(loglevel_python_values, - ARRAY_SIZE(loglevel_python_values), loglevel); -} diff --git a/src/bin/lttng/loglevel.cpp b/src/bin/lttng/loglevel.cpp new file mode 100644 index 000000000..83c7b3048 --- /dev/null +++ b/src/bin/lttng/loglevel.cpp @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2021 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "loglevel.h" +#include +#include +#include + +struct loglevel_name_value { + const char *name; + int value; +}; + +static +const struct loglevel_name_value loglevel_values[] = { + { .name = "EMERG", .value = LTTNG_LOGLEVEL_EMERG }, + { .name = "TRACE_EMERG", .value = LTTNG_LOGLEVEL_EMERG }, + { .name = "ALERT", .value = LTTNG_LOGLEVEL_ALERT }, + { .name = "TRACE_ALERT", .value = LTTNG_LOGLEVEL_ALERT }, + { .name = "CRIT", .value = LTTNG_LOGLEVEL_CRIT }, + { .name = "TRACE_CRIT", .value = LTTNG_LOGLEVEL_CRIT }, + { .name = "ERR", .value = LTTNG_LOGLEVEL_ERR }, + { .name = "TRACE_ERR", .value = LTTNG_LOGLEVEL_ERR }, + { .name = "WARNING", .value = LTTNG_LOGLEVEL_WARNING }, + { .name = "TRACE_WARNING", .value = LTTNG_LOGLEVEL_WARNING }, + { .name = "NOTICE", .value = LTTNG_LOGLEVEL_NOTICE }, + { .name = "TRACE_NOTICE", .value = LTTNG_LOGLEVEL_NOTICE }, + { .name = "INFO", .value = LTTNG_LOGLEVEL_INFO }, + { .name = "TRACE_INFO", .value = LTTNG_LOGLEVEL_INFO }, + { .name = "DEBUG_SYSTEM", .value = LTTNG_LOGLEVEL_DEBUG_SYSTEM }, + { .name = "TRACE_DEBUG_SYSTEM", .value = LTTNG_LOGLEVEL_DEBUG_SYSTEM }, + { .name = "SYSTEM", .value = LTTNG_LOGLEVEL_DEBUG_SYSTEM }, + { .name = "DEBUG_PROGRAM", .value = LTTNG_LOGLEVEL_DEBUG_PROGRAM }, + { .name = "TRACE_DEBUG_PROGRAM", .value = LTTNG_LOGLEVEL_DEBUG_PROGRAM }, + { .name = "PROGRAM", .value = LTTNG_LOGLEVEL_DEBUG_PROGRAM }, + { .name = "DEBUG_PROCESS", .value = LTTNG_LOGLEVEL_DEBUG_PROCESS }, + { .name = "TRACE_DEBUG_PROCESS", .value = LTTNG_LOGLEVEL_DEBUG_PROCESS }, + { .name = "PROCESS", .value = LTTNG_LOGLEVEL_DEBUG_PROCESS }, + { .name = "DEBUG_MODULE", .value = LTTNG_LOGLEVEL_DEBUG_MODULE }, + { .name = "TRACE_DEBUG_MODULE", .value = LTTNG_LOGLEVEL_DEBUG_MODULE }, + { .name = "MODULE", .value = LTTNG_LOGLEVEL_DEBUG_MODULE }, + { .name = "DEBUG_UNIT", .value = LTTNG_LOGLEVEL_DEBUG_UNIT }, + { .name = "TRACE_DEBUG_UNIT", .value = LTTNG_LOGLEVEL_DEBUG_UNIT }, + { .name = "UNIT", .value = LTTNG_LOGLEVEL_DEBUG_UNIT }, + { .name = "DEBUG_FUNCTION", .value = LTTNG_LOGLEVEL_DEBUG_FUNCTION }, + { .name = "TRACE_DEBUG_FUNCTION", .value = LTTNG_LOGLEVEL_DEBUG_FUNCTION }, + { .name = "FUNCTION", .value = LTTNG_LOGLEVEL_DEBUG_FUNCTION }, + { .name = "DEBUG_LINE", .value = LTTNG_LOGLEVEL_DEBUG_LINE }, + { .name = "TRACE_DEBUG_LINE", .value = LTTNG_LOGLEVEL_DEBUG_LINE }, + { .name = "LINE", .value = LTTNG_LOGLEVEL_DEBUG_LINE }, + { .name = "DEBUG", .value = LTTNG_LOGLEVEL_DEBUG }, + { .name = "TRACE_DEBUG", .value = LTTNG_LOGLEVEL_DEBUG }, +}; + +static +const struct loglevel_name_value loglevel_log4j_values[] = { + { .name = "OFF", .value = LTTNG_LOGLEVEL_LOG4J_OFF }, + { .name = "LOG4J_OFF", .value = LTTNG_LOGLEVEL_LOG4J_OFF }, + { .name = "FATAL", .value = LTTNG_LOGLEVEL_LOG4J_FATAL }, + { .name = "LOG4J_FATAL", .value = LTTNG_LOGLEVEL_LOG4J_FATAL }, + { .name = "ERROR", .value = LTTNG_LOGLEVEL_LOG4J_ERROR }, + { .name = "LOG4J_ERROR", .value = LTTNG_LOGLEVEL_LOG4J_ERROR }, + { .name = "WARN", .value = LTTNG_LOGLEVEL_LOG4J_WARN }, + { .name = "LOG4J_WARN", .value = LTTNG_LOGLEVEL_LOG4J_WARN }, + { .name = "INFO", .value = LTTNG_LOGLEVEL_LOG4J_INFO }, + { .name = "LOG4J_INFO", .value = LTTNG_LOGLEVEL_LOG4J_INFO }, + { .name = "DEBUG", .value = LTTNG_LOGLEVEL_LOG4J_DEBUG }, + { .name = "LOG4J_DEBUG", .value = LTTNG_LOGLEVEL_LOG4J_DEBUG }, + { .name = "TRACE", .value = LTTNG_LOGLEVEL_LOG4J_TRACE }, + { .name = "LOG4J_TRACE", .value = LTTNG_LOGLEVEL_LOG4J_TRACE }, + { .name = "ALL", .value = LTTNG_LOGLEVEL_LOG4J_ALL }, + { .name = "LOG4J_ALL", .value = LTTNG_LOGLEVEL_LOG4J_ALL }, +}; + +static +const struct loglevel_name_value loglevel_jul_values[] = { + { .name = "OFF", .value = LTTNG_LOGLEVEL_JUL_OFF }, + { .name = "JUL_OFF", .value = LTTNG_LOGLEVEL_JUL_OFF }, + { .name = "SEVERE", .value = LTTNG_LOGLEVEL_JUL_SEVERE }, + { .name = "JUL_SEVERE", .value = LTTNG_LOGLEVEL_JUL_SEVERE }, + { .name = "WARNING", .value = LTTNG_LOGLEVEL_JUL_WARNING }, + { .name = "JUL_WARNING", .value = LTTNG_LOGLEVEL_JUL_WARNING }, + { .name = "INFO", .value = LTTNG_LOGLEVEL_JUL_INFO }, + { .name = "JUL_INFO", .value = LTTNG_LOGLEVEL_JUL_INFO }, + { .name = "CONFIG", .value = LTTNG_LOGLEVEL_JUL_CONFIG }, + { .name = "JUL_CONFIG", .value = LTTNG_LOGLEVEL_JUL_CONFIG }, + { .name = "FINE", .value = LTTNG_LOGLEVEL_JUL_FINE }, + { .name = "JUL_FINE", .value = LTTNG_LOGLEVEL_JUL_FINE }, + { .name = "FINER", .value = LTTNG_LOGLEVEL_JUL_FINER }, + { .name = "JUL_FINER", .value = LTTNG_LOGLEVEL_JUL_FINER }, + { .name = "FINEST", .value = LTTNG_LOGLEVEL_JUL_FINEST }, + { .name = "JUL_FINEST", .value = LTTNG_LOGLEVEL_JUL_FINEST }, + { .name = "ALL", .value = LTTNG_LOGLEVEL_JUL_ALL }, + { .name = "JUL_ALL", .value = LTTNG_LOGLEVEL_JUL_ALL }, +}; + +static +const struct loglevel_name_value loglevel_python_values[] = { + { .name = "CRITICAL", .value = LTTNG_LOGLEVEL_PYTHON_CRITICAL }, + { .name = "PYTHON_CRITICAL", .value = LTTNG_LOGLEVEL_PYTHON_CRITICAL }, + { .name = "ERROR", .value = LTTNG_LOGLEVEL_PYTHON_ERROR }, + { .name = "PYTHON_ERROR", .value = LTTNG_LOGLEVEL_PYTHON_ERROR }, + { .name = "WARNING", .value = LTTNG_LOGLEVEL_PYTHON_WARNING }, + { .name = "PYTHON_WARNING", .value = LTTNG_LOGLEVEL_PYTHON_WARNING }, + { .name = "INFO", .value = LTTNG_LOGLEVEL_PYTHON_INFO }, + { .name = "PYTHON_INFO", .value = LTTNG_LOGLEVEL_PYTHON_INFO }, + { .name = "DEBUG", .value = LTTNG_LOGLEVEL_PYTHON_DEBUG }, + { .name = "PYTNON_DEBUG", .value = LTTNG_LOGLEVEL_PYTHON_DEBUG }, + { .name = "NOTSET", .value = LTTNG_LOGLEVEL_PYTHON_NOTSET }, + { .name = "PYTHON_NOTSET", .value = LTTNG_LOGLEVEL_PYTHON_NOTSET }, +}; + +static +bool string_equal_insensitive(const char *a, const char *b) +{ + return strcasecmp(a, b) == 0; +} + +static +int lookup_value_from_name(const struct loglevel_name_value values[], + size_t values_count, const char *name) +{ + size_t i; + int ret = -1; + + if (!name) { + goto end; + } + + for (i = 0; i < values_count; i++) { + if (string_equal_insensitive(values[i].name, name)) { + /* Match found. */ + ret = values[i].value; + goto end; + } + } + +end: + return ret; +} + +static bool loglevel_parse_range_string_common(const char *str, + const struct loglevel_name_value *nvs, + size_t nvs_count, + int *min, + int *max) +{ + bool ret; + int i; + const struct loglevel_name_value *nv; + + for (i = 0; i < nvs_count; i++) { + nv = &nvs[i]; + + if (strncmp(str, nv->name, strlen(nv->name)) == 0) { + break; + } + } + + if (i == nvs_count) { + goto error; + } + + *min = nv->value; + str += strlen(nv->name); + + if (*str == '\0') { + *max = nv->value; + ret = true; + goto end; + } + + if (strncmp(str, "..", strlen("..")) != 0) { + goto error; + } + + str += strlen(".."); + + if (*str == '\0') { + *max = LTTNG_LOGLEVEL_EMERG; + ret = true; + goto end; + } + + for (i = 0; i < nvs_count; i++) { + nv = &nvs[i]; + + if (strcmp(str, nv->name) == 0) { + break; + } + } + + if (i == nvs_count) { + goto error; + } + + *max = nv->value; + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +int loglevel_name_to_value(const char *name, enum lttng_loglevel *loglevel) +{ + int ret = lookup_value_from_name(loglevel_values, + ARRAY_SIZE(loglevel_values), name); + + if (ret >= 0) { + *loglevel = (typeof(*loglevel)) ret; + ret = 0; + } + + return ret; +} + +bool loglevel_parse_range_string(const char *str, + enum lttng_loglevel *min, + enum lttng_loglevel *max) +{ + int min_int, max_int; + bool ret = loglevel_parse_range_string_common(str, loglevel_values, + ARRAY_SIZE(loglevel_values), &min_int, &max_int); + + *min = (lttng_loglevel) min_int; + *max = (lttng_loglevel) max_int; + + return ret; +} + +int loglevel_log4j_name_to_value( + const char *name, enum lttng_loglevel_log4j *loglevel) +{ + int ret = lookup_value_from_name(loglevel_log4j_values, + ARRAY_SIZE(loglevel_log4j_values), + name); + + if (ret >= 0) { + *loglevel = (typeof(*loglevel)) ret; + ret = 0; + } + + return ret; +} + +bool loglevel_log4j_parse_range_string(const char *str, + enum lttng_loglevel_log4j *min, + enum lttng_loglevel_log4j *max) +{ + int min_int, max_int; + bool ret = loglevel_parse_range_string_common(str, + loglevel_log4j_values, + ARRAY_SIZE(loglevel_log4j_values), &min_int, &max_int); + + *min = (lttng_loglevel_log4j) min_int; + *max = (lttng_loglevel_log4j) max_int; + + return ret; +} + +int loglevel_jul_name_to_value( + const char *name, enum lttng_loglevel_jul *loglevel) +{ + int ret = lookup_value_from_name(loglevel_jul_values, + ARRAY_SIZE(loglevel_jul_values), + name); + + if (ret >= 0) { + *loglevel = (typeof(*loglevel)) ret; + ret = 0; + } + + return ret; +} + +bool loglevel_jul_parse_range_string(const char *str, + enum lttng_loglevel_jul *min, + enum lttng_loglevel_jul *max) +{ + int min_int, max_int; + bool ret = loglevel_parse_range_string_common(str, loglevel_jul_values, + ARRAY_SIZE(loglevel_jul_values), &min_int, &max_int); + + *min = (lttng_loglevel_jul) min_int; + *max = (lttng_loglevel_jul) max_int; + + return ret; +} + +int loglevel_python_name_to_value( + const char *name, enum lttng_loglevel_python *loglevel) +{ + int ret = lookup_value_from_name(loglevel_python_values, + ARRAY_SIZE(loglevel_python_values), + name); + + if (ret >= 0) { + *loglevel = (typeof(*loglevel)) ret; + ret = 0; + } + + return ret; +} + +bool loglevel_python_parse_range_string(const char *str, + enum lttng_loglevel_python *min, + enum lttng_loglevel_python *max) +{ + int min_int, max_int; + bool ret = loglevel_parse_range_string_common(str, + loglevel_python_values, + ARRAY_SIZE(loglevel_python_values), &min_int, &max_int); + + *min = (lttng_loglevel_python) min_int; + *max = (lttng_loglevel_python) max_int; + + return ret; +} + +static +const char *lookup_name_from_value(const struct loglevel_name_value values[], + size_t values_count, int loglevel) +{ + size_t i; + const char *name = NULL; + + for (i = 0; i < values_count; i++) { + if (values[i].value == loglevel) { + /* Match found. */ + name = values[i].name; + goto end; + } + } + +end: + return name; +} + +const char *loglevel_value_to_name(int loglevel) +{ + return lookup_name_from_value( + loglevel_values, ARRAY_SIZE(loglevel_values), loglevel); +} + +const char *loglevel_log4j_value_to_name(int loglevel) +{ + return lookup_name_from_value(loglevel_log4j_values, + ARRAY_SIZE(loglevel_log4j_values), loglevel); +} + +const char *loglevel_jul_value_to_name(int loglevel) +{ + return lookup_name_from_value(loglevel_jul_values, + ARRAY_SIZE(loglevel_jul_values), loglevel); +} + +const char *loglevel_python_value_to_name(int loglevel) +{ + return lookup_name_from_value(loglevel_python_values, + ARRAY_SIZE(loglevel_python_values), loglevel); +} diff --git a/src/bin/lttng/loglevel.h b/src/bin/lttng/loglevel.h index 43ae8e448..06dcbaa86 100644 --- a/src/bin/lttng/loglevel.h +++ b/src/bin/lttng/loglevel.h @@ -11,6 +11,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + int loglevel_name_to_value(const char *name, enum lttng_loglevel *loglevel); bool loglevel_parse_range_string(const char *str, @@ -46,4 +50,8 @@ const char *loglevel_jul_value_to_name(int loglevel); const char *loglevel_python_value_to_name(int loglevel); +#ifdef __cplusplus +} +#endif + #endif /* _LTTNG_LOGLEVEL_UTILS_H */ diff --git a/src/bin/lttng/lttng.c b/src/bin/lttng/lttng.c deleted file mode 100644 index af1d3f488..000000000 --- a/src/bin/lttng/lttng.c +++ /dev/null @@ -1,481 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "command.h" -#include "version.h" - -static const char *help_msg = -#ifdef LTTNG_EMBED_HELP -#include -#else -NULL -#endif -; - -/* Variables */ -static const char *progname; -int opt_no_sessiond; -char *opt_sessiond_path; - -char *opt_relayd_path; - -enum { - OPT_RELAYD_PATH, - OPT_SESSION_PATH, - OPT_DUMP_OPTIONS, - OPT_DUMP_COMMANDS, -}; - -/* Getopt options. No first level command. */ -static struct option long_options[] = { - {"version", 0, NULL, 'V'}, - {"help", 0, NULL, 'h'}, - {"group", 1, NULL, 'g'}, - {"verbose", 0, NULL, 'v'}, - {"quiet", 0, NULL, 'q'}, - {"mi", 1, NULL, 'm'}, - {"no-sessiond", 0, NULL, 'n'}, - {"sessiond-path", 1, NULL, OPT_SESSION_PATH}, - {"relayd-path", 1, NULL, OPT_RELAYD_PATH}, - {"list-options", 0, NULL, OPT_DUMP_OPTIONS}, - {"list-commands", 0, NULL, OPT_DUMP_COMMANDS}, - {NULL, 0, NULL, 0} -}; - -/* First level command */ -static struct cmd_struct commands[] = { - { "add-context", cmd_add_context}, - { "add-trigger", cmd_add_trigger}, - { "create", cmd_create}, - { "clear", cmd_clear}, - { "destroy", cmd_destroy}, - { "disable-channel", cmd_disable_channels}, - { "disable-event", cmd_disable_events}, - { "enable-channel", cmd_enable_channels}, - { "enable-event", cmd_enable_events}, - { "help", NULL}, - { "list", cmd_list}, - { "list-triggers", cmd_list_triggers}, - { "load", cmd_load}, - { "metadata", cmd_metadata}, - { "regenerate", cmd_regenerate}, - { "remove-trigger", cmd_remove_trigger}, - { "rotate", cmd_rotate}, - { "enable-rotation", cmd_enable_rotation}, - { "disable-rotation", cmd_disable_rotation}, - { "save", cmd_save}, - { "set-session", cmd_set_session}, - { "snapshot", cmd_snapshot}, - { "start", cmd_start}, - { "status", cmd_status}, - { "stop", cmd_stop}, - { "track", cmd_track}, - { "untrack", cmd_untrack}, - { "version", cmd_version}, - { "view", cmd_view}, - { NULL, NULL} /* Array closure */ -}; - -static void version(FILE *ofp) -{ - fprintf(ofp, "%s (LTTng Trace Control) " VERSION" - " VERSION_NAME "%s%s\n", - progname, - GIT_VERSION[0] == '\0' ? "" : " - " GIT_VERSION, - EXTRA_VERSION_NAME[0] == '\0' ? "" : " - " EXTRA_VERSION_NAME); -} - -/* - * Find the MI output type enum from a string. This function is for the support - * of machine interface output. - */ -static int mi_output_type(const char *output_type) -{ - int ret = 0; - - if (!strncasecmp("xml", output_type, 3)) { - ret = LTTNG_MI_XML; - } else { - /* Invalid output format */ - ERR("MI output format not supported"); - ret = -LTTNG_ERR_MI_OUTPUT_TYPE; - } - - return ret; -} - -/* - * list_options - * - * List options line by line. This is mostly for bash auto completion and to - * avoid difficult parsing. - */ -static void list_options(FILE *ofp) -{ - int i = 0; - struct option *option = NULL; - - option = &long_options[i]; - while (option->name != NULL) { - fprintf(ofp, "--%s\n", option->name); - - if (isprint(option->val)) { - fprintf(ofp, "-%c\n", option->val); - } - - i++; - option = &long_options[i]; - } -} - -/* - * clean_exit - */ -static void clean_exit(int code) -{ - DBG("Clean exit"); - exit(code); -} - -/* - * sighandler - * - * Signal handler for the daemon - */ -static void sighandler(int sig) -{ - switch (sig) { - case SIGTERM: - DBG("SIGTERM caught"); - clean_exit(EXIT_FAILURE); - break; - default: - DBG("Unknown signal %d caught", sig); - break; - } - - return; -} - -/* - * set_signal_handler - * - * Setup signal handler for SIGCHLD and SIGTERM. - */ -static int set_signal_handler(void) -{ - int ret = 0; - struct sigaction sa; - sigset_t sigset; - - if ((ret = sigemptyset(&sigset)) < 0) { - PERROR("sigemptyset"); - goto end; - } - - sa.sa_handler = sighandler; - sa.sa_mask = sigset; - sa.sa_flags = 0; - - if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) { - PERROR("sigaction"); - goto end; - } - -end: - return ret; -} - -/* - * handle_command - * - * Handle the full argv list of a first level command. Will find the command - * in the global commands array and call the function callback associated. - * - * If command not found, return -1 - * else, return function command error code. - */ -static int handle_command(int argc, char **argv) -{ - int i = 0, ret; - struct cmd_struct *cmd; - - if (*argv == NULL) { - ret = CMD_SUCCESS; - goto end; - } - - /* Special case for help command which needs the commands array */ - if (strcmp(argv[0], "help") == 0) { - ret = cmd_help(argc, (const char**) argv, commands); - goto end; - } - - cmd = &commands[i]; - while (cmd->name != NULL) { - /* Find command */ - if (strcmp(argv[0], cmd->name) == 0) { - ret = cmd->func(argc, (const char**) argv); - goto end; - } - i++; - cmd = &commands[i]; - } - - /* Command not found */ - ret = CMD_UNDEFINED; - -end: - return ret; -} - -static bool command_exists(const char *command) -{ - const struct cmd_struct *cmd = commands; - bool exists = false; - - while (cmd->name != NULL) { - if (!strcmp(command, cmd->name)) { - exists = true; - goto end; - } - cmd++; - } - -end: - return exists; -} - -static void show_basic_help(void) -{ - puts("Usage: lttng [--group=GROUP] [--mi=TYPE] [--no-sessiond | --sessiond-path=PATH]"); - puts(" [--quiet | -v | -vv | -vvv] COMMAND [COMMAND OPTIONS]"); - puts(""); - puts("Available commands:"); - puts(""); - puts("Recording sessions:"); - puts(" create " CONFIG_CMD_DESCR_CREATE); - puts(" clear " CONFIG_CMD_DESCR_CLEAR); - puts(" destroy " CONFIG_CMD_DESCR_DESTROY); - puts(" load " CONFIG_CMD_DESCR_LOAD); - puts(" regenerate " CONFIG_CMD_DESCR_REGENERATE); - puts(" save " CONFIG_CMD_DESCR_SAVE); - puts(" set-session " CONFIG_CMD_DESCR_SET_SESSION); - puts(""); - puts("Channels:"); - puts(" add-context " CONFIG_CMD_DESCR_ADD_CONTEXT); - puts(" disable-channel " CONFIG_CMD_DESCR_DISABLE_CHANNEL); - puts(" enable-channel " CONFIG_CMD_DESCR_ENABLE_CHANNEL); - puts(""); - puts("Recording event rules:"); - puts(" disable-event " CONFIG_CMD_DESCR_DISABLE_EVENT); - puts(" enable-event " CONFIG_CMD_DESCR_ENABLE_EVENT); - puts(""); - puts("Status:"); - puts(" list " CONFIG_CMD_DESCR_LIST); - puts(" status " CONFIG_CMD_DESCR_STATUS); - puts(""); - puts("Control:"); - puts(" snapshot " CONFIG_CMD_DESCR_SNAPSHOT); - puts(" start " CONFIG_CMD_DESCR_START); - puts(" stop " CONFIG_CMD_DESCR_STOP); - puts(""); - puts("Recording session rotation:"); - puts(" disable-rotation " CONFIG_CMD_DESCR_DISABLE_ROTATION); - puts(" enable-rotation " CONFIG_CMD_DESCR_ENABLE_ROTATION); - puts(" rotate " CONFIG_CMD_DESCR_ROTATE); - puts(""); - puts("Resource tracking:"); - puts(" track " CONFIG_CMD_DESCR_TRACK); - puts(" untrack " CONFIG_CMD_DESCR_UNTRACK); - puts(""); - puts("Triggers:"); - puts(" add-trigger " CONFIG_CMD_DESCR_ADD_TRIGGER); - puts(" remove-trigger " CONFIG_CMD_DESCR_REMOVE_TRIGGER); - puts(" list-triggers " CONFIG_CMD_DESCR_LIST_TRIGGERS); - puts(""); - puts("Miscellaneous:"); - puts(" help " CONFIG_CMD_DESCR_HELP); - puts(" version " CONFIG_CMD_DESCR_VERSION); - puts(" view " CONFIG_CMD_DESCR_VIEW); - puts(""); - puts("Run `lttng help COMMAND` or `lttng COMMAND --help` to get help with"); - puts("command COMMAND."); - puts(""); - puts("See `man lttng` for more help with the lttng command."); -} - -/* - * Parse command line arguments. - * - * Return 0 if OK, else -1 - */ -static int parse_args(int argc, char **argv) -{ - int opt, ret; - - if (lttng_is_setuid_setgid()) { - ERR("'%s' is not allowed to be executed as a setuid/setgid binary for security reasons. Aborting.", argv[0]); - clean_exit(EXIT_FAILURE); - } - - if (argc < 2) { - show_basic_help(); - clean_exit(EXIT_FAILURE); - } - - while ((opt = getopt_long(argc, argv, "+Vhnvqg:m:", long_options, NULL)) != -1) { - switch (opt) { - case 'V': - version(stdout); - ret = 0; - goto end; - case 'h': - ret = utils_show_help(1, "lttng", help_msg); - if (ret) { - ERR("Cannot show --help for `lttng`"); - perror("exec"); - } - goto end; - case 'v': - /* There is only 3 possible level of verbosity. (-vvv) */ - if (lttng_opt_verbose < 3) { - lttng_opt_verbose += 1; - } - break; - case 'q': - lttng_opt_quiet = 1; - break; - case 'm': - lttng_opt_mi = mi_output_type(optarg); - if (lttng_opt_mi < 0) { - ret = lttng_opt_mi; - goto error; - } - break; - case 'g': - lttng_set_tracing_group(optarg); - break; - case 'n': - opt_no_sessiond = 1; - break; - case OPT_SESSION_PATH: - free(opt_sessiond_path); - opt_sessiond_path = strdup(optarg); - if (!opt_sessiond_path) { - ret = -1; - goto error; - } - break; - case OPT_RELAYD_PATH: - free(opt_relayd_path); - opt_relayd_path = strdup(optarg); - if (!opt_relayd_path) { - ret = -1; - goto error; - } - break; - case OPT_DUMP_OPTIONS: - list_options(stdout); - ret = 0; - goto end; - case OPT_DUMP_COMMANDS: - list_commands(commands, stdout); - ret = 0; - goto end; - default: - ret = 1; - goto error; - } - } - - /* If both options are specified, quiet wins */ - if (lttng_opt_verbose && lttng_opt_quiet) { - lttng_opt_verbose = 0; - } - - /* No leftovers, quit */ - if ((argc - optind) == 0) { - ret = 1; - goto error; - } - - /* - * Handle leftovers which is a first level command with the trailing - * options. - */ - ret = handle_command(argc - optind, argv + optind); - switch (ret) { - case CMD_WARNING: - case CMD_ERROR: - break; - case CMD_UNDEFINED: - if (!command_exists(*(argv + optind))) { - MSG("lttng: %s is not an lttng command. See 'lttng --help'.", - *(argv + optind)); - } else { - ERR("Unrecognized argument used with \'%s\' command", - *(argv + optind)); - } - break; - case CMD_FATAL: - case CMD_UNSUPPORTED: - break; - case -1: - ret = 1; - break; - case 0: - break; - default: - if (ret < 0) { - ret = -ret; - } - break; - } - -end: -error: - return ret; -} - - -/* - * main - */ -int main(int argc, char *argv[]) -{ - int ret; - - progname = argv[0] ? argv[0] : "lttng"; - - ret = set_signal_handler(); - if (ret < 0) { - clean_exit(ret); - } - - ret = parse_args(argc, argv); - if (ret != 0) { - clean_exit(ret); - } - - return 0; -} diff --git a/src/bin/lttng/lttng.cpp b/src/bin/lttng/lttng.cpp new file mode 100644 index 000000000..af1d3f488 --- /dev/null +++ b/src/bin/lttng/lttng.cpp @@ -0,0 +1,481 @@ +/* + * Copyright (C) 2011 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "command.h" +#include "version.h" + +static const char *help_msg = +#ifdef LTTNG_EMBED_HELP +#include +#else +NULL +#endif +; + +/* Variables */ +static const char *progname; +int opt_no_sessiond; +char *opt_sessiond_path; + +char *opt_relayd_path; + +enum { + OPT_RELAYD_PATH, + OPT_SESSION_PATH, + OPT_DUMP_OPTIONS, + OPT_DUMP_COMMANDS, +}; + +/* Getopt options. No first level command. */ +static struct option long_options[] = { + {"version", 0, NULL, 'V'}, + {"help", 0, NULL, 'h'}, + {"group", 1, NULL, 'g'}, + {"verbose", 0, NULL, 'v'}, + {"quiet", 0, NULL, 'q'}, + {"mi", 1, NULL, 'm'}, + {"no-sessiond", 0, NULL, 'n'}, + {"sessiond-path", 1, NULL, OPT_SESSION_PATH}, + {"relayd-path", 1, NULL, OPT_RELAYD_PATH}, + {"list-options", 0, NULL, OPT_DUMP_OPTIONS}, + {"list-commands", 0, NULL, OPT_DUMP_COMMANDS}, + {NULL, 0, NULL, 0} +}; + +/* First level command */ +static struct cmd_struct commands[] = { + { "add-context", cmd_add_context}, + { "add-trigger", cmd_add_trigger}, + { "create", cmd_create}, + { "clear", cmd_clear}, + { "destroy", cmd_destroy}, + { "disable-channel", cmd_disable_channels}, + { "disable-event", cmd_disable_events}, + { "enable-channel", cmd_enable_channels}, + { "enable-event", cmd_enable_events}, + { "help", NULL}, + { "list", cmd_list}, + { "list-triggers", cmd_list_triggers}, + { "load", cmd_load}, + { "metadata", cmd_metadata}, + { "regenerate", cmd_regenerate}, + { "remove-trigger", cmd_remove_trigger}, + { "rotate", cmd_rotate}, + { "enable-rotation", cmd_enable_rotation}, + { "disable-rotation", cmd_disable_rotation}, + { "save", cmd_save}, + { "set-session", cmd_set_session}, + { "snapshot", cmd_snapshot}, + { "start", cmd_start}, + { "status", cmd_status}, + { "stop", cmd_stop}, + { "track", cmd_track}, + { "untrack", cmd_untrack}, + { "version", cmd_version}, + { "view", cmd_view}, + { NULL, NULL} /* Array closure */ +}; + +static void version(FILE *ofp) +{ + fprintf(ofp, "%s (LTTng Trace Control) " VERSION" - " VERSION_NAME "%s%s\n", + progname, + GIT_VERSION[0] == '\0' ? "" : " - " GIT_VERSION, + EXTRA_VERSION_NAME[0] == '\0' ? "" : " - " EXTRA_VERSION_NAME); +} + +/* + * Find the MI output type enum from a string. This function is for the support + * of machine interface output. + */ +static int mi_output_type(const char *output_type) +{ + int ret = 0; + + if (!strncasecmp("xml", output_type, 3)) { + ret = LTTNG_MI_XML; + } else { + /* Invalid output format */ + ERR("MI output format not supported"); + ret = -LTTNG_ERR_MI_OUTPUT_TYPE; + } + + return ret; +} + +/* + * list_options + * + * List options line by line. This is mostly for bash auto completion and to + * avoid difficult parsing. + */ +static void list_options(FILE *ofp) +{ + int i = 0; + struct option *option = NULL; + + option = &long_options[i]; + while (option->name != NULL) { + fprintf(ofp, "--%s\n", option->name); + + if (isprint(option->val)) { + fprintf(ofp, "-%c\n", option->val); + } + + i++; + option = &long_options[i]; + } +} + +/* + * clean_exit + */ +static void clean_exit(int code) +{ + DBG("Clean exit"); + exit(code); +} + +/* + * sighandler + * + * Signal handler for the daemon + */ +static void sighandler(int sig) +{ + switch (sig) { + case SIGTERM: + DBG("SIGTERM caught"); + clean_exit(EXIT_FAILURE); + break; + default: + DBG("Unknown signal %d caught", sig); + break; + } + + return; +} + +/* + * set_signal_handler + * + * Setup signal handler for SIGCHLD and SIGTERM. + */ +static int set_signal_handler(void) +{ + int ret = 0; + struct sigaction sa; + sigset_t sigset; + + if ((ret = sigemptyset(&sigset)) < 0) { + PERROR("sigemptyset"); + goto end; + } + + sa.sa_handler = sighandler; + sa.sa_mask = sigset; + sa.sa_flags = 0; + + if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) { + PERROR("sigaction"); + goto end; + } + +end: + return ret; +} + +/* + * handle_command + * + * Handle the full argv list of a first level command. Will find the command + * in the global commands array and call the function callback associated. + * + * If command not found, return -1 + * else, return function command error code. + */ +static int handle_command(int argc, char **argv) +{ + int i = 0, ret; + struct cmd_struct *cmd; + + if (*argv == NULL) { + ret = CMD_SUCCESS; + goto end; + } + + /* Special case for help command which needs the commands array */ + if (strcmp(argv[0], "help") == 0) { + ret = cmd_help(argc, (const char**) argv, commands); + goto end; + } + + cmd = &commands[i]; + while (cmd->name != NULL) { + /* Find command */ + if (strcmp(argv[0], cmd->name) == 0) { + ret = cmd->func(argc, (const char**) argv); + goto end; + } + i++; + cmd = &commands[i]; + } + + /* Command not found */ + ret = CMD_UNDEFINED; + +end: + return ret; +} + +static bool command_exists(const char *command) +{ + const struct cmd_struct *cmd = commands; + bool exists = false; + + while (cmd->name != NULL) { + if (!strcmp(command, cmd->name)) { + exists = true; + goto end; + } + cmd++; + } + +end: + return exists; +} + +static void show_basic_help(void) +{ + puts("Usage: lttng [--group=GROUP] [--mi=TYPE] [--no-sessiond | --sessiond-path=PATH]"); + puts(" [--quiet | -v | -vv | -vvv] COMMAND [COMMAND OPTIONS]"); + puts(""); + puts("Available commands:"); + puts(""); + puts("Recording sessions:"); + puts(" create " CONFIG_CMD_DESCR_CREATE); + puts(" clear " CONFIG_CMD_DESCR_CLEAR); + puts(" destroy " CONFIG_CMD_DESCR_DESTROY); + puts(" load " CONFIG_CMD_DESCR_LOAD); + puts(" regenerate " CONFIG_CMD_DESCR_REGENERATE); + puts(" save " CONFIG_CMD_DESCR_SAVE); + puts(" set-session " CONFIG_CMD_DESCR_SET_SESSION); + puts(""); + puts("Channels:"); + puts(" add-context " CONFIG_CMD_DESCR_ADD_CONTEXT); + puts(" disable-channel " CONFIG_CMD_DESCR_DISABLE_CHANNEL); + puts(" enable-channel " CONFIG_CMD_DESCR_ENABLE_CHANNEL); + puts(""); + puts("Recording event rules:"); + puts(" disable-event " CONFIG_CMD_DESCR_DISABLE_EVENT); + puts(" enable-event " CONFIG_CMD_DESCR_ENABLE_EVENT); + puts(""); + puts("Status:"); + puts(" list " CONFIG_CMD_DESCR_LIST); + puts(" status " CONFIG_CMD_DESCR_STATUS); + puts(""); + puts("Control:"); + puts(" snapshot " CONFIG_CMD_DESCR_SNAPSHOT); + puts(" start " CONFIG_CMD_DESCR_START); + puts(" stop " CONFIG_CMD_DESCR_STOP); + puts(""); + puts("Recording session rotation:"); + puts(" disable-rotation " CONFIG_CMD_DESCR_DISABLE_ROTATION); + puts(" enable-rotation " CONFIG_CMD_DESCR_ENABLE_ROTATION); + puts(" rotate " CONFIG_CMD_DESCR_ROTATE); + puts(""); + puts("Resource tracking:"); + puts(" track " CONFIG_CMD_DESCR_TRACK); + puts(" untrack " CONFIG_CMD_DESCR_UNTRACK); + puts(""); + puts("Triggers:"); + puts(" add-trigger " CONFIG_CMD_DESCR_ADD_TRIGGER); + puts(" remove-trigger " CONFIG_CMD_DESCR_REMOVE_TRIGGER); + puts(" list-triggers " CONFIG_CMD_DESCR_LIST_TRIGGERS); + puts(""); + puts("Miscellaneous:"); + puts(" help " CONFIG_CMD_DESCR_HELP); + puts(" version " CONFIG_CMD_DESCR_VERSION); + puts(" view " CONFIG_CMD_DESCR_VIEW); + puts(""); + puts("Run `lttng help COMMAND` or `lttng COMMAND --help` to get help with"); + puts("command COMMAND."); + puts(""); + puts("See `man lttng` for more help with the lttng command."); +} + +/* + * Parse command line arguments. + * + * Return 0 if OK, else -1 + */ +static int parse_args(int argc, char **argv) +{ + int opt, ret; + + if (lttng_is_setuid_setgid()) { + ERR("'%s' is not allowed to be executed as a setuid/setgid binary for security reasons. Aborting.", argv[0]); + clean_exit(EXIT_FAILURE); + } + + if (argc < 2) { + show_basic_help(); + clean_exit(EXIT_FAILURE); + } + + while ((opt = getopt_long(argc, argv, "+Vhnvqg:m:", long_options, NULL)) != -1) { + switch (opt) { + case 'V': + version(stdout); + ret = 0; + goto end; + case 'h': + ret = utils_show_help(1, "lttng", help_msg); + if (ret) { + ERR("Cannot show --help for `lttng`"); + perror("exec"); + } + goto end; + case 'v': + /* There is only 3 possible level of verbosity. (-vvv) */ + if (lttng_opt_verbose < 3) { + lttng_opt_verbose += 1; + } + break; + case 'q': + lttng_opt_quiet = 1; + break; + case 'm': + lttng_opt_mi = mi_output_type(optarg); + if (lttng_opt_mi < 0) { + ret = lttng_opt_mi; + goto error; + } + break; + case 'g': + lttng_set_tracing_group(optarg); + break; + case 'n': + opt_no_sessiond = 1; + break; + case OPT_SESSION_PATH: + free(opt_sessiond_path); + opt_sessiond_path = strdup(optarg); + if (!opt_sessiond_path) { + ret = -1; + goto error; + } + break; + case OPT_RELAYD_PATH: + free(opt_relayd_path); + opt_relayd_path = strdup(optarg); + if (!opt_relayd_path) { + ret = -1; + goto error; + } + break; + case OPT_DUMP_OPTIONS: + list_options(stdout); + ret = 0; + goto end; + case OPT_DUMP_COMMANDS: + list_commands(commands, stdout); + ret = 0; + goto end; + default: + ret = 1; + goto error; + } + } + + /* If both options are specified, quiet wins */ + if (lttng_opt_verbose && lttng_opt_quiet) { + lttng_opt_verbose = 0; + } + + /* No leftovers, quit */ + if ((argc - optind) == 0) { + ret = 1; + goto error; + } + + /* + * Handle leftovers which is a first level command with the trailing + * options. + */ + ret = handle_command(argc - optind, argv + optind); + switch (ret) { + case CMD_WARNING: + case CMD_ERROR: + break; + case CMD_UNDEFINED: + if (!command_exists(*(argv + optind))) { + MSG("lttng: %s is not an lttng command. See 'lttng --help'.", + *(argv + optind)); + } else { + ERR("Unrecognized argument used with \'%s\' command", + *(argv + optind)); + } + break; + case CMD_FATAL: + case CMD_UNSUPPORTED: + break; + case -1: + ret = 1; + break; + case 0: + break; + default: + if (ret < 0) { + ret = -ret; + } + break; + } + +end: +error: + return ret; +} + + +/* + * main + */ +int main(int argc, char *argv[]) +{ + int ret; + + progname = argv[0] ? argv[0] : "lttng"; + + ret = set_signal_handler(); + if (ret < 0) { + clean_exit(ret); + } + + ret = parse_args(argc, argv); + if (ret != 0) { + clean_exit(ret); + } + + return 0; +} diff --git a/src/bin/lttng/uprobe.c b/src/bin/lttng/uprobe.c deleted file mode 100644 index 2a2890184..000000000 --- a/src/bin/lttng/uprobe.c +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (C) 2020 EfficiOS, Inc. - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include "uprobe.h" - -#include -#include -#include - -#include "common/compat/getenv.h" -#include "common/string-utils/string-utils.h" -#include "common/utils.h" -#include "lttng/constant.h" - -#include "command.h" - -/* - * Walk the directories in the PATH environment variable to find the target - * binary passed as parameter. - * - * On success, the full path of the binary is copied in binary_full_path out - * parameter. This buffer is allocated by the caller and must be at least - * LTTNG_PATH_MAX bytes long. - * On failure, returns -1; - */ -static -int walk_command_search_path(const char *binary, char *binary_full_path) -{ - char *tentative_binary_path = NULL; - char *command_search_path = NULL; - char *curr_search_dir_end = NULL; - char *curr_search_dir = NULL; - struct stat stat_output; - int ret = 0; - - command_search_path = lttng_secure_getenv("PATH"); - if (!command_search_path) { - ret = -1; - goto end; - } - - /* - * Duplicate the $PATH string as the char pointer returned by getenv() should - * not be modified. - */ - command_search_path = strdup(command_search_path); - if (!command_search_path) { - ret = -1; - goto end; - } - - /* - * This char array is used to concatenate path to binary to look for - * the binary. - */ - tentative_binary_path = zmalloc(LTTNG_PATH_MAX * sizeof(char)); - if (!tentative_binary_path) { - ret = -1; - goto alloc_error; - } - - curr_search_dir = command_search_path; - do { - /* - * Split on ':'. The return value of this call points to the - * matching character. - */ - curr_search_dir_end = strchr(curr_search_dir, ':'); - if (curr_search_dir_end != NULL) { - /* - * Add a NULL byte to the end of the first token so it - * can be used as a string. - */ - curr_search_dir_end[0] = '\0'; - } - - /* Empty the tentative path */ - memset(tentative_binary_path, 0, LTTNG_PATH_MAX * sizeof(char)); - - /* - * Build the tentative path to the binary using the current - * search directory and the name of the binary. - */ - ret = snprintf(tentative_binary_path, LTTNG_PATH_MAX, "%s/%s", - curr_search_dir, binary); - if (ret < 0) { - goto free_binary_path; - } - if (ret < LTTNG_PATH_MAX) { - /* - * Use STAT(2) to see if the file exists. - */ - ret = stat(tentative_binary_path, &stat_output); - if (ret == 0) { - /* - * Verify that it is a regular file or a - * symlink and not a special file (e.g. - * device). - */ - if (S_ISREG(stat_output.st_mode) - || S_ISLNK(stat_output.st_mode)) { - /* - * Found a match, set the out parameter - * and return success. - */ - ret = lttng_strncpy(binary_full_path, - tentative_binary_path, - LTTNG_PATH_MAX); - if (ret == -1) { - ERR("Source path does not fit " - "in destination buffer."); - } - goto free_binary_path; - } - } - } - /* Go to the next entry in the $PATH variable. */ - curr_search_dir = curr_search_dir_end + 1; - } while (curr_search_dir_end != NULL); - -free_binary_path: - free(tentative_binary_path); -alloc_error: - free(command_search_path); -end: - return ret; -} - -/* - * Check if the symbol field passed by the user is in fact an address or an - * offset from a symbol. Those two instrumentation types are not supported yet. - * It's expected to be a common mistake because of the existing --probe option - * that does support these formats. - * - * Here are examples of these unsupported formats for the --userspace-probe - * option: - * elf:/path/to/binary:0x400430 - * elf:/path/to/binary:4194364 - * elf:/path/to/binary:my_symbol+0x323 - * elf:/path/to/binary:my_symbol+43 - */ -static -int warn_userspace_probe_syntax(const char *symbol) -{ - int ret; - - /* Check if the symbol field is an hex address. */ - ret = sscanf(symbol, "0x%*x"); - if (ret > 0) { - /* If there is a match, print a warning and return an error. */ - ERR("Userspace probe on address not supported yet."); - ret = CMD_UNSUPPORTED; - goto error; - } - - /* Check if the symbol field is an decimal address. */ - ret = sscanf(symbol, "%*u"); - if (ret > 0) { - /* If there is a match, print a warning and return an error. */ - ERR("Userspace probe on address not supported yet."); - ret = CMD_UNSUPPORTED; - goto error; - } - - /* Check if the symbol field is symbol+hex_offset. */ - ret = sscanf(symbol, "%*[^+]+0x%*x"); - if (ret > 0) { - /* If there is a match, print a warning and return an error. */ - ERR("Userspace probe on symbol+offset not supported yet."); - ret = CMD_UNSUPPORTED; - goto error; - } - - /* Check if the symbol field is symbol+decimal_offset. */ - ret = sscanf(symbol, "%*[^+]+%*u"); - if (ret > 0) { - /* If there is a match, print a warning and return an error. */ - ERR("Userspace probe on symbol+offset not supported yet."); - ret = CMD_UNSUPPORTED; - goto error; - } - - ret = 0; - -error: - return ret; -} - -/* - * Parse userspace probe options - * Set the userspace probe fields in the lttng_event struct and set the - * target_path to the path to the binary. - */ -int parse_userspace_probe_opts(const char *opt, - struct lttng_userspace_probe_location **probe_location) -{ - int ret = CMD_SUCCESS; - size_t num_token = 0; - char *target_path = NULL; - char *unescaped_target_path = NULL; - char *real_target_path = NULL; - char *symbol_name = NULL, *probe_name = NULL, *provider_name = NULL; - struct lttng_userspace_probe_location *probe_location_local = NULL; - struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL; - struct lttng_dynamic_pointer_array tokens; - - LTTNG_ASSERT(opt); - - /* - * userspace probe fields are separated by ':'. - */ - ret = strutils_split(opt, ':', true, &tokens); - if (ret == 0) { - num_token = lttng_dynamic_pointer_array_get_count(&tokens); - } - - /* - * Early sanity check that the number of parameter is between 2 and 4 - * inclusively. - * elf:PATH:SYMBOL - * std:PATH:PROVIDER_NAME:PROBE_NAME - * PATH:SYMBOL (same behavior as ELF) - */ - if (ret < 0 || num_token < 2 || num_token > 4) { - ret = CMD_ERROR; - goto end; - } - - /* - * Looking up the first parameter will tell the technique to use to - * interpret the userspace probe/function description. - */ - switch (num_token) { - case 2: - /* When the probe type is omitted we assume ELF for now. */ - case 3: - if (num_token == 3 && strcmp(lttng_dynamic_pointer_array_get_pointer(&tokens, 0), "elf") == 0) { - target_path = lttng_dynamic_pointer_array_get_pointer(&tokens, 1); - symbol_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 2); - } else if (num_token == 2) { - target_path = lttng_dynamic_pointer_array_get_pointer(&tokens, 0); - symbol_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 1); - } else { - ret = CMD_ERROR; - goto end; - } - lookup_method = - lttng_userspace_probe_location_lookup_method_function_elf_create(); - if (!lookup_method) { - WARN("Failed to create ELF lookup method"); - ret = CMD_ERROR; - goto end; - } - break; - case 4: - if (strcmp(lttng_dynamic_pointer_array_get_pointer(&tokens, 0), "sdt") == 0) { - target_path = lttng_dynamic_pointer_array_get_pointer(&tokens, 1); - provider_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 2); - probe_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 3); - } else { - ret = CMD_ERROR; - goto end; - } - lookup_method = - lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create(); - if (!lookup_method) { - WARN("Failed to create SDT lookup method"); - ret = CMD_ERROR; - goto end; - } - break; - default: - ret = CMD_ERROR; - goto end; - } - - /* strutils_unescape_string allocates a new char *. */ - unescaped_target_path = strutils_unescape_string(target_path, 0); - if (!unescaped_target_path) { - ret = CMD_ERROR; - goto end; - } - - /* - * If there is not forward slash in the path. Walk the $PATH else - * expand. - */ - if (strchr(unescaped_target_path, '/') == NULL) { - /* Walk the $PATH variable to find the targeted binary. */ - real_target_path = zmalloc(LTTNG_PATH_MAX * sizeof(char)); - if (!real_target_path) { - PERROR("Error allocating path buffer"); - ret = CMD_ERROR; - goto end; - } - ret = walk_command_search_path(unescaped_target_path, real_target_path); - if (ret) { - ERR("Binary not found."); - ret = CMD_ERROR; - goto end; - } - } else { - /* - * Expand references to `/./` and `/../`. This function does not check - * if the file exists. This call returns an allocated buffer on - * success. - */ - real_target_path = utils_expand_path_keep_symlink(unescaped_target_path); - if (!real_target_path) { - ERR("Error expanding the path to binary."); - ret = CMD_ERROR; - goto end; - } - - /* - * Check if the file exists using access(2), If it does not, - * return an error. - */ - ret = access(real_target_path, F_OK); - if (ret) { - ERR("Cannot find binary at path: %s.", real_target_path); - ret = CMD_ERROR; - goto end; - } - } - - switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) { - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: - /* - * Check for common mistakes in userspace probe description syntax. - */ - ret = warn_userspace_probe_syntax(symbol_name); - if (ret) { - goto end; - } - - probe_location_local = lttng_userspace_probe_location_function_create( - real_target_path, symbol_name, lookup_method); - if (!probe_location_local) { - WARN("Failed to create function probe location"); - ret = CMD_ERROR; - goto end; - } - - /* Ownership transferred to probe_location. */ - lookup_method = NULL; - break; - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: - probe_location_local = lttng_userspace_probe_location_tracepoint_create( - real_target_path, provider_name, probe_name, lookup_method); - if (!probe_location_local) { - WARN("Failed to create function probe location"); - ret = CMD_ERROR; - goto end; - } - - /* Ownership transferred to probe_location. */ - lookup_method = NULL; - break; - default: - ret = CMD_ERROR; - goto end; - } - - /* - * Everything went fine, transfer ownership of probe location to - * caller. - */ - *probe_location = probe_location_local; - probe_location_local = NULL; - -end: - lttng_userspace_probe_location_destroy(probe_location_local); - lttng_userspace_probe_location_lookup_method_destroy(lookup_method); - lttng_dynamic_pointer_array_reset(&tokens); - /* - * Freeing both char * here makes the error handling simplier. free() - * performs not action if the pointer is NULL. - */ - free(real_target_path); - free(unescaped_target_path); - - return ret; -} diff --git a/src/bin/lttng/uprobe.cpp b/src/bin/lttng/uprobe.cpp new file mode 100644 index 000000000..7a119ae61 --- /dev/null +++ b/src/bin/lttng/uprobe.cpp @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2020 EfficiOS, Inc. + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "uprobe.h" + +#include +#include +#include + +#include "common/compat/getenv.h" +#include "common/string-utils/string-utils.h" +#include "common/utils.h" +#include "lttng/constant.h" + +#include "command.h" + +/* + * Walk the directories in the PATH environment variable to find the target + * binary passed as parameter. + * + * On success, the full path of the binary is copied in binary_full_path out + * parameter. This buffer is allocated by the caller and must be at least + * LTTNG_PATH_MAX bytes long. + * On failure, returns -1; + */ +static +int walk_command_search_path(const char *binary, char *binary_full_path) +{ + char *tentative_binary_path = NULL; + char *command_search_path = NULL; + char *curr_search_dir_end = NULL; + char *curr_search_dir = NULL; + struct stat stat_output; + int ret = 0; + + command_search_path = lttng_secure_getenv("PATH"); + if (!command_search_path) { + ret = -1; + goto end; + } + + /* + * Duplicate the $PATH string as the char pointer returned by getenv() should + * not be modified. + */ + command_search_path = strdup(command_search_path); + if (!command_search_path) { + ret = -1; + goto end; + } + + /* + * This char array is used to concatenate path to binary to look for + * the binary. + */ + tentative_binary_path = (char *) zmalloc(LTTNG_PATH_MAX * sizeof(char)); + if (!tentative_binary_path) { + ret = -1; + goto alloc_error; + } + + curr_search_dir = command_search_path; + do { + /* + * Split on ':'. The return value of this call points to the + * matching character. + */ + curr_search_dir_end = strchr(curr_search_dir, ':'); + if (curr_search_dir_end != NULL) { + /* + * Add a NULL byte to the end of the first token so it + * can be used as a string. + */ + curr_search_dir_end[0] = '\0'; + } + + /* Empty the tentative path */ + memset(tentative_binary_path, 0, LTTNG_PATH_MAX * sizeof(char)); + + /* + * Build the tentative path to the binary using the current + * search directory and the name of the binary. + */ + ret = snprintf(tentative_binary_path, LTTNG_PATH_MAX, "%s/%s", + curr_search_dir, binary); + if (ret < 0) { + goto free_binary_path; + } + if (ret < LTTNG_PATH_MAX) { + /* + * Use STAT(2) to see if the file exists. + */ + ret = stat(tentative_binary_path, &stat_output); + if (ret == 0) { + /* + * Verify that it is a regular file or a + * symlink and not a special file (e.g. + * device). + */ + if (S_ISREG(stat_output.st_mode) + || S_ISLNK(stat_output.st_mode)) { + /* + * Found a match, set the out parameter + * and return success. + */ + ret = lttng_strncpy(binary_full_path, + tentative_binary_path, + LTTNG_PATH_MAX); + if (ret == -1) { + ERR("Source path does not fit " + "in destination buffer."); + } + goto free_binary_path; + } + } + } + /* Go to the next entry in the $PATH variable. */ + curr_search_dir = curr_search_dir_end + 1; + } while (curr_search_dir_end != NULL); + +free_binary_path: + free(tentative_binary_path); +alloc_error: + free(command_search_path); +end: + return ret; +} + +/* + * Check if the symbol field passed by the user is in fact an address or an + * offset from a symbol. Those two instrumentation types are not supported yet. + * It's expected to be a common mistake because of the existing --probe option + * that does support these formats. + * + * Here are examples of these unsupported formats for the --userspace-probe + * option: + * elf:/path/to/binary:0x400430 + * elf:/path/to/binary:4194364 + * elf:/path/to/binary:my_symbol+0x323 + * elf:/path/to/binary:my_symbol+43 + */ +static +int warn_userspace_probe_syntax(const char *symbol) +{ + int ret; + + /* Check if the symbol field is an hex address. */ + ret = sscanf(symbol, "0x%*x"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on address not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + /* Check if the symbol field is an decimal address. */ + ret = sscanf(symbol, "%*u"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on address not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + /* Check if the symbol field is symbol+hex_offset. */ + ret = sscanf(symbol, "%*[^+]+0x%*x"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on symbol+offset not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + /* Check if the symbol field is symbol+decimal_offset. */ + ret = sscanf(symbol, "%*[^+]+%*u"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on symbol+offset not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + ret = 0; + +error: + return ret; +} + +/* + * Parse userspace probe options + * Set the userspace probe fields in the lttng_event struct and set the + * target_path to the path to the binary. + */ +int parse_userspace_probe_opts(const char *opt, + struct lttng_userspace_probe_location **probe_location) +{ + int ret = CMD_SUCCESS; + size_t num_token = 0; + char *target_path = NULL; + char *unescaped_target_path = NULL; + char *real_target_path = NULL; + char *symbol_name = NULL, *probe_name = NULL, *provider_name = NULL; + struct lttng_userspace_probe_location *probe_location_local = NULL; + struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL; + struct lttng_dynamic_pointer_array tokens; + + LTTNG_ASSERT(opt); + + /* + * userspace probe fields are separated by ':'. + */ + ret = strutils_split(opt, ':', true, &tokens); + if (ret == 0) { + num_token = lttng_dynamic_pointer_array_get_count(&tokens); + } + + /* + * Early sanity check that the number of parameter is between 2 and 4 + * inclusively. + * elf:PATH:SYMBOL + * std:PATH:PROVIDER_NAME:PROBE_NAME + * PATH:SYMBOL (same behavior as ELF) + */ + if (ret < 0 || num_token < 2 || num_token > 4) { + ret = CMD_ERROR; + goto end; + } + + /* + * Looking up the first parameter will tell the technique to use to + * interpret the userspace probe/function description. + */ + switch (num_token) { + case 2: + /* When the probe type is omitted we assume ELF for now. */ + case 3: + if (num_token == 3 && strcmp((const char *) lttng_dynamic_pointer_array_get_pointer(&tokens, 0), "elf") == 0) { + target_path = (char *) lttng_dynamic_pointer_array_get_pointer(&tokens, 1); + symbol_name = (char *) lttng_dynamic_pointer_array_get_pointer(&tokens, 2); + } else if (num_token == 2) { + target_path = (char *) lttng_dynamic_pointer_array_get_pointer(&tokens, 0); + symbol_name = (char *) lttng_dynamic_pointer_array_get_pointer(&tokens, 1); + } else { + ret = CMD_ERROR; + goto end; + } + lookup_method = + lttng_userspace_probe_location_lookup_method_function_elf_create(); + if (!lookup_method) { + WARN("Failed to create ELF lookup method"); + ret = CMD_ERROR; + goto end; + } + break; + case 4: + if (strcmp((const char *) lttng_dynamic_pointer_array_get_pointer(&tokens, 0), "sdt") == 0) { + target_path = (char *) lttng_dynamic_pointer_array_get_pointer(&tokens, 1); + provider_name = (char *) lttng_dynamic_pointer_array_get_pointer(&tokens, 2); + probe_name = (char *) lttng_dynamic_pointer_array_get_pointer(&tokens, 3); + } else { + ret = CMD_ERROR; + goto end; + } + lookup_method = + lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create(); + if (!lookup_method) { + WARN("Failed to create SDT lookup method"); + ret = CMD_ERROR; + goto end; + } + break; + default: + ret = CMD_ERROR; + goto end; + } + + /* strutils_unescape_string allocates a new char *. */ + unescaped_target_path = strutils_unescape_string(target_path, 0); + if (!unescaped_target_path) { + ret = CMD_ERROR; + goto end; + } + + /* + * If there is not forward slash in the path. Walk the $PATH else + * expand. + */ + if (strchr(unescaped_target_path, '/') == NULL) { + /* Walk the $PATH variable to find the targeted binary. */ + real_target_path = (char *) zmalloc(LTTNG_PATH_MAX * sizeof(char)); + if (!real_target_path) { + PERROR("Error allocating path buffer"); + ret = CMD_ERROR; + goto end; + } + ret = walk_command_search_path(unescaped_target_path, real_target_path); + if (ret) { + ERR("Binary not found."); + ret = CMD_ERROR; + goto end; + } + } else { + /* + * Expand references to `/./` and `/../`. This function does not check + * if the file exists. This call returns an allocated buffer on + * success. + */ + real_target_path = utils_expand_path_keep_symlink(unescaped_target_path); + if (!real_target_path) { + ERR("Error expanding the path to binary."); + ret = CMD_ERROR; + goto end; + } + + /* + * Check if the file exists using access(2), If it does not, + * return an error. + */ + ret = access(real_target_path, F_OK); + if (ret) { + ERR("Cannot find binary at path: %s.", real_target_path); + ret = CMD_ERROR; + goto end; + } + } + + switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + /* + * Check for common mistakes in userspace probe description syntax. + */ + ret = warn_userspace_probe_syntax(symbol_name); + if (ret) { + goto end; + } + + probe_location_local = lttng_userspace_probe_location_function_create( + real_target_path, symbol_name, lookup_method); + if (!probe_location_local) { + WARN("Failed to create function probe location"); + ret = CMD_ERROR; + goto end; + } + + /* Ownership transferred to probe_location. */ + lookup_method = NULL; + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + probe_location_local = lttng_userspace_probe_location_tracepoint_create( + real_target_path, provider_name, probe_name, lookup_method); + if (!probe_location_local) { + WARN("Failed to create function probe location"); + ret = CMD_ERROR; + goto end; + } + + /* Ownership transferred to probe_location. */ + lookup_method = NULL; + break; + default: + ret = CMD_ERROR; + goto end; + } + + /* + * Everything went fine, transfer ownership of probe location to + * caller. + */ + *probe_location = probe_location_local; + probe_location_local = NULL; + +end: + lttng_userspace_probe_location_destroy(probe_location_local); + lttng_userspace_probe_location_lookup_method_destroy(lookup_method); + lttng_dynamic_pointer_array_reset(&tokens); + /* + * Freeing both char * here makes the error handling simplier. free() + * performs not action if the pointer is NULL. + */ + free(real_target_path); + free(unescaped_target_path); + + return ret; +} diff --git a/src/bin/lttng/utils.c b/src/bin/lttng/utils.c deleted file mode 100644 index 6440ca7df..000000000 --- a/src/bin/lttng/utils.c +++ /dev/null @@ -1,675 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "conf.h" -#include "utils.h" -#include "command.h" - -static const char *str_all = "ALL"; -static const char *str_tracepoint = "Tracepoint"; -static const char *str_syscall = "Syscall"; -static const char *str_probe = "Probe"; -static const char *str_userspace_probe = "Userspace Probe"; -static const char *str_function = "Function"; - -static -char *_get_session_name(int quiet) -{ - const char *path; - char *session_name = NULL; - - /* Get path to config file */ - path = utils_get_home_dir(); - if (path == NULL) { - goto error; - } - - /* Get session name from config */ - session_name = quiet ? config_read_session_name_quiet(path) : - config_read_session_name(path); - if (session_name == NULL) { - goto error; - } - - DBG2("Config file path found: %s", path); - DBG("Session name found: %s", session_name); - return session_name; - -error: - return NULL; -} - -/* - * get_session_name - * - * Return allocated string with the session name found in the config - * directory. - */ -char *get_session_name(void) -{ - return _get_session_name(0); -} - -/* - * get_session_name_quiet (no warnings/errors emitted) - * - * Return allocated string with the session name found in the config - * directory. - */ -char *get_session_name_quiet(void) -{ - return _get_session_name(1); -} - -/* - * list_commands - * - * List commands line by line. This is mostly for bash auto completion and to - * avoid difficult parsing. - */ -void list_commands(struct cmd_struct *commands, FILE *ofp) -{ - int i = 0; - struct cmd_struct *cmd = NULL; - - cmd = &commands[i]; - while (cmd->name != NULL) { - fprintf(ofp, "%s\n", cmd->name); - i++; - cmd = &commands[i]; - } -} - -/* - * list_cmd_options - * - * Prints a simple list of the options available to a command. This is intended - * to be easily parsed for bash completion. - */ -void list_cmd_options(FILE *ofp, struct poptOption *options) -{ - int i; - struct poptOption *option = NULL; - - for (i = 0; options[i].longName != NULL; i++) { - option = &options[i]; - - fprintf(ofp, "--%s\n", option->longName); - - if (isprint(option->shortName)) { - fprintf(ofp, "-%c\n", option->shortName); - } - } -} - -/* - * Same as list_cmd_options, but for options specified for argpar. - */ -void list_cmd_options_argpar(FILE *ofp, const struct argpar_opt_descr *options) -{ - int i; - - for (i = 0; options[i].long_name != NULL; i++) { - const struct argpar_opt_descr *option = &options[i]; - - fprintf(ofp, "--%s\n", option->long_name); - - if (isprint(option->short_name)) { - fprintf(ofp, "-%c\n", option->short_name); - } - } -} - -/* - * fls: returns the position of the most significant bit. - * Returns 0 if no bit is set, else returns the position of the most - * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit). - */ -#if defined(__i386) || defined(__x86_64) -static inline -unsigned int fls_u32(uint32_t x) -{ - int r; - - asm("bsrl %1,%0\n\t" - "jnz 1f\n\t" - "movl $-1,%0\n\t" - "1:\n\t" - : "=r" (r) : "rm" (x)); - return r + 1; -} -#define HAS_FLS_U32 -#endif - -#if defined(__x86_64) && defined(__LP64__) -static inline -unsigned int fls_u64(uint64_t x) -{ - long r; - - asm("bsrq %1,%0\n\t" - "jnz 1f\n\t" - "movq $-1,%0\n\t" - "1:\n\t" - : "=r" (r) : "rm" (x)); - return r + 1; -} -#define HAS_FLS_U64 -#endif - -#ifndef HAS_FLS_U64 -static __attribute__((unused)) -unsigned int fls_u64(uint64_t x) -{ - unsigned int r = 64; - - if (!x) - return 0; - - if (!(x & 0xFFFFFFFF00000000ULL)) { - x <<= 32; - r -= 32; - } - if (!(x & 0xFFFF000000000000ULL)) { - x <<= 16; - r -= 16; - } - if (!(x & 0xFF00000000000000ULL)) { - x <<= 8; - r -= 8; - } - if (!(x & 0xF000000000000000ULL)) { - x <<= 4; - r -= 4; - } - if (!(x & 0xC000000000000000ULL)) { - x <<= 2; - r -= 2; - } - if (!(x & 0x8000000000000000ULL)) { - x <<= 1; - r -= 1; - } - return r; -} -#endif - -#ifndef HAS_FLS_U32 -static __attribute__((unused)) -unsigned int fls_u32(uint32_t x) -{ - unsigned int r = 32; - - if (!x) - return 0; - if (!(x & 0xFFFF0000U)) { - x <<= 16; - r -= 16; - } - if (!(x & 0xFF000000U)) { - x <<= 8; - r -= 8; - } - if (!(x & 0xF0000000U)) { - x <<= 4; - r -= 4; - } - if (!(x & 0xC0000000U)) { - x <<= 2; - r -= 2; - } - if (!(x & 0x80000000U)) { - x <<= 1; - r -= 1; - } - return r; -} -#endif - -static -unsigned int fls_ulong(unsigned long x) -{ -#if (CAA_BITS_PER_LONG == 32) - return fls_u32(x); -#else - return fls_u64(x); -#endif -} - -/* - * Return the minimum order for which x <= (1UL << order). - * Return -1 if x is 0. - */ -int get_count_order_u32(uint32_t x) -{ - if (!x) - return -1; - - return fls_u32(x - 1); -} - -/* - * Return the minimum order for which x <= (1UL << order). - * Return -1 if x is 0. - */ -int get_count_order_u64(uint64_t x) -{ - if (!x) - return -1; - - return fls_u64(x - 1); -} - -/* - * Return the minimum order for which x <= (1UL << order). - * Return -1 if x is 0. - */ -int get_count_order_ulong(unsigned long x) -{ - if (!x) - return -1; - - return fls_ulong(x - 1); -} - -const char *get_event_type_str(enum lttng_event_type type) -{ - const char *str_event_type; - - switch (type) { - case LTTNG_EVENT_ALL: - str_event_type = str_all; - break; - case LTTNG_EVENT_TRACEPOINT: - str_event_type = str_tracepoint; - break; - case LTTNG_EVENT_SYSCALL: - str_event_type = str_syscall; - break; - case LTTNG_EVENT_PROBE: - str_event_type = str_probe; - break; - case LTTNG_EVENT_USERSPACE_PROBE: - str_event_type = str_userspace_probe; - break; - case LTTNG_EVENT_FUNCTION: - str_event_type = str_function; - break; - default: - /* Should not have an unknown event type or else define it. */ - abort(); - } - - return str_event_type; -} - -/* - * Spawn a lttng relayd daemon by forking and execv. - */ -int spawn_relayd(const char *pathname, int port) -{ - int ret = 0; - pid_t pid; - char url[255]; - - if (!port) { - port = DEFAULT_NETWORK_VIEWER_PORT; - } - - ret = snprintf(url, sizeof(url), "tcp://localhost:%d", port); - if (ret < 0) { - goto end; - } - - MSG("Spawning a relayd daemon"); - pid = fork(); - if (pid == 0) { - /* - * Spawn session daemon and tell - * it to signal us when ready. - */ - execlp(pathname, "lttng-relayd", "-L", url, NULL); - /* execlp only returns if error happened */ - if (errno == ENOENT) { - ERR("No relayd found. Use --relayd-path."); - } else { - PERROR("execlp"); - } - kill(getppid(), SIGTERM); /* wake parent */ - exit(EXIT_FAILURE); - } else if (pid > 0) { - goto end; - } else { - PERROR("fork"); - ret = -1; - goto end; - } - -end: - return ret; -} - -/* - * Check if relayd is alive. - * - * Return 1 if found else 0 if NOT found. Negative value on error. - */ -int check_relayd(void) -{ - int ret, fd; - struct sockaddr_in sin; - - fd = socket(AF_INET, SOCK_STREAM, 0); - if (fd < 0) { - PERROR("socket check relayd"); - ret = -1; - goto error_socket; - } - - sin.sin_family = AF_INET; - sin.sin_port = htons(DEFAULT_NETWORK_VIEWER_PORT); - ret = inet_pton(sin.sin_family, "127.0.0.1", &sin.sin_addr); - if (ret < 1) { - PERROR("inet_pton check relayd"); - ret = -1; - goto error; - } - - /* - * A successful connect means the relayd exists thus returning 0 else a - * negative value means it does NOT exists. - */ - ret = connect(fd, (struct sockaddr *) &sin, sizeof(sin)); - if (ret < 0) { - /* Not found. */ - ret = 0; - } else { - /* Already spawned. */ - ret = 1; - } - -error: - if (close(fd) < 0) { - PERROR("close relayd fd"); - } -error_socket: - return ret; -} - -int print_missing_or_multiple_domains(unsigned int domain_count, - bool include_agent_domains) -{ - int ret = 0; - - if (domain_count == 0) { - ERR("Please specify a domain (--kernel/--userspace%s).", - include_agent_domains ? - "/--jul/--log4j/--python" : - ""); - ret = -1; - } else if (domain_count > 1) { - ERR("Only one domain must be specified."); - ret = -1; - } - - return ret; -} - -/* - * Get the discarded events and lost packet counts. - */ -void print_session_stats(const char *session_name) -{ - char *str; - const int ret = get_session_stats_str(session_name, &str); - - if (ret >= 0 && str) { - MSG("%s", str); - free(str); - } -} - -int get_session_stats_str(const char *session_name, char **out_str) -{ - int count, nb_domains, domain_idx, channel_idx, session_idx, ret; - struct lttng_domain *domains = NULL; - struct lttng_channel *channels = NULL; - uint64_t discarded_events_total = 0, lost_packets_total = 0; - struct lttng_session *sessions = NULL; - const struct lttng_session *selected_session = NULL; - char *stats_str = NULL; - bool print_discarded_events = false, print_lost_packets = false; - - count = lttng_list_sessions(&sessions); - if (count < 1) { - ERR("Failed to retrieve session descriptions while printing session statistics."); - ret = -1; - goto end; - } - - /* Identify the currently-selected sessions. */ - for (session_idx = 0; session_idx < count; session_idx++) { - if (!strcmp(session_name, sessions[session_idx].name)) { - selected_session = &sessions[session_idx]; - break; - } - } - if (!selected_session) { - ERR("Failed to retrieve session \"%s\" description while printing session statistics.", session_name); - ret = -1; - goto end; - } - - nb_domains = lttng_list_domains(session_name, &domains); - if (nb_domains < 0) { - ret = -1; - goto end; - } - for (domain_idx = 0; domain_idx < nb_domains; domain_idx++) { - struct lttng_handle *handle = lttng_create_handle(session_name, - &domains[domain_idx]); - - if (!handle) { - ERR("Failed to create session handle while printing session statistics."); - ret = -1; - goto end; - } - - free(channels); - channels = NULL; - count = lttng_list_channels(handle, &channels); - for (channel_idx = 0; channel_idx < count; channel_idx++) { - uint64_t discarded_events = 0, lost_packets = 0; - struct lttng_channel *channel = &channels[channel_idx]; - - ret = lttng_channel_get_discarded_event_count(channel, - &discarded_events); - if (ret) { - ERR("Failed to retrieve discarded event count from channel %s", - channel->name); - } - - ret = lttng_channel_get_lost_packet_count(channel, - &lost_packets); - if (ret) { - ERR("Failed to retrieve lost packet count from channel %s", - channel->name); - } - - discarded_events_total += discarded_events; - lost_packets_total += lost_packets; - } - lttng_destroy_handle(handle); - } - - print_discarded_events = discarded_events_total > 0 && - !selected_session->snapshot_mode; - print_lost_packets = lost_packets_total > 0 && - !selected_session->snapshot_mode; - - if (print_discarded_events && print_lost_packets) { - ret = asprintf(&stats_str, - "Warning: %" PRIu64 - " events were discarded and %" PRIu64 - " packets were lost, please refer to " - "the documentation on channel configuration.", - discarded_events_total, lost_packets_total); - } else if (print_discarded_events) { - ret = asprintf(&stats_str, - "Warning: %" PRIu64 - " events were discarded, please refer to " - "the documentation on channel configuration.", - discarded_events_total); - } else if (print_lost_packets) { - ret = asprintf(&stats_str, - "Warning: %" PRIu64 - " packets were lost, please refer to " - "the documentation on channel configuration.", - lost_packets_total); - } else { - ret = 0; - } - - if (ret < 0) { - ERR("Failed to format lost packet and discarded events statistics"); - } else { - *out_str = stats_str; - ret = 0; - } -end: - free(sessions); - free(channels); - free(domains); - return ret; -} - -int show_cmd_help(const char *cmd_name, const char *help_msg) -{ - int ret; - char page_name[32]; - - ret = sprintf(page_name, "lttng-%s", cmd_name); - LTTNG_ASSERT(ret > 0 && ret < 32); - ret = utils_show_help(1, page_name, help_msg); - if (ret && !help_msg) { - ERR("Cannot view man page `lttng-%s(1)`", cmd_name); - perror("exec"); - } - - return ret; -} - -int print_trace_archive_location( - const struct lttng_trace_archive_location *location, - const char *session_name) -{ - int ret = 0; - enum lttng_trace_archive_location_type location_type; - enum lttng_trace_archive_location_status status; - bool printed_location = false; - - location_type = lttng_trace_archive_location_get_type(location); - - _MSG("Trace chunk archive for session %s is now readable", - session_name); - switch (location_type) { - case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL: - { - const char *absolute_path; - - status = lttng_trace_archive_location_local_get_absolute_path( - location, &absolute_path); - if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { - ret = -1; - goto end; - } - MSG(" at %s", absolute_path); - printed_location = true; - break; - } - case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY: - { - uint16_t control_port, data_port; - const char *host, *relative_path, *protocol_str; - enum lttng_trace_archive_location_relay_protocol_type protocol; - - /* Fetch all relay location parameters. */ - status = lttng_trace_archive_location_relay_get_protocol_type( - location, &protocol); - if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { - ret = -1; - goto end; - } - - status = lttng_trace_archive_location_relay_get_host( - location, &host); - if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { - ret = -1; - goto end; - } - - status = lttng_trace_archive_location_relay_get_control_port( - location, &control_port); - if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { - ret = -1; - goto end; - } - - status = lttng_trace_archive_location_relay_get_data_port( - location, &data_port); - if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { - ret = -1; - goto end; - } - - status = lttng_trace_archive_location_relay_get_relative_path( - location, &relative_path); - if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { - ret = -1; - goto end; - } - - switch (protocol) { - case LTTNG_TRACE_ARCHIVE_LOCATION_RELAY_PROTOCOL_TYPE_TCP: - protocol_str = "tcp"; - break; - default: - protocol_str = "unknown"; - break; - } - - MSG(" on relay %s://%s/%s [control port %" PRIu16 ", data port %" - PRIu16 "]", protocol_str, host, - relative_path, control_port, data_port); - printed_location = true; - break; - } - default: - break; - } -end: - if (!printed_location) { - MSG(" at an unknown location"); - } - return ret; -} diff --git a/src/bin/lttng/utils.cpp b/src/bin/lttng/utils.cpp new file mode 100644 index 000000000..6440ca7df --- /dev/null +++ b/src/bin/lttng/utils.cpp @@ -0,0 +1,675 @@ +/* + * Copyright (C) 2011 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "conf.h" +#include "utils.h" +#include "command.h" + +static const char *str_all = "ALL"; +static const char *str_tracepoint = "Tracepoint"; +static const char *str_syscall = "Syscall"; +static const char *str_probe = "Probe"; +static const char *str_userspace_probe = "Userspace Probe"; +static const char *str_function = "Function"; + +static +char *_get_session_name(int quiet) +{ + const char *path; + char *session_name = NULL; + + /* Get path to config file */ + path = utils_get_home_dir(); + if (path == NULL) { + goto error; + } + + /* Get session name from config */ + session_name = quiet ? config_read_session_name_quiet(path) : + config_read_session_name(path); + if (session_name == NULL) { + goto error; + } + + DBG2("Config file path found: %s", path); + DBG("Session name found: %s", session_name); + return session_name; + +error: + return NULL; +} + +/* + * get_session_name + * + * Return allocated string with the session name found in the config + * directory. + */ +char *get_session_name(void) +{ + return _get_session_name(0); +} + +/* + * get_session_name_quiet (no warnings/errors emitted) + * + * Return allocated string with the session name found in the config + * directory. + */ +char *get_session_name_quiet(void) +{ + return _get_session_name(1); +} + +/* + * list_commands + * + * List commands line by line. This is mostly for bash auto completion and to + * avoid difficult parsing. + */ +void list_commands(struct cmd_struct *commands, FILE *ofp) +{ + int i = 0; + struct cmd_struct *cmd = NULL; + + cmd = &commands[i]; + while (cmd->name != NULL) { + fprintf(ofp, "%s\n", cmd->name); + i++; + cmd = &commands[i]; + } +} + +/* + * list_cmd_options + * + * Prints a simple list of the options available to a command. This is intended + * to be easily parsed for bash completion. + */ +void list_cmd_options(FILE *ofp, struct poptOption *options) +{ + int i; + struct poptOption *option = NULL; + + for (i = 0; options[i].longName != NULL; i++) { + option = &options[i]; + + fprintf(ofp, "--%s\n", option->longName); + + if (isprint(option->shortName)) { + fprintf(ofp, "-%c\n", option->shortName); + } + } +} + +/* + * Same as list_cmd_options, but for options specified for argpar. + */ +void list_cmd_options_argpar(FILE *ofp, const struct argpar_opt_descr *options) +{ + int i; + + for (i = 0; options[i].long_name != NULL; i++) { + const struct argpar_opt_descr *option = &options[i]; + + fprintf(ofp, "--%s\n", option->long_name); + + if (isprint(option->short_name)) { + fprintf(ofp, "-%c\n", option->short_name); + } + } +} + +/* + * fls: returns the position of the most significant bit. + * Returns 0 if no bit is set, else returns the position of the most + * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit). + */ +#if defined(__i386) || defined(__x86_64) +static inline +unsigned int fls_u32(uint32_t x) +{ + int r; + + asm("bsrl %1,%0\n\t" + "jnz 1f\n\t" + "movl $-1,%0\n\t" + "1:\n\t" + : "=r" (r) : "rm" (x)); + return r + 1; +} +#define HAS_FLS_U32 +#endif + +#if defined(__x86_64) && defined(__LP64__) +static inline +unsigned int fls_u64(uint64_t x) +{ + long r; + + asm("bsrq %1,%0\n\t" + "jnz 1f\n\t" + "movq $-1,%0\n\t" + "1:\n\t" + : "=r" (r) : "rm" (x)); + return r + 1; +} +#define HAS_FLS_U64 +#endif + +#ifndef HAS_FLS_U64 +static __attribute__((unused)) +unsigned int fls_u64(uint64_t x) +{ + unsigned int r = 64; + + if (!x) + return 0; + + if (!(x & 0xFFFFFFFF00000000ULL)) { + x <<= 32; + r -= 32; + } + if (!(x & 0xFFFF000000000000ULL)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xFF00000000000000ULL)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xF000000000000000ULL)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xC000000000000000ULL)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x8000000000000000ULL)) { + x <<= 1; + r -= 1; + } + return r; +} +#endif + +#ifndef HAS_FLS_U32 +static __attribute__((unused)) +unsigned int fls_u32(uint32_t x) +{ + unsigned int r = 32; + + if (!x) + return 0; + if (!(x & 0xFFFF0000U)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xFF000000U)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xF0000000U)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xC0000000U)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x80000000U)) { + x <<= 1; + r -= 1; + } + return r; +} +#endif + +static +unsigned int fls_ulong(unsigned long x) +{ +#if (CAA_BITS_PER_LONG == 32) + return fls_u32(x); +#else + return fls_u64(x); +#endif +} + +/* + * Return the minimum order for which x <= (1UL << order). + * Return -1 if x is 0. + */ +int get_count_order_u32(uint32_t x) +{ + if (!x) + return -1; + + return fls_u32(x - 1); +} + +/* + * Return the minimum order for which x <= (1UL << order). + * Return -1 if x is 0. + */ +int get_count_order_u64(uint64_t x) +{ + if (!x) + return -1; + + return fls_u64(x - 1); +} + +/* + * Return the minimum order for which x <= (1UL << order). + * Return -1 if x is 0. + */ +int get_count_order_ulong(unsigned long x) +{ + if (!x) + return -1; + + return fls_ulong(x - 1); +} + +const char *get_event_type_str(enum lttng_event_type type) +{ + const char *str_event_type; + + switch (type) { + case LTTNG_EVENT_ALL: + str_event_type = str_all; + break; + case LTTNG_EVENT_TRACEPOINT: + str_event_type = str_tracepoint; + break; + case LTTNG_EVENT_SYSCALL: + str_event_type = str_syscall; + break; + case LTTNG_EVENT_PROBE: + str_event_type = str_probe; + break; + case LTTNG_EVENT_USERSPACE_PROBE: + str_event_type = str_userspace_probe; + break; + case LTTNG_EVENT_FUNCTION: + str_event_type = str_function; + break; + default: + /* Should not have an unknown event type or else define it. */ + abort(); + } + + return str_event_type; +} + +/* + * Spawn a lttng relayd daemon by forking and execv. + */ +int spawn_relayd(const char *pathname, int port) +{ + int ret = 0; + pid_t pid; + char url[255]; + + if (!port) { + port = DEFAULT_NETWORK_VIEWER_PORT; + } + + ret = snprintf(url, sizeof(url), "tcp://localhost:%d", port); + if (ret < 0) { + goto end; + } + + MSG("Spawning a relayd daemon"); + pid = fork(); + if (pid == 0) { + /* + * Spawn session daemon and tell + * it to signal us when ready. + */ + execlp(pathname, "lttng-relayd", "-L", url, NULL); + /* execlp only returns if error happened */ + if (errno == ENOENT) { + ERR("No relayd found. Use --relayd-path."); + } else { + PERROR("execlp"); + } + kill(getppid(), SIGTERM); /* wake parent */ + exit(EXIT_FAILURE); + } else if (pid > 0) { + goto end; + } else { + PERROR("fork"); + ret = -1; + goto end; + } + +end: + return ret; +} + +/* + * Check if relayd is alive. + * + * Return 1 if found else 0 if NOT found. Negative value on error. + */ +int check_relayd(void) +{ + int ret, fd; + struct sockaddr_in sin; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + PERROR("socket check relayd"); + ret = -1; + goto error_socket; + } + + sin.sin_family = AF_INET; + sin.sin_port = htons(DEFAULT_NETWORK_VIEWER_PORT); + ret = inet_pton(sin.sin_family, "127.0.0.1", &sin.sin_addr); + if (ret < 1) { + PERROR("inet_pton check relayd"); + ret = -1; + goto error; + } + + /* + * A successful connect means the relayd exists thus returning 0 else a + * negative value means it does NOT exists. + */ + ret = connect(fd, (struct sockaddr *) &sin, sizeof(sin)); + if (ret < 0) { + /* Not found. */ + ret = 0; + } else { + /* Already spawned. */ + ret = 1; + } + +error: + if (close(fd) < 0) { + PERROR("close relayd fd"); + } +error_socket: + return ret; +} + +int print_missing_or_multiple_domains(unsigned int domain_count, + bool include_agent_domains) +{ + int ret = 0; + + if (domain_count == 0) { + ERR("Please specify a domain (--kernel/--userspace%s).", + include_agent_domains ? + "/--jul/--log4j/--python" : + ""); + ret = -1; + } else if (domain_count > 1) { + ERR("Only one domain must be specified."); + ret = -1; + } + + return ret; +} + +/* + * Get the discarded events and lost packet counts. + */ +void print_session_stats(const char *session_name) +{ + char *str; + const int ret = get_session_stats_str(session_name, &str); + + if (ret >= 0 && str) { + MSG("%s", str); + free(str); + } +} + +int get_session_stats_str(const char *session_name, char **out_str) +{ + int count, nb_domains, domain_idx, channel_idx, session_idx, ret; + struct lttng_domain *domains = NULL; + struct lttng_channel *channels = NULL; + uint64_t discarded_events_total = 0, lost_packets_total = 0; + struct lttng_session *sessions = NULL; + const struct lttng_session *selected_session = NULL; + char *stats_str = NULL; + bool print_discarded_events = false, print_lost_packets = false; + + count = lttng_list_sessions(&sessions); + if (count < 1) { + ERR("Failed to retrieve session descriptions while printing session statistics."); + ret = -1; + goto end; + } + + /* Identify the currently-selected sessions. */ + for (session_idx = 0; session_idx < count; session_idx++) { + if (!strcmp(session_name, sessions[session_idx].name)) { + selected_session = &sessions[session_idx]; + break; + } + } + if (!selected_session) { + ERR("Failed to retrieve session \"%s\" description while printing session statistics.", session_name); + ret = -1; + goto end; + } + + nb_domains = lttng_list_domains(session_name, &domains); + if (nb_domains < 0) { + ret = -1; + goto end; + } + for (domain_idx = 0; domain_idx < nb_domains; domain_idx++) { + struct lttng_handle *handle = lttng_create_handle(session_name, + &domains[domain_idx]); + + if (!handle) { + ERR("Failed to create session handle while printing session statistics."); + ret = -1; + goto end; + } + + free(channels); + channels = NULL; + count = lttng_list_channels(handle, &channels); + for (channel_idx = 0; channel_idx < count; channel_idx++) { + uint64_t discarded_events = 0, lost_packets = 0; + struct lttng_channel *channel = &channels[channel_idx]; + + ret = lttng_channel_get_discarded_event_count(channel, + &discarded_events); + if (ret) { + ERR("Failed to retrieve discarded event count from channel %s", + channel->name); + } + + ret = lttng_channel_get_lost_packet_count(channel, + &lost_packets); + if (ret) { + ERR("Failed to retrieve lost packet count from channel %s", + channel->name); + } + + discarded_events_total += discarded_events; + lost_packets_total += lost_packets; + } + lttng_destroy_handle(handle); + } + + print_discarded_events = discarded_events_total > 0 && + !selected_session->snapshot_mode; + print_lost_packets = lost_packets_total > 0 && + !selected_session->snapshot_mode; + + if (print_discarded_events && print_lost_packets) { + ret = asprintf(&stats_str, + "Warning: %" PRIu64 + " events were discarded and %" PRIu64 + " packets were lost, please refer to " + "the documentation on channel configuration.", + discarded_events_total, lost_packets_total); + } else if (print_discarded_events) { + ret = asprintf(&stats_str, + "Warning: %" PRIu64 + " events were discarded, please refer to " + "the documentation on channel configuration.", + discarded_events_total); + } else if (print_lost_packets) { + ret = asprintf(&stats_str, + "Warning: %" PRIu64 + " packets were lost, please refer to " + "the documentation on channel configuration.", + lost_packets_total); + } else { + ret = 0; + } + + if (ret < 0) { + ERR("Failed to format lost packet and discarded events statistics"); + } else { + *out_str = stats_str; + ret = 0; + } +end: + free(sessions); + free(channels); + free(domains); + return ret; +} + +int show_cmd_help(const char *cmd_name, const char *help_msg) +{ + int ret; + char page_name[32]; + + ret = sprintf(page_name, "lttng-%s", cmd_name); + LTTNG_ASSERT(ret > 0 && ret < 32); + ret = utils_show_help(1, page_name, help_msg); + if (ret && !help_msg) { + ERR("Cannot view man page `lttng-%s(1)`", cmd_name); + perror("exec"); + } + + return ret; +} + +int print_trace_archive_location( + const struct lttng_trace_archive_location *location, + const char *session_name) +{ + int ret = 0; + enum lttng_trace_archive_location_type location_type; + enum lttng_trace_archive_location_status status; + bool printed_location = false; + + location_type = lttng_trace_archive_location_get_type(location); + + _MSG("Trace chunk archive for session %s is now readable", + session_name); + switch (location_type) { + case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL: + { + const char *absolute_path; + + status = lttng_trace_archive_location_local_get_absolute_path( + location, &absolute_path); + if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { + ret = -1; + goto end; + } + MSG(" at %s", absolute_path); + printed_location = true; + break; + } + case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY: + { + uint16_t control_port, data_port; + const char *host, *relative_path, *protocol_str; + enum lttng_trace_archive_location_relay_protocol_type protocol; + + /* Fetch all relay location parameters. */ + status = lttng_trace_archive_location_relay_get_protocol_type( + location, &protocol); + if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { + ret = -1; + goto end; + } + + status = lttng_trace_archive_location_relay_get_host( + location, &host); + if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { + ret = -1; + goto end; + } + + status = lttng_trace_archive_location_relay_get_control_port( + location, &control_port); + if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { + ret = -1; + goto end; + } + + status = lttng_trace_archive_location_relay_get_data_port( + location, &data_port); + if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { + ret = -1; + goto end; + } + + status = lttng_trace_archive_location_relay_get_relative_path( + location, &relative_path); + if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { + ret = -1; + goto end; + } + + switch (protocol) { + case LTTNG_TRACE_ARCHIVE_LOCATION_RELAY_PROTOCOL_TYPE_TCP: + protocol_str = "tcp"; + break; + default: + protocol_str = "unknown"; + break; + } + + MSG(" on relay %s://%s/%s [control port %" PRIu16 ", data port %" + PRIu16 "]", protocol_str, host, + relative_path, control_port, data_port); + printed_location = true; + break; + } + default: + break; + } +end: + if (!printed_location) { + MSG(" at an unknown location"); + } + return ret; +} diff --git a/src/common/argpar/argpar.h b/src/common/argpar/argpar.h index 43dbd0d56..4bfc51d2b 100644 --- a/src/common/argpar/argpar.h +++ b/src/common/argpar/argpar.h @@ -9,6 +9,10 @@ #include +#if defined(__cplusplus) +extern "C" { +#endif + /* * argpar is a library that provides facilities for argument parsing. * @@ -310,5 +314,8 @@ void argpar_item_destroy(struct argpar_item *item); _item = NULL; \ } +#if defined(__cplusplus) +} +#endif #endif /* BABELTRACE_ARGPAR_H */ diff --git a/src/common/compat/string.h b/src/common/compat/string.h index a18d9e7ca..c9aa7f7fa 100644 --- a/src/common/compat/string.h +++ b/src/common/compat/string.h @@ -113,7 +113,7 @@ static inline int lttng_fls(int val) static inline void *lttng_memrchr(const void *s, int c, size_t n) { - return memrchr(s, c, n); + return (void *) memrchr(s, c, n); } #else static inline diff --git a/src/common/dynamic-array.h b/src/common/dynamic-array.h index 4ce2316f1..a37077404 100644 --- a/src/common/dynamic-array.h +++ b/src/common/dynamic-array.h @@ -10,6 +10,10 @@ #include +#if defined(__cplusplus) +extern "C" { +#endif + typedef void (*lttng_dynamic_array_element_destructor)(void *element); typedef void (*lttng_dynamic_pointer_array_destructor)(void *ptr); @@ -127,7 +131,7 @@ static inline void *lttng_dynamic_pointer_array_get_pointer( const struct lttng_dynamic_pointer_array *array, size_t index) { - void **element = lttng_dynamic_array_get_element(&array->array, index); + void **element = (void **) lttng_dynamic_array_get_element(&array->array, index); return *element; } @@ -141,7 +145,7 @@ static inline void *lttng_dynamic_pointer_array_steal_pointer( struct lttng_dynamic_pointer_array *array, size_t index) { - void **p_element = lttng_dynamic_array_get_element(&array->array, index); + void **p_element = (void **) lttng_dynamic_array_get_element(&array->array, index); void *element = *p_element; *p_element = NULL; @@ -177,4 +181,8 @@ void lttng_dynamic_pointer_array_reset( void lttng_dynamic_pointer_array_clear( struct lttng_dynamic_pointer_array *array); +#if defined(__cplusplus) +} +#endif + #endif /* LTTNG_DYNAMIC_ARRAY_H */ diff --git a/src/common/error.h b/src/common/error.h index f9b2ec2f9..6660cee6a 100644 --- a/src/common/error.h +++ b/src/common/error.h @@ -25,6 +25,10 @@ #include #include +#if defined(__cplusplus) +extern "C" { +#endif + /* Avoid conflict with Solaris */ #if defined(ERR) && defined(__sun__) #undef ERR @@ -264,4 +268,8 @@ const char *log_add_time(void); /* Name must be a statically-allocated string. */ void logger_set_thread_name(const char *name, bool set_pthread_name); +#if defined(__cplusplus) +} +#endif + #endif /* _ERROR_H */ diff --git a/src/common/filter/filter-ast.h b/src/common/filter/filter-ast.h index 93f9b9b25..d7a567445 100644 --- a/src/common/filter/filter-ast.h +++ b/src/common/filter/filter-ast.h @@ -20,6 +20,10 @@ #include #include +#if defined(__cplusplus) +extern "C" { +#endif + #define printf_debug(fmt, args...) \ do { \ if (filter_parser_debug) \ @@ -99,6 +103,8 @@ struct filter_node { enum node_type type; union { struct { + /* Avoid -Wextern-c-compat warning with clang++. */ + char unused; } unknown; struct { struct filter_node *child; @@ -186,4 +192,8 @@ int filter_visitor_ir_validate_string(struct filter_parser_ctx *ctx); int filter_visitor_ir_normalize_glob_patterns(struct filter_parser_ctx *ctx); int filter_visitor_ir_validate_globbing(struct filter_parser_ctx *ctx); +#if defined(__cplusplus) +} +#endif + #endif /* _FILTER_AST_H */ diff --git a/src/common/mi-lttng.c b/src/common/mi-lttng.c index fc3706616..b790f793a 100644 --- a/src/common/mi-lttng.c +++ b/src/common/mi-lttng.c @@ -933,7 +933,7 @@ int mi_lttng_writer_write_element_double(struct mi_writer *writer, writer->writer, element_name, value); } -int mi_lttng_version(struct mi_writer *writer, struct mi_lttng_version *version, +int mi_lttng_version(struct mi_writer *writer, struct mi_lttng_version_data *version, const char *lttng_description, const char *lttng_license) { int ret; diff --git a/src/common/mi-lttng.h b/src/common/mi-lttng.h index 414c731d2..2f27bf6cd 100644 --- a/src/common/mi-lttng.h +++ b/src/common/mi-lttng.h @@ -17,6 +17,10 @@ #include #include +#if defined(__cplusplus) +extern "C" { +#endif + /* Don't want to reference snapshot-internal.h here */ struct lttng_snapshot_output; @@ -29,7 +33,7 @@ struct mi_writer { /* * Version information for the machine interface. */ -struct mi_lttng_version { +struct mi_lttng_version_data { char version[LTTNG_NAME_MAX]; /* Version number of package */ uint32_t version_major; /* LTTng-Tools major version number */ uint32_t version_minor; /* LTTng-Tools minor version number */ @@ -563,7 +567,7 @@ int mi_lttng_writer_write_element_double(struct mi_writer *writer, * Returns zero if the element's value could be written. * Negative values indicate an error. */ -int mi_lttng_version(struct mi_writer *writer, struct mi_lttng_version *version, +int mi_lttng_version(struct mi_writer *writer, struct mi_lttng_version_data *version, const char *lttng_description, const char *lttng_license); /* @@ -1117,4 +1121,8 @@ int mi_lttng_rotate(struct mi_writer *writer, enum lttng_rotation_state rotation_state, const struct lttng_trace_archive_location *location); +#if defined(__cplusplus) +} +#endif + #endif /* _MI_LTTNG_H */ diff --git a/src/common/runas.h b/src/common/runas.h index 58c376fe1..406dfca22 100644 --- a/src/common/runas.h +++ b/src/common/runas.h @@ -46,9 +46,9 @@ int run_as_rmdir(const char *path, uid_t uid, gid_t gid); int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid, int flags); int run_as_rmdirat(int dirfd, const char *path, uid_t uid, gid_t gid); int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid, int flags); -int run_as_rename(const char *old, const char *new, uid_t uid, gid_t gid); -int run_as_renameat(int old_dirfd, const char *old, - int new_dirfd, const char *new, uid_t uid, gid_t gid); +int run_as_rename(const char *old_name, const char *new_name, uid_t uid, gid_t gid); +int run_as_renameat(int old_dirfd, const char *old_name, + int new_dirfd, const char *new_name, uid_t uid, gid_t gid); int run_as_extract_elf_symbol_offset(int fd, const char* function, uid_t uid, gid_t gid, uint64_t *offset); int run_as_extract_sdt_probe_offsets(int fd, const char *provider_name, diff --git a/src/common/spawn-viewer.h b/src/common/spawn-viewer.h index 409c60ec8..316fe1998 100644 --- a/src/common/spawn-viewer.h +++ b/src/common/spawn-viewer.h @@ -10,6 +10,10 @@ #include +#if defined(__cplusplus) +extern "C" { +#endif + /* * Read the trace by `exec()ing` the provided viewer program if any. If * `opt_viewer` is NULL, try to read the trace with the default trace reader. @@ -22,4 +26,8 @@ */ int spawn_viewer(const char *trace_path, char *opt_viewer, bool opt_live_mode); +#if defined(__cplusplus) +} +#endif + #endif /* ifndef LTTNG_SPAWN_VIEWER_H */ diff --git a/src/common/string-utils/string-utils.h b/src/common/string-utils/string-utils.h index 1cf31e9d4..fb795b9dd 100644 --- a/src/common/string-utils/string-utils.h +++ b/src/common/string-utils/string-utils.h @@ -12,6 +12,10 @@ #include #include +#if defined(__cplusplus) +extern "C" { +#endif + void strutils_normalize_star_glob_pattern(char *pattern); bool strutils_is_star_glob_pattern(const char *pattern); @@ -27,4 +31,8 @@ void strutils_free_null_terminated_array_of_strings(char **array); size_t strutils_array_of_strings_len(char * const *array); +#if defined(__cplusplus) +} +#endif + #endif /* _STRING_UTILS_H */ diff --git a/src/common/time.h b/src/common/time.h index 87adf17d7..dc3f76342 100644 --- a/src/common/time.h +++ b/src/common/time.h @@ -13,6 +13,10 @@ #include #include +#if defined(__cplusplus) +extern "C" { +#endif + #define MSEC_PER_SEC 1000ULL #define NSEC_PER_SEC 1000000000ULL #define NSEC_PER_MSEC 1000000ULL @@ -62,4 +66,8 @@ int time_to_iso8601_str(time_t time, char *str, size_t len); int time_to_datetime_str(time_t time, char *str, size_t len); +#if defined(__cplusplus) +} +#endif + #endif /* LTTNG_TIME_H */ diff --git a/src/common/tracker.h b/src/common/tracker.h index 8b3794049..e81ee309e 100644 --- a/src/common/tracker.h +++ b/src/common/tracker.h @@ -17,6 +17,10 @@ #include #include +#if defined(__cplusplus) +extern "C" { +#endif + struct process_attr_value { enum lttng_process_attr_value_type type; union value { @@ -76,4 +80,8 @@ enum lttng_error_code process_attr_value_from_comm( const struct lttng_buffer_view *value_view, struct process_attr_value **value); +#if defined(__cplusplus) +} +#endif + #endif /* LTTNG_COMMON_TRACKER_H */ diff --git a/src/common/uri.h b/src/common/uri.h index ac1de9e0c..a270db9d9 100644 --- a/src/common/uri.h +++ b/src/common/uri.h @@ -12,6 +12,10 @@ #include #include +#if defined(__cplusplus) +extern "C" { +#endif + /* Destination type of lttng URI */ enum lttng_dst_type { LTTNG_DST_IPV4 = 1, @@ -72,4 +76,8 @@ ssize_t uri_parse_str_urls(const char *ctrl_url, const char *data_url, struct lttng_uri **uris); int uri_to_str_url(struct lttng_uri *uri, char *dst, size_t size); +#if defined(__cplusplus) +} +#endif + #endif /* _LTT_URI_H */ diff --git a/src/common/utils.h b/src/common/utils.h index a3250639b..6e478b39e 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -17,6 +17,10 @@ #include +#if defined(__cplusplus) +extern "C" { +#endif + #define KIBI_LOG2 10 #define MEBI_LOG2 20 #define GIBI_LOG2 30 @@ -68,4 +72,8 @@ enum lttng_error_code utils_group_id_from_name( int utils_parse_unsigned_long_long(const char *str, unsigned long long *value); +#if defined(__cplusplus) +} +#endif + #endif /* _COMMON_UTILS_H */