AM_CONDITIONAL([LTTNG_TOOLS_BUILD_WITH_LIBDL], [test "x$have_libdl" = "xyes"])
AM_CONDITIONAL([LTTNG_TOOLS_BUILD_WITH_LIBC_DL], [test "x$have_libc_dl" = "xyes"])
+# Check for fmemopen
+AC_CHECK_LIB([c], [fmemopen],
+[
+ AC_DEFINE_UNQUOTED([LTTNG_HAVE_FMEMOPEN], 1, [Has fmemopen support.])
+]
+)
+
+# Check for open_memstream
+AC_CHECK_LIB([c], [open_memstream],
+[
+ AC_DEFINE_UNQUOTED([LTTNG_HAVE_OPEN_MEMSTREAM], 1, [Has open_memstream support.])
+]
+)
+
# Option to only build the consumer daemon and its libraries
AC_ARG_WITH([consumerd-only],
AS_HELP_STRING([--with-consumerd-only],[Only build the consumer daemon [default=no]]),
extern int lttng_enable_event(struct lttng_handle *handle,
struct lttng_event *ev, const char *channel_name);
+/*
+ * Apply a filter expression to an event.
+ *
+ * If event_name is NULL, the filter is applied to all events of the channel.
+ * If channel_name is NULL, a lookup of the event's channel is done.
+ * If both are NULL, the filter is applied to all events of all channels.
+ */
+extern int lttng_set_event_filter(struct lttng_handle *handle,
+ const char *event_name,
+ const char *channel_name,
+ const char *filter_expression);
/*
* Create or enable a channel.
* The channel name cannot be NULL.
fd-limit.c fd-limit.h \
consumer.c consumer.h \
kernel-consumer.c kernel-consumer.h \
- consumer.h
+ consumer.h filter.c filter.h
if HAVE_LIBLTTNG_UST_CTL
lttng_sessiond_SOURCES += trace-ust.c ust-app.c ust-consumer.c ust-consumer.h
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C) 2012 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <urcu/list.h>
+
+#include <common/error.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+
+#include "filter.h"
+#include "kernel.h"
+#include "ust-app.h"
+#include "trace-ust.h"
+
+/*
+ * Add UST context to event.
+ */
+static int add_ufilter_to_event(struct ltt_ust_session *usess, int domain,
+ struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent,
+ struct lttng_filter_bytecode *bytecode)
+{
+ int ret;
+
+ if (uevent->filter) {
+ ret = -EEXIST;
+ goto error;
+ }
+ /* Same layout. */
+ uevent->filter = (struct lttng_ust_filter_bytecode *) bytecode;
+
+ switch (domain) {
+ case LTTNG_DOMAIN_UST:
+ ret = ust_app_set_filter_event_glb(usess, uchan, uevent,
+ bytecode);
+ if (ret < 0) {
+ goto error;
+ }
+ break;
+ default:
+ ret = -ENOSYS;
+ goto error;
+ }
+
+ DBG("Filter UST added to event %s",uevent->attr.name);
+
+ return 0;
+
+error:
+ free(bytecode);
+ return ret;
+}
+
+/*
+ * Add UST context to tracer.
+ */
+int filter_ust_set(struct ltt_ust_session *usess, int domain,
+ struct lttng_filter_bytecode *bytecode, char *event_name,
+ char *channel_name)
+{
+ int ret = LTTCOMM_OK, have_event = 0;
+ struct lttng_ht_iter iter;
+ struct lttng_ht *chan_ht;
+ struct ltt_ust_channel *uchan = NULL;
+ struct ltt_ust_event *uevent = NULL;
+
+ /*
+ * Define which channel's hashtable to use from the domain or quit if
+ * unknown domain.
+ */
+ switch (domain) {
+ case LTTNG_DOMAIN_UST:
+ chan_ht = usess->domain_global.channels;
+ break;
+#if 0
+ case LTTNG_DOMAIN_UST_EXEC_NAME:
+ case LTTNG_DOMAIN_UST_PID:
+ case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN:
+#endif
+ default:
+ ret = LTTCOMM_UND;
+ goto error;
+ }
+
+ /* Do we have an event name */
+ if (strlen(event_name) != 0) {
+ have_event = 1;
+ }
+
+ /* Get UST channel if defined */
+ if (strlen(channel_name) != 0) {
+ uchan = trace_ust_find_channel_by_name(chan_ht, channel_name);
+ if (uchan == NULL) {
+ ret = LTTCOMM_UST_CHAN_NOT_FOUND;
+ goto error;
+ }
+ }
+
+ /* If UST channel specified and event name, get UST event ref */
+ if (uchan && have_event) {
+ uevent = trace_ust_find_event_by_name(uchan->events, event_name);
+ if (uevent == NULL) {
+ ret = LTTCOMM_UST_EVENT_NOT_FOUND;
+ goto error;
+ }
+ }
+
+ /* At this point, we have 4 possibilities */
+
+ if (uchan && uevent) { /* Add filter to event in channel */
+ ret = add_ufilter_to_event(usess, domain, uchan, uevent,
+ bytecode);
+ } else if (uchan && !have_event) { /* Add filter to channel */
+ ERR("Cannot add filter to channel");
+ ret = LTTCOMM_FATAL; /* not supported. */
+ goto error;
+ } else if (!uchan && have_event) { /* Add filter to event */
+ /* Add context to event without having the channel name */
+ cds_lfht_for_each_entry(chan_ht->ht, &iter.iter, uchan, node.node) {
+ uevent = trace_ust_find_event_by_name(uchan->events, event_name);
+ if (uevent != NULL) {
+ ret = add_ufilter_to_event(usess, domain, uchan, uevent, bytecode);
+ /*
+ * LTTng UST does not allowed the same event to be registered
+ * multiple time in different or the same channel. So, if we
+ * found our event, we stop.
+ */
+ goto end;
+ }
+ }
+ ret = LTTCOMM_UST_EVENT_NOT_FOUND;
+ goto error;
+ } else if (!uchan && !have_event) { /* Add filter all events, all channels */
+ ERR("Cannot add filter to channel");
+ ret = LTTCOMM_FATAL; /* not supported. */
+ goto error;
+ }
+
+end:
+ switch (ret) {
+ case -EEXIST:
+ ret = LTTCOMM_FILTER_EXIST;
+ break;
+ case -ENOMEM:
+ ret = LTTCOMM_FATAL;
+ break;
+ case -EINVAL:
+ ret = LTTCOMM_FILTER_INVAL;
+ break;
+ case -ENOSYS:
+ ret = LTTCOMM_UNKNOWN_DOMAIN;
+ break;
+ default:
+ ret = LTTCOMM_OK;
+ break;
+ }
+
+error:
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C) 2012 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _LTT_FILTER_H
+#define _LTT_FILTER_H
+
+#include <lttng/lttng.h>
+
+#include "trace-kernel.h"
+#include "trace-ust.h"
+#include "ust-ctl.h"
+
+struct lttng_filter_bytecode;
+
+int filter_ust_set(struct ltt_ust_session *usess, int domain,
+ struct lttng_filter_bytecode *bytecode, char *event_name,
+ char *channel_name);
+
+#endif /* _LTT_FILTER_H */
} u;
};
+#define FILTER_BYTECODE_MAX_LEN 65535
+struct lttng_ust_filter_bytecode {
+ uint16_t len;
+ uint16_t reloc_offset;
+ char data[0];
+};
+
#define _UST_CMD(minor) (minor)
#define _UST_CMDR(minor, type) (minor)
#define _UST_CMDW(minor, type) (minor)
int ustctl_add_context(int sock, struct lttng_ust_context *ctx,
struct lttng_ust_object_data *obj_data,
struct lttng_ust_object_data **context_data);
+int ustctl_set_filter(int sock, struct lttng_ust_filter_bytecode *bytecode,
+ struct lttng_ust_object_data *obj_data);
int ustctl_enable(int sock, struct lttng_ust_object_data *object);
int ustctl_disable(int sock, struct lttng_ust_object_data *object);
#include "ust-consumer.h"
#include "utils.h"
#include "fd-limit.h"
+#include "filter.h"
#define CONSUMERD_FILE "lttng-consumerd"
return ret;
}
+/*
+ * Command LTTNG_SET_FILTER processed by the client thread.
+ */
+static int cmd_set_filter(struct ltt_session *session, int domain,
+ char *channel_name, char *event_name,
+ struct lttng_filter_bytecode *bytecode)
+{
+ int ret;
+
+ switch (domain) {
+ case LTTNG_DOMAIN_KERNEL:
+ ret = LTTCOMM_FATAL;
+ break;
+ case LTTNG_DOMAIN_UST:
+ {
+ struct ltt_ust_session *usess = session->ust_session;
+
+ ret = filter_ust_set(usess, domain, bytecode, event_name, channel_name);
+ if (ret != LTTCOMM_OK) {
+ goto error;
+ }
+ break;
+ }
+#if 0
+ case LTTNG_DOMAIN_UST_EXEC_NAME:
+ case LTTNG_DOMAIN_UST_PID:
+ case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN:
+#endif
+ default:
+ ret = LTTCOMM_UND;
+ goto error;
+ }
+
+ ret = LTTCOMM_OK;
+
+error:
+ return ret;
+
+}
+
/*
* Command LTTNG_ENABLE_EVENT processed by the client thread.
*/
* is set and ready for transmission before returning.
*
* Return any error encountered or 0 for success.
+ *
+ * "sock" is only used for special-case var. len data.
*/
-static int process_client_msg(struct command_ctx *cmd_ctx)
+static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
+ int *sock_error)
{
int ret = LTTCOMM_OK;
int need_tracing_session = 1;
DBG("Processing client command %d", cmd_ctx->lsm->cmd_type);
+ *sock_error = 0;
+
switch (cmd_ctx->lsm->cmd_type) {
case LTTNG_CREATE_SESSION:
case LTTNG_CREATE_SESSION_URI:
cmd_ctx->lsm->u.reg.path);
break;
}
+ case LTTNG_SET_FILTER:
+ {
+ struct lttng_filter_bytecode *bytecode;
+
+ if (cmd_ctx->lsm->u.filter.bytecode_len > 65336) {
+ ret = LTTCOMM_FILTER_INVAL;
+ goto error;
+ }
+ bytecode = zmalloc(cmd_ctx->lsm->u.filter.bytecode_len);
+ if (!bytecode) {
+ ret = LTTCOMM_FILTER_NOMEM;
+ goto error;
+ }
+ /* Receive var. len. data */
+ DBG("Receiving var len data from client ...");
+ ret = lttcomm_recv_unix_sock(sock, bytecode,
+ cmd_ctx->lsm->u.filter.bytecode_len);
+ if (ret <= 0) {
+ DBG("Nothing recv() from client var len data... continuing");
+ *sock_error = 1;
+ ret = LTTCOMM_FILTER_INVAL;
+ goto error;
+ }
+
+ if (bytecode->len + sizeof(*bytecode)
+ != cmd_ctx->lsm->u.filter.bytecode_len) {
+ free(bytecode);
+ ret = LTTCOMM_FILTER_INVAL;
+ goto error;
+ }
+
+ ret = cmd_set_filter(cmd_ctx->session, cmd_ctx->lsm->domain.type,
+ cmd_ctx->lsm->u.filter.channel_name,
+ cmd_ctx->lsm->u.filter.event_name,
+ bytecode);
+ break;
+ }
default:
ret = LTTCOMM_UND;
break;
static void *thread_manage_clients(void *data)
{
int sock = -1, ret, i, pollfd;
+ int sock_error;
uint32_t revents, nb_fd;
struct command_ctx *cmd_ctx = NULL;
struct lttng_poll_event events;
* informations for the client. The command context struct contains
* everything this function may needs.
*/
- ret = process_client_msg(cmd_ctx);
+ ret = process_client_msg(cmd_ctx, sock, &sock_error);
rcu_thread_offline();
if (ret < 0) {
+ if (sock_error) {
+ ret = close(sock);
+ if (ret) {
+ PERROR("close");
+ }
+ sock = -1;
+ }
/*
* TODO: Inform client somehow of the fatal error. At
* this point, ret < 0 means that a zmalloc failed
- * (ENOMEM). Error detected but still accept command.
+ * (ENOMEM). Error detected but still accept
+ * command, unless a socket error has been
+ * detected.
*/
clean_command_ctx(&cmd_ctx);
continue;
{
DBG2("Trace destroy UST event %s", event->attr.name);
destroy_contexts(event->ctx);
-
+ free(event->filter);
free(event);
}
struct lttng_ust_event attr;
struct lttng_ht *ctx;
struct lttng_ht_node_str node;
+ struct lttng_ust_filter_bytecode *filter;
};
/* UST stream */
#include "ust-consumer.h"
#include "ust-ctl.h"
#include "fd-limit.h"
+#include "../../common/sessiond-comm/sessiond-comm.h"
/*
* Delete ust context safely. RCU read lock must be held before calling
assert(!ret);
delete_ust_app_ctx(sock, ua_ctx);
}
+ free(ua_event->filter);
lttng_ht_destroy(ua_event->ctx);
if (ua_event->obj != NULL) {
return ret;
}
+/*
+ * Set the filter on the tracer.
+ */
+static
+int set_ust_event_filter(struct ust_app_event *ua_event,
+ struct ust_app *app)
+{
+ int ret;
+
+ if (!ua_event->filter) {
+ return 0;
+ }
+
+ ret = ustctl_set_filter(app->sock, ua_event->filter,
+ ua_event->obj);
+ if (ret < 0) {
+ goto error;
+ }
+
+ DBG2("UST filter set successfully for event %s", ua_event->name);
+
+error:
+ return ret;
+}
+
/*
* Disable the specified event on to UST tracer for the UST session.
*/
/* Copy event attributes */
memcpy(&ua_event->attr, &uevent->attr, sizeof(ua_event->attr));
+ /* Copy filter bytecode */
+ if (uevent->filter) {
+ ua_event->filter = zmalloc(sizeof(*ua_event->filter) +
+ uevent->filter->len);
+ if (!ua_event->filter) {
+ return;
+ }
+ memcpy(ua_event->filter, uevent->filter,
+ sizeof(*ua_event->filter) + uevent->filter->len);
+ }
cds_lfht_for_each_entry(uevent->ctx->ht, &iter.iter, uctx, node.node) {
ua_ctx = alloc_ust_app_ctx(&uctx->ctx);
if (ua_ctx == NULL) {
return ret;
}
+/*
+ * Set UST filter for the event on the tracer.
+ */
+static
+int set_ust_app_event_filter(struct ust_app_session *ua_sess,
+ struct ust_app_event *ua_event,
+ struct lttng_filter_bytecode *bytecode,
+ struct ust_app *app)
+{
+ int ret = 0;
+
+ DBG2("UST app adding context to event %s", ua_event->name);
+
+ /* Copy filter bytecode */
+ ua_event->filter = zmalloc(sizeof(*ua_event->filter) + bytecode->len);
+ if (!ua_event->filter) {
+ return -ENOMEM;
+ }
+ memcpy(ua_event->filter, bytecode,
+ sizeof(*ua_event->filter) + bytecode->len);
+ ret = set_ust_event_filter(ua_event, app);
+ if (ret < 0) {
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
/*
* Enable on the tracer side a ust app event for the session and channel.
*/
continue;
}
}
+ ret = set_ust_event_filter(ua_event, app);
+ if (ret < 0) {
+ /* FIXME: Should we quit here or continue... */
+ continue;
+ }
}
}
return ret;
}
+/*
+ * Add context to a specific event in a channel for global UST domain.
+ */
+int ust_app_set_filter_event_glb(struct ltt_ust_session *usess,
+ struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent,
+ struct lttng_filter_bytecode *bytecode)
+{
+ int ret = 0;
+ struct lttng_ht_node_str *ua_chan_node, *ua_event_node;
+ struct lttng_ht_iter iter, uiter;
+ struct ust_app_session *ua_sess;
+ struct ust_app_event *ua_event;
+ struct ust_app_channel *ua_chan = NULL;
+ struct ust_app *app;
+
+ rcu_read_lock();
+
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ if (!app->compatible) {
+ /*
+ * TODO: In time, we should notice the caller of this error by
+ * telling him that this is a version error.
+ */
+ continue;
+ }
+ ua_sess = lookup_session_by_app(usess, app);
+ if (ua_sess == NULL) {
+ continue;
+ }
+
+ /* Lookup channel in the ust app session */
+ lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter);
+ ua_chan_node = lttng_ht_iter_get_node_str(&uiter);
+ if (ua_chan_node == NULL) {
+ continue;
+ }
+ ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel,
+ node);
+
+ lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &uiter);
+ ua_event_node = lttng_ht_iter_get_node_str(&uiter);
+ if (ua_event_node == NULL) {
+ continue;
+ }
+ ua_event = caa_container_of(ua_event_node, struct ust_app_event,
+ node);
+
+ ret = set_ust_app_event_filter(ua_sess, ua_event, bytecode, app);
+ if (ret < 0) {
+ continue;
+ }
+ }
+
+ rcu_read_unlock();
+ return ret;
+}
+
/*
* Enable event for a channel from a UST session for a specific PID.
*/
#define UST_APP_EVENT_LIST_SIZE 32
+struct lttng_filter_bytecode;
+struct lttng_ust_filter_bytecode;
+
extern int ust_consumerd64_fd, ust_consumerd32_fd;
/*
char name[LTTNG_UST_SYM_NAME_LEN];
struct lttng_ht *ctx;
struct lttng_ht_node_str node;
+ struct lttng_ust_filter_bytecode *filter;
};
struct ust_app_channel {
struct ltt_ust_context *uctx);
int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess,
struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx);
+int ust_app_set_filter_event_glb(struct ltt_ust_session *usess,
+ struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent,
+ struct lttng_filter_bytecode *bytecode);
void ust_app_global_update(struct ltt_ust_session *usess, int sock);
void ust_app_clean_list(void);
static char *opt_function;
static char *opt_function_entry_symbol;
static char *opt_channel_name;
+static char *opt_filter;
#if 0
/* Not implemented yet */
static char *opt_cmd_name;
OPT_LOGLEVEL,
OPT_LOGLEVEL_ONLY,
OPT_LIST_OPTIONS,
+ OPT_FILTER,
};
static struct lttng_handle *handle;
{"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},
{0, 0, 0, 0, 0, 0, 0}
};
fprintf(ofp, " TRACE_DEBUG_LINE = 13\n");
fprintf(ofp, " TRACE_DEBUG = 14\n");
fprintf(ofp, " (shortcuts such as \"system\" are allowed)\n");
+ fprintf(ofp, " --filter \'expression\'\n");
+ fprintf(ofp, " Filter expression on event fields,\n");
+ fprintf(ofp, " event recording depends on evaluation.\n");
fprintf(ofp, "\n");
}
memset(&ev, 0, sizeof(ev));
memset(&dom, 0, sizeof(dom));
+ if (opt_kernel) {
+ if (opt_filter) {
+ ERR("Filter not implement for kernel tracing yet");
+ ret = CMD_ERROR;
+ goto error;
+ }
+ }
+
/* Create lttng domain */
if (opt_kernel) {
dom.type = LTTNG_DOMAIN_KERNEL;
}
goto end;
}
+ if (opt_filter) {
+ ret = lttng_set_event_filter(handle, ev.name, channel_name,
+ opt_filter);
+ if (ret < 0) {
+ ERR("Error setting filter");
+ ret = -1;
+ goto error;
+ }
+ }
switch (opt_event_type) {
case LTTNG_EVENT_TRACEPOINT:
MSG("%s event %s created in channel %s",
opt_kernel ? "kernel": "UST", event_name, channel_name);
}
+ if (opt_filter) {
+ ret = lttng_set_event_filter(handle, ev.name,
+ channel_name, opt_filter);
+ if (ret < 0) {
+ ERR("Error setting filter");
+ ret = -1;
+ goto error;
+ }
+ }
/* Next event */
event_name = strtok(NULL, ",");
case OPT_LIST_OPTIONS:
list_cmd_options(stdout, long_options);
goto end;
+ case OPT_FILTER:
+ break;
default:
usage(stderr);
ret = CMD_UNDEFINED;
[ LTTCOMM_ERR_INDEX(LTTCOMM_ENABLE_CONSUMER_FAIL) ] = "Enabling consumer failed",
[ LTTCOMM_ERR_INDEX(LTTCOMM_RELAYD_SESSION_FAIL) ] = "Unable to create session on lttng-relayd",
[ LTTCOMM_ERR_INDEX(LTTCOMM_RELAYD_VERSION_FAIL) ] = "Relay daemon not compatible",
+ [ LTTCOMM_ERR_INDEX(LTTCOMM_FILTER_INVAL) ] = "Invalid filter bytecode",
+ [ LTTCOMM_ERR_INDEX(LTTCOMM_FILTER_NOMEM) ] = "Not enough memory for filter bytecode",
+ [ LTTCOMM_ERR_INDEX(LTTCOMM_FILTER_EXIST) ] = "Filter already exist",
};
/*
LTTNG_START_TRACE,
LTTNG_STOP_TRACE,
LTTNG_LIST_TRACEPOINT_FIELDS,
+
/* Consumer */
LTTNG_DISABLE_CONSUMER,
LTTNG_ENABLE_CONSUMER,
RELAYD_UPDATE_SYNC_INFO,
RELAYD_VERSION,
RELAYD_SEND_METADATA,
+
+ /* Other tracer commands */
+ LTTNG_SET_FILTER,
};
/*
LTTCOMM_ENABLE_CONSUMER_FAIL, /* Enabling consumer failed */
LTTCOMM_RELAYD_SESSION_FAIL, /* lttng-relayd create session failed */
LTTCOMM_RELAYD_VERSION_FAIL, /* lttng-relayd not compatible */
+ LTTCOMM_FILTER_INVAL, /* Invalid filter bytecode */
+ LTTCOMM_FILTER_NOMEM, /* Lack of memory for filter bytecode */
+ LTTCOMM_FILTER_EXIST, /* Filter already exist */
/* MUST be last element */
LTTCOMM_NR, /* Last element */
struct lttng_uri ctrl_uri;
struct lttng_uri data_uri;
} create_uri;
+ struct {
+ char channel_name[NAME_MAX];
+ char event_name[NAME_MAX];
+ /* Length of following bytecode */
+ uint32_t bytecode_len;
+ } filter;
} u;
};
+#define LTTNG_FILTER_MAX_LEN 65336
+
+/*
+ * Filter bytecode data. The reloc table is located at the end of the
+ * bytecode. It is made of tuples: (uint16_t, var. len. string). It
+ * starts at reloc_table_offset.
+ */
+struct lttng_filter_bytecode {
+ uint16_t len; /* len of data */
+ uint16_t reloc_table_offset;
+ char data[0];
+};
+
/*
* Data structure for the response from sessiond to the lttng client.
*/
align.h \
filter-ast.h \
filter-bytecode.h \
- filter-ir.h
+ filter-ir.h \
+ memstream.h
filter_grammar_test_SOURCES = filter-grammar-test.c
filter_grammar_test_LDADD = liblttng-ctl.la
const char *node_type(struct filter_node *node);
struct ir_op;
-struct filter_bytecode;
struct filter_parser_ctx {
yyscan_t scanner;
struct filter_ast *ast;
struct cds_list_head allocated_strings;
struct ir_op *ir_root;
- struct filter_bytecode_alloc *bytecode;
- struct filter_bytecode_alloc *bytecode_reloc;
+ struct lttng_filter_bytecode_alloc *bytecode;
+ struct lttng_filter_bytecode_alloc *bytecode_reloc;
};
struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input);
*/
#include "filter-ast.h"
+#include "../../common/sessiond-comm/sessiond-comm.h"
/*
* offsets are absolute from start of bytecode.
filter_opcode_t op;
} __attribute__((packed));
-/*
- * The reloc table is located at the end of the bytecode. It is made of
- * tuples: (uint16_t, var. len. string). It starts at
- * reloc_table_offset.
- */
-struct filter_bytecode {
- uint16_t len;
- uint16_t reloc_table_offset;
- char data[0];
-};
-
-struct filter_bytecode_alloc {
+struct lttng_filter_bytecode_alloc {
uint16_t alloc_len;
- struct filter_bytecode b;
+ struct lttng_filter_bytecode b;
};
static inline
-unsigned int bytecode_get_len(struct filter_bytecode *bytecode)
+unsigned int bytecode_get_len(struct lttng_filter_bytecode *bytecode)
{
return bytecode->len;
}
struct ir_op *node);
static
-int bytecode_init(struct filter_bytecode_alloc **fb)
+int bytecode_init(struct lttng_filter_bytecode_alloc **fb)
{
- *fb = calloc(sizeof(struct filter_bytecode_alloc) + INIT_ALLOC_SIZE, 1);
+ *fb = calloc(sizeof(struct lttng_filter_bytecode_alloc) + INIT_ALLOC_SIZE, 1);
if (!*fb) {
return -ENOMEM;
} else {
}
static
-int32_t bytecode_reserve(struct filter_bytecode_alloc **fb, uint32_t align, uint32_t len)
+int32_t bytecode_reserve(struct lttng_filter_bytecode_alloc **fb, uint32_t align, uint32_t len)
{
int32_t ret;
uint32_t padding = offset_align((*fb)->b.len, align);
if (new_len > 0xFFFF)
return -EINVAL;
- *fb = realloc(*fb, sizeof(struct filter_bytecode_alloc) + new_len);
+ *fb = realloc(*fb, sizeof(struct lttng_filter_bytecode_alloc) + new_len);
if (!*fb)
return -ENOMEM;
memset(&(*fb)->b.data[old_len], 0, new_len - old_len);
}
static
-int bytecode_push(struct filter_bytecode_alloc **fb, const void *data,
+int bytecode_push(struct lttng_filter_bytecode_alloc **fb, const void *data,
uint32_t align, uint32_t len)
{
int32_t offset;
}
static
-int bytecode_push_logical(struct filter_bytecode_alloc **fb,
+int bytecode_push_logical(struct lttng_filter_bytecode_alloc **fb,
struct logical_op *data,
uint32_t align, uint32_t len,
uint16_t *skip_offset)
}
static
-int bytecode_patch(struct filter_bytecode_alloc **fb,
+int bytecode_patch(struct lttng_filter_bytecode_alloc **fb,
const void *data,
uint16_t offset,
uint32_t len)
#include <common/sessiond-comm/sessiond-comm.h>
#include <lttng/lttng.h>
+#include "filter-parser.h"
+#include "filter-ast.h"
+#include "filter-bytecode.h"
+#include "memstream.h"
+
+#ifdef DEBUG
+const int print_xml = 1;
+#define dbg_printf(fmt, args...) \
+ printf("[debug liblttng-ctl] " fmt, ## args)
+#else
+const int print_xml = 0;
+#define dbg_printf(fmt, args...) \
+do { \
+ /* do nothing but check printf format */ \
+ if (0) \
+ printf("[debug liblttnctl] " fmt, ## args); \
+} while (0)
+#endif
+
+
/* Socket to session daemon for communication */
static int sessiond_socket;
static char sessiond_sock_path[PATH_MAX];
return ret;
}
+/*
+ * Send var len data to the session daemon.
+ *
+ * On success, returns the number of bytes sent (>=0)
+ * On error, returns -1
+ */
+static int send_session_varlen(void *data, size_t len)
+{
+ int ret;
+
+ if (!connected) {
+ ret = -ENOTCONN;
+ goto end;
+ }
+ if (!data || !len) {
+ ret = 0;
+ goto end;
+ }
+
+ ret = lttcomm_send_unix_sock(sessiond_socket, data, len);
+
+end:
+ return ret;
+}
+
/*
* Receive data from the sessiond socket.
*
/*
* Ask the session daemon a specific command and put the data into buf.
+ * Takes extra var. len. data as input to send to the session daemon.
*
* Return size of data (only payload, not header) or a negative error code.
*/
-static int ask_sessiond(struct lttcomm_session_msg *lsm, void **buf)
+static int ask_sessiond_varlen(struct lttcomm_session_msg *lsm,
+ void *vardata,
+ size_t varlen,
+ void **buf)
{
int ret;
size_t size;
if (ret < 0) {
goto end;
}
+ /* Send var len data */
+ ret = send_session_varlen(vardata, varlen);
+ if (ret < 0) {
+ goto end;
+ }
/* Get header from data transmission */
ret = recv_data_sessiond(&llm, sizeof(llm));
return ret;
}
+/*
+ * Ask the session daemon a specific command and put the data into buf.
+ *
+ * Return size of data (only payload, not header) or a negative error code.
+ */
+static int ask_sessiond(struct lttcomm_session_msg *lsm, void **buf)
+{
+ return ask_sessiond_varlen(lsm, NULL, 0, buf);
+}
+
/*
* Create lttng handle and return pointer.
* The returned pointer will be NULL in case of malloc() error.
return ask_sessiond(&lsm, NULL);
}
+/*
+ * set filter for an event
+ * Return negative error value on error.
+ * Return size of returned session payload data if OK.
+ */
+
+int lttng_set_event_filter(struct lttng_handle *handle,
+ const char *event_name, const char *channel_name,
+ const char *filter_expression)
+{
+ struct lttcomm_session_msg lsm;
+ struct filter_parser_ctx *ctx;
+ FILE *fmem;
+ int ret = 0;
+
+ /* Safety check. */
+ if (handle == NULL) {
+ return -1;
+ }
+
+ if (!filter_expression) {
+ return 0;
+ }
+
+ /*
+ * casting const to non-const, as the underlying function will
+ * use it in read-only mode.
+ */
+ fmem = lttng_fmemopen((void *) filter_expression,
+ strlen(filter_expression), "r");
+ if (!fmem) {
+ fprintf(stderr, "Error opening memory as stream\n");
+ return -ENOMEM;
+ }
+ ctx = filter_parser_ctx_alloc(fmem);
+ if (!ctx) {
+ fprintf(stderr, "Error allocating parser\n");
+ ret = -ENOMEM;
+ goto alloc_error;
+ }
+ ret = filter_parser_ctx_append_ast(ctx);
+ if (ret) {
+ fprintf(stderr, "Parse error\n");
+ ret = -EINVAL;
+ goto parse_error;
+ }
+ ret = filter_visitor_set_parent(ctx);
+ if (ret) {
+ fprintf(stderr, "Set parent error\n");
+ ret = -EINVAL;
+ goto parse_error;
+ }
+ if (print_xml) {
+ ret = filter_visitor_print_xml(ctx, stdout, 0);
+ if (ret) {
+ fflush(stdout);
+ fprintf(stderr, "XML print error\n");
+ ret = -EINVAL;
+ goto parse_error;
+ }
+ }
+
+ dbg_printf("Generating IR... ");
+ fflush(stdout);
+ ret = filter_visitor_ir_generate(ctx);
+ if (ret) {
+ fprintf(stderr, "Generate IR error\n");
+ ret = -EINVAL;
+ goto parse_error;
+ }
+ dbg_printf("done\n");
+
+ dbg_printf("Validating IR... ");
+ fflush(stdout);
+ ret = filter_visitor_ir_check_binary_op_nesting(ctx);
+ if (ret) {
+ ret = -EINVAL;
+ goto parse_error;
+ }
+ dbg_printf("done\n");
+
+ dbg_printf("Generating bytecode... ");
+ fflush(stdout);
+ ret = filter_visitor_bytecode_generate(ctx);
+ if (ret) {
+ fprintf(stderr, "Generate bytecode error\n");
+ ret = -EINVAL;
+ goto parse_error;
+ }
+ dbg_printf("done\n");
+ dbg_printf("Size of bytecode generated: %u bytes.\n",
+ bytecode_get_len(&ctx->bytecode->b));
+
+ memset(&lsm, 0, sizeof(lsm));
+
+ lsm.cmd_type = LTTNG_SET_FILTER;
+
+ /* Copy channel name */
+ copy_string(lsm.u.filter.channel_name, channel_name,
+ sizeof(lsm.u.filter.channel_name));
+ /* Copy event name */
+ copy_string(lsm.u.filter.event_name, event_name,
+ sizeof(lsm.u.filter.event_name));
+ lsm.u.filter.bytecode_len = sizeof(ctx->bytecode->b)
+ + bytecode_get_len(&ctx->bytecode->b);
+
+ copy_lttng_domain(&lsm.domain, &handle->domain);
+
+ copy_string(lsm.session.name, handle->session_name,
+ sizeof(lsm.session.name));
+
+ ret = ask_sessiond_varlen(&lsm, &ctx->bytecode->b,
+ lsm.u.filter.bytecode_len, NULL);
+
+ filter_bytecode_free(ctx);
+ filter_ir_free(ctx);
+ filter_parser_ctx_free(ctx);
+ if (fclose(fmem) != 0) {
+ perror("fclose");
+ }
+ return ret;
+
+parse_error:
+ filter_bytecode_free(ctx);
+ filter_ir_free(ctx);
+ filter_parser_ctx_free(ctx);
+alloc_error:
+ if (fclose(fmem) != 0) {
+ perror("fclose");
+ }
+ return ret;
+}
+
/*
* Disable event(s) of a channel and domain.
* If no event name is specified, all events are disabled.
--- /dev/null
+#ifndef _LTTNG_CTL_MEMSTREAM_H
+#define _LTTNG_CTL_MEMSTREAM_H
+
+/*
+ * src/lib/lttng-ctl/memstream.h
+ *
+ * Copyright 2012 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * memstream compatibility layer.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ */
+
+#define _GNU_SOURCE
+#include <config.h>
+
+#ifdef LTTNG_HAVE_FMEMOPEN
+#include <stdio.h>
+
+static inline
+FILE *lttng_fmemopen(void *buf, size_t size, const char *mode)
+{
+ return fmemopen(buf, size, mode);
+}
+
+#else /* LTTNG_HAVE_FMEMOPEN */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * Fallback for systems which don't have fmemopen. Copy buffer to a
+ * temporary file, and use that file as FILE * input.
+ */
+static inline
+FILE *lttng_fmemopen(void *buf, size_t size, const char *mode)
+{
+ char tmpname[PATH_MAX];
+ size_t len;
+ FILE *fp;
+ int ret;
+
+ /*
+ * Support reading only.
+ */
+ if (strcmp(mode, "rb") != 0) {
+ return NULL;
+ }
+ strncpy(tmpname, "/tmp/lttng-tmp-XXXXXX", PATH_MAX);
+ ret = mkstemp(tmpname);
+ if (ret < 0) {
+ return NULL;
+ }
+ /*
+ * We need to write to the file.
+ */
+ fp = fdopen(ret, "w+");
+ if (!fp) {
+ goto error_unlink;
+ }
+ /* Copy the entire buffer to the file */
+ len = fwrite(buf, sizeof(char), size, fp);
+ if (len != size) {
+ goto error_close;
+ }
+ ret = fseek(fp, 0L, SEEK_SET);
+ if (ret < 0) {
+ perror("fseek");
+ goto error_close;
+ }
+ /* We keep the handle open, but can unlink the file on the VFS. */
+ ret = unlink(tmpname);
+ if (ret < 0) {
+ perror("unlink");
+ }
+ return fp;
+
+error_close:
+ ret = fclose(fp);
+ if (ret < 0) {
+ perror("close");
+ }
+error_unlink:
+ ret = unlink(tmpname);
+ if (ret < 0) {
+ perror("unlink");
+ }
+ return NULL;
+}
+
+#endif /* LTTNG_HAVE_FMEMOPEN */
+
+#ifdef LTTNG_HAVE_OPEN_MEMSTREAM
+
+#include <stdio.h>
+
+static inline
+FILE *lttng_open_memstream(char **ptr, size_t *sizeloc)
+{
+ return open_memstream(ptr, sizeloc);
+}
+
+static inline
+int lttng_close_memstream(char **buf, size_t *size, FILE *fp)
+{
+ return fclose(fp);
+}
+
+#else /* LTTNG_HAVE_OPEN_MEMSTREAM */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * Fallback for systems which don't have open_memstream. Create FILE *
+ * with lttng_open_memstream, but require call to
+ * lttng_close_memstream to flush all data written to the FILE *
+ * into the buffer (which we allocate).
+ */
+static inline
+FILE *lttng_open_memstream(char **ptr, size_t *sizeloc)
+{
+ char tmpname[PATH_MAX];
+ int ret;
+ FILE *fp;
+
+ strncpy(tmpname, "/tmp/lttng-tmp-XXXXXX", PATH_MAX);
+ ret = mkstemp(tmpname);
+ if (ret < 0) {
+ return NULL;
+ }
+ fp = fdopen(ret, "w+");
+ if (!fp) {
+ goto error_unlink;
+ }
+ /*
+ * lttng_flush_memstream will update the buffer content
+ * with read from fp. No need to keep the file around, just the
+ * handle.
+ */
+ ret = unlink(tmpname);
+ if (ret < 0) {
+ perror("unlink");
+ }
+ return fp;
+
+error_unlink:
+ ret = unlink(tmpname);
+ if (ret < 0) {
+ perror("unlink");
+ }
+ return NULL;
+}
+
+/* Get file size, allocate buffer, copy. */
+static inline
+int lttng_close_memstream(char **buf, size_t *size, FILE *fp)
+{
+ size_t len, n;
+ long pos;
+ int ret;
+
+ ret = fflush(fp);
+ if (ret < 0) {
+ perror("fflush");
+ return ret;
+ }
+ ret = fseek(fp, 0L, SEEK_END);
+ if (ret < 0) {
+ perror("fseek");
+ return ret;
+ }
+ pos = ftell(fp);
+ if (ret < 0) {
+ perror("ftell");
+ return ret;
+ }
+ *size = pos;
+ /* add final \0 */
+ *buf = calloc(pos + 1, sizeof(char));
+ if (!*buf) {
+ return -ENOMEM;
+ }
+ ret = fseek(fp, 0L, SEEK_SET);
+ if (ret < 0) {
+ perror("fseek");
+ goto error_free;
+ }
+ /* Copy the entire file into the buffer */
+ n = 0;
+ clearerr(fp);
+ while (!feof(fp) && !ferror(fp) && (*size - n > 0)) {
+ len = fread(*buf, sizeof(char), *size - n, fp);
+ n += len;
+ }
+ if (n != *size) {
+ ret = -1;
+ goto error_close;
+ }
+ ret = fclose(fp);
+ if (ret < 0) {
+ perror("fclose");
+ return ret;
+ }
+ return 0;
+
+error_close:
+ ret = fclose(fp);
+ if (ret < 0) {
+ perror("fclose");
+ }
+error_free:
+ free(*buf);
+ *buf = NULL;
+ return ret;
+}
+
+#endif /* LTTNG_HAVE_OPEN_MEMSTREAM */
+
+#endif /* _LTTNG_CTL_MEMSTREAM_H */
noinst_PROGRAMS = kernel_all_events_basic kernel_event_basic
UTILS=../utils.h
-LIBLTTNG=$(top_srcdir)/src/lib/lttng-ctl/lttng-ctl.c \
- $(top_srcdir)/src/common/sessiond-comm/sessiond-comm.c \
+LIBLTTNG=$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la
+
+SESSIONDSRC=$(top_srcdir)/src/common/sessiond-comm/sessiond-comm.c \
$(top_srcdir)/src/common/sessiond-comm/unix.c \
$(top_srcdir)/src/common/sessiond-comm/inet.c \
$(top_srcdir)/src/common/sessiond-comm/inet6.c
-kernel_all_events_basic_SOURCES = kernel_all_events_basic.c $(UTILS) $(LIBLTTNG)
+kernel_all_events_basic_SOURCES = kernel_all_events_basic.c $(UTILS) \
+ $(SESSIONDSRC)
+kernel_all_events_basic_LDADD = $(LIBLTTNG)
-kernel_event_basic_SOURCES = kernel_event_basic.c $(UTILS) $(LIBLTTNG)
+kernel_event_basic_SOURCES = kernel_event_basic.c $(UTILS) \
+ $(SESSIONDSRC)
+kernel_event_basic_LDADD = $(LIBLTTNG)
noinst_PROGRAMS = ust_global_event_basic ust_global_event_wildcard
UTILS=../utils.h
-LIBLTTNG=$(top_srcdir)/src/lib/lttng-ctl/lttng-ctl.c \
- $(top_srcdir)/src/common/sessiond-comm/sessiond-comm.c \
+LIBLTTNG=$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la
+
+SESSIONDSRC=$(top_srcdir)/src/common/sessiond-comm/sessiond-comm.c \
$(top_srcdir)/src/common/sessiond-comm/unix.c \
$(top_srcdir)/src/common/sessiond-comm/inet.c \
$(top_srcdir)/src/common/sessiond-comm/inet6.c
-ust_global_event_wildcard_SOURCES = ust_global_event_wildcard.c $(UTILS) $(LIBLTTNG)
+ust_global_event_wildcard_SOURCES = ust_global_event_wildcard.c $(UTILS) \
+ $(SESSIONDSRC)
+ust_global_event_wildcard_LDADD = $(LIBLTTNG)
+
+ust_global_event_basic_SOURCES = ust_global_event_basic.c $(UTILS) \
+ $(SESSIONDSRC)
+ust_global_event_basic_LDADD = $(LIBLTTNG)
-ust_global_event_basic_SOURCES = ust_global_event_basic.c $(UTILS) $(LIBLTTNG)
endif