#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 struct lttng_channel chan;
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 <lttng-enable-channel.1.h>
+;
+#endif
+
enum {
OPT_HELP = 1,
OPT_DISCARD,
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;
{"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-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}
};
{
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.attr.overwrite == -1) {
- chan.attr.overwrite = default_attr.overwrite;
+ if (chan_opts.attr.overwrite == -1) {
+ chan_opts.attr.overwrite = default_attr.overwrite;
}
- if (chan.attr.subbuf_size == -1) {
- chan.attr.subbuf_size = default_attr.subbuf_size;
+ if (chan_opts.attr.subbuf_size == -1) {
+ chan_opts.attr.subbuf_size = default_attr.subbuf_size;
}
- if (chan.attr.num_subbuf == -1) {
- chan.attr.num_subbuf = default_attr.num_subbuf;
+ if (chan_opts.attr.num_subbuf == -1) {
+ chan_opts.attr.num_subbuf = default_attr.num_subbuf;
}
- if (chan.attr.switch_timer_interval == -1) {
- chan.attr.switch_timer_interval = default_attr.switch_timer_interval;
+ if (chan_opts.attr.switch_timer_interval == -1) {
+ chan_opts.attr.switch_timer_interval = default_attr.switch_timer_interval;
}
- if (chan.attr.read_timer_interval == -1) {
- chan.attr.read_timer_interval = default_attr.read_timer_interval;
+ if (chan_opts.attr.read_timer_interval == -1) {
+ chan_opts.attr.read_timer_interval = default_attr.read_timer_interval;
}
- if ((int) chan.attr.output == -1) {
- chan.attr.output = default_attr.output;
+ if ((int) chan_opts.attr.output == -1) {
+ chan_opts.attr.output = default_attr.output;
}
- if (chan.attr.tracefile_count == -1) {
- chan.attr.tracefile_count = default_attr.tracefile_count;
+ if (chan_opts.attr.tracefile_count == -1) {
+ chan_opts.attr.tracefile_count = default_attr.tracefile_count;
}
- if (chan.attr.tracefile_size == -1) {
- chan.attr.tracefile_size = default_attr.tracefile_size;
+ if (chan_opts.attr.tracefile_size == -1) {
+ chan_opts.attr.tracefile_size = default_attr.tracefile_size;
}
}
*/
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;
set_default_attr(&dom);
- if (chan.attr.tracefile_size == 0 && chan.attr.tracefile_count) {
+ 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.attr.tracefile_size > 0) &&
- (chan.attr.tracefile_size < chan.attr.subbuf_size)) {
+ 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.attr.tracefile_size, chan.attr.subbuf_size);
- chan.attr.tracefile_size = chan.attr.subbuf_size;
+ 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.attr.output = LTTNG_EVENT_MMAP;
+ chan_opts.attr.output = LTTNG_EVENT_MMAP;
} else if (!strncmp(output_splice, opt_output, strlen(output_splice))) {
- chan.attr.output = LTTNG_EVENT_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);
/* 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) >= NAME_MAX) {
ERR("Channel name is too long (max. %zu characters)",
- sizeof(chan.name) - 1);
+ 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(chan.name, 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, &chan);
+ ret = lttng_enable_channel(handle, channel);
if (ret < 0) {
success = 0;
switch (-ret) {
skip_enable:
if (lttng_opt_mi) {
/* Mi print the channel element and leave it open */
- ret = mi_lttng_channel(writer, &chan, 1);
+ ret = mi_lttng_channel(writer, channel, 1);
if (ret) {
ret = CMD_ERROR;
goto error;
/* Next channel */
channel_name = strtok(NULL, ",");
+ lttng_channel_destroy(channel);
+ channel = NULL;
}
if (lttng_opt_mi) {
ret = CMD_SUCCESS;
error:
+ if (channel) {
+ lttng_channel_destroy(channel);
+ }
/* If more important error happen bypass the warning */
if (!ret && warn) {
ret = CMD_WARNING;
* 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.attr, -1, sizeof(chan.attr));
- chan.attr.extended.ptr = NULL;
+ memset(&chan_opts.attr, -1, sizeof(chan_opts.attr));
+ chan_opts.attr.extended.ptr = NULL;
}
/*
static poptContext pc;
char *session_name = NULL;
char *opt_arg = NULL;
+ const char *leftover = NULL;
init_channel_config();
SHOW_HELP();
goto end;
case OPT_DISCARD:
- chan.attr.overwrite = 0;
+ chan_opts.attr.overwrite = 0;
DBG("Channel set to discard");
break;
case OPT_OVERWRITE:
- chan.attr.overwrite = 1;
+ chan_opts.attr.overwrite = 1;
DBG("Channel set to overwrite");
break;
case OPT_SUBBUF_SIZE:
/* Parse the size */
opt_arg = poptGetOptArg(pc);
- if (utils_parse_size_suffix(opt_arg, &chan.attr.subbuf_size) < 0 || !chan.attr.subbuf_size) {
+ 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.attr.subbuf_size);
+ order = get_count_order_u64(chan_opts.attr.subbuf_size);
assert(order >= 0);
rounded_size = 1ULL << order;
- if (rounded_size < chan.attr.subbuf_size) {
+ if (rounded_size < chan_opts.attr.subbuf_size) {
ERR("The subbuf size (%" PRIu64 ") is rounded and overflows!",
- chan.attr.subbuf_size);
+ chan_opts.attr.subbuf_size);
ret = CMD_ERROR;
goto end;
}
- if (rounded_size != chan.attr.subbuf_size) {
+ if (rounded_size != chan_opts.attr.subbuf_size) {
WARN("The subbuf size (%" PRIu64 ") is rounded to the next power of 2 (%" PRIu64 ")",
- chan.attr.subbuf_size, rounded_size);
- chan.attr.subbuf_size = rounded_size;
+ chan_opts.attr.subbuf_size, rounded_size);
+ chan_opts.attr.subbuf_size = rounded_size;
}
/* Should now be power of 2 */
- assert(!((chan.attr.subbuf_size - 1) & chan.attr.subbuf_size));
+ assert(!((chan_opts.attr.subbuf_size - 1) & chan_opts.attr.subbuf_size));
- DBG("Channel subbuf size set to %" PRIu64, chan.attr.subbuf_size);
+ DBG("Channel subbuf size set to %" PRIu64, chan_opts.attr.subbuf_size);
break;
}
case OPT_NUM_SUBBUF:
errno = 0;
opt_arg = poptGetOptArg(pc);
- chan.attr.num_subbuf = strtoull(opt_arg, NULL, 0);
- if (errno != 0 || !chan.attr.num_subbuf || !isdigit(opt_arg[0])) {
+ 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.attr.num_subbuf);
+ order = get_count_order_u64(chan_opts.attr.num_subbuf);
assert(order >= 0);
rounded_size = 1ULL << order;
- if (rounded_size < chan.attr.num_subbuf) {
+ if (rounded_size < chan_opts.attr.num_subbuf) {
ERR("The number of subbuffers (%" PRIu64 ") is rounded and overflows!",
- chan.attr.num_subbuf);
+ chan_opts.attr.num_subbuf);
ret = CMD_ERROR;
goto end;
}
- if (rounded_size != chan.attr.num_subbuf) {
+ if (rounded_size != chan_opts.attr.num_subbuf) {
WARN("The number of subbuffers (%" PRIu64 ") is rounded to the next power of 2 (%" PRIu64 ")",
- chan.attr.num_subbuf, rounded_size);
- chan.attr.num_subbuf = rounded_size;
+ chan_opts.attr.num_subbuf, rounded_size);
+ chan_opts.attr.num_subbuf = rounded_size;
}
/* Should now be power of 2 */
- assert(!((chan.attr.num_subbuf - 1) & chan.attr.num_subbuf));
+ assert(!((chan_opts.attr.num_subbuf - 1) & chan_opts.attr.num_subbuf));
- DBG("Channel subbuf num set to %" PRIu64, chan.attr.num_subbuf);
+ DBG("Channel subbuf num set to %" PRIu64, chan_opts.attr.num_subbuf);
break;
}
case OPT_SWITCH_TIMER:
{
- unsigned long v;
+ uint64_t v;
errno = 0;
opt_arg = poptGetOptArg(pc);
- v = strtoul(opt_arg, NULL, 0);
- if (errno != 0 || !isdigit(opt_arg[0])) {
- ERR("Wrong value in --switch-timer parameter: %s", opt_arg);
+
+ 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.attr.switch_timer_interval = (uint32_t) v;
- DBG("Channel switch timer interval set to %d", chan.attr.switch_timer_interval);
+ 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:
{
- unsigned long v;
+ uint64_t v;
errno = 0;
opt_arg = poptGetOptArg(pc);
- v = strtoul(opt_arg, NULL, 0);
- if (errno != 0 || !isdigit(opt_arg[0])) {
- ERR("Wrong value in --read-timer parameter: %s", opt_arg);
+
+ 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.attr.read_timer_interval = (uint32_t) v;
- DBG("Channel read timer interval set to %d", chan.attr.read_timer_interval);
+ 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:
break;
case OPT_TRACEFILE_SIZE:
opt_arg = poptGetOptArg(pc);
- if (utils_parse_size_suffix(opt_arg, &chan.attr.tracefile_size) < 0) {
+ 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.attr.tracefile_size);
+ chan_opts.attr.tracefile_size);
break;
case OPT_TRACEFILE_COUNT:
{
ret = CMD_ERROR;
goto end;
}
- chan.attr.tracefile_count = (uint32_t) v;
+ chan_opts.attr.tracefile_count = (uint32_t) v;
DBG("Maximum tracefile count set to %" PRIu64,
- chan.attr.tracefile_count);
+ chan_opts.attr.tracefile_count);
break;
}
case OPT_LIST_OPTIONS:
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);
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) {