X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fkernel.c;h=123be4be523d24194309cc93e5c9a52e9fba16c7;hp=fea51b761c8ea2f0b224f2dfd2dc8589a9c8ad8b;hb=55d097957f5bb8138959ad2202a40d85d49f029e;hpb=799e2c4f4ecb595fcb52c3e6affd3b4ec7b5b24e diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index fea51b761..123be4be5 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -1,19 +1,18 @@ /* * Copyright (C) 2011 - David Goulet * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; only version 2 - * of the License. + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * 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 @@ -23,11 +22,15 @@ #include #include #include +#include #include #include +#include +#include "consumer.h" #include "kernel.h" +#include "kernel-consumer.h" #include "kern-modules.h" /* @@ -38,6 +41,9 @@ int kernel_add_channel_context(struct ltt_kernel_channel *chan, { int ret; + assert(chan); + assert(ctx); + DBG("Adding context to channel %s", chan->channel->name); ret = kernctl_add_context(chan->fd, ctx); if (ret < 0) { @@ -64,35 +70,6 @@ error: return ret; } -/* - * Add context on a kernel event. - */ -int kernel_add_event_context(struct ltt_kernel_event *event, - struct lttng_kernel_context *ctx) -{ - int ret; - - DBG("Adding context to event %s", event->event->name); - ret = kernctl_add_context(event->fd, ctx); - if (ret < 0) { - PERROR("add context ioctl"); - goto error; - } - - event->ctx = zmalloc(sizeof(struct lttng_kernel_context)); - if (event->ctx == NULL) { - PERROR("zmalloc event context"); - goto error; - } - - memcpy(event->ctx, ctx, sizeof(struct lttng_kernel_context)); - - return 0; - -error: - return ret; -} - /* * Create a new kernel session, register it to the kernel tracer and add it to * the session daemon session. @@ -102,8 +79,10 @@ int kernel_create_session(struct ltt_session *session, int tracer_fd) int ret; struct ltt_kernel_session *lks; + assert(session); + /* Allocate data structure */ - lks = trace_kernel_create_session(session->path); + lks = trace_kernel_create_session(); if (lks == NULL) { ret = -1; goto error; @@ -123,6 +102,7 @@ int kernel_create_session(struct ltt_session *session, int tracer_fd) PERROR("fcntl session fd"); } + lks->id = session->id; lks->consumer_fds_sent = 0; session->kernel_session = lks; @@ -131,6 +111,9 @@ int kernel_create_session(struct ltt_session *session, int tracer_fd) return 0; error: + if (lks) { + trace_kernel_destroy_session(lks); + } return ret; } @@ -139,17 +122,26 @@ error: * kernel session. */ int kernel_create_channel(struct ltt_kernel_session *session, - struct lttng_channel *chan, char *path) + struct lttng_channel *chan) { int ret; struct ltt_kernel_channel *lkc; + assert(session); + assert(chan); + /* Allocate kernel channel */ - lkc = trace_kernel_create_channel(chan, path); + lkc = trace_kernel_create_channel(chan); if (lkc == NULL) { goto error; } + DBG3("Kernel create channel %s with attr: %d, %" PRIu64 ", %" PRIu64 ", %u, %u, %d, %d", + chan->name, lkc->channel->attr.overwrite, + lkc->channel->attr.subbuf_size, lkc->channel->attr.num_subbuf, + lkc->channel->attr.switch_timer_interval, lkc->channel->attr.read_timer_interval, + lkc->channel->attr.live_timer_interval, lkc->channel->attr.output); + /* Kernel tracer channel creation */ ret = kernctl_create_channel(session->fd, &lkc->channel->attr); if (ret < 0) { @@ -168,13 +160,17 @@ int kernel_create_channel(struct ltt_kernel_session *session, /* Add channel to session */ cds_list_add(&lkc->list, &session->channel_list.head); session->channel_count++; + lkc->session = session; - DBG("Kernel channel %s created (fd: %d and path: %s)", - lkc->channel->name, lkc->fd, lkc->pathname); + DBG("Kernel channel %s created (fd: %d)", lkc->channel->name, lkc->fd); return 0; error: + if (lkc) { + free(lkc->channel); + free(lkc); + } return -1; } @@ -188,6 +184,9 @@ int kernel_create_event(struct lttng_event *ev, int ret; struct ltt_kernel_event *event; + assert(ev); + assert(channel); + event = trace_kernel_create_event(ev); if (event == NULL) { ret = -1; @@ -196,7 +195,13 @@ int kernel_create_event(struct lttng_event *ev, ret = kernctl_create_event(channel->fd, event->event); if (ret < 0) { - if (errno != EEXIST) { + switch (errno) { + case EEXIST: + break; + case ENOSYS: + WARN("Event type not implemented"); + break; + default: PERROR("create event ioctl"); } ret = -errno; @@ -245,6 +250,8 @@ int kernel_disable_channel(struct ltt_kernel_channel *chan) { int ret; + assert(chan); + ret = kernctl_disable(chan->fd); if (ret < 0) { PERROR("disable chan ioctl"); @@ -268,6 +275,8 @@ int kernel_enable_channel(struct ltt_kernel_channel *chan) { int ret; + assert(chan); + ret = kernctl_enable(chan->fd); if (ret < 0 && errno != EEXIST) { PERROR("Enable kernel chan"); @@ -290,9 +299,18 @@ int kernel_enable_event(struct ltt_kernel_event *event) { int ret; + assert(event); + ret = kernctl_enable(event->fd); - if (ret < 0 && errno != EEXIST) { - PERROR("enable kernel event"); + if (ret < 0) { + switch (errno) { + case EEXIST: + ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("enable kernel event"); + break; + } goto error; } @@ -312,9 +330,18 @@ int kernel_disable_event(struct ltt_kernel_event *event) { int ret; + assert(event); + ret = kernctl_disable(event->fd); - if (ret < 0 && errno != EEXIST) { - PERROR("disable kernel event"); + if (ret < 0) { + switch (errno) { + case EEXIST: + ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("disable kernel event"); + break; + } goto error; } @@ -331,13 +358,15 @@ error: * Create kernel metadata, open from the kernel tracer and add it to the * kernel session. */ -int kernel_open_metadata(struct ltt_kernel_session *session, char *path) +int kernel_open_metadata(struct ltt_kernel_session *session) { int ret; - struct ltt_kernel_metadata *lkm; + struct ltt_kernel_metadata *lkm = NULL; + + assert(session); /* Allocate kernel metadata */ - lkm = trace_kernel_create_metadata(path); + lkm = trace_kernel_create_metadata(); if (lkm == NULL) { goto error; } @@ -345,7 +374,7 @@ int kernel_open_metadata(struct ltt_kernel_session *session, char *path) /* Kernel tracer metadata creation */ ret = kernctl_open_metadata(session->fd, &lkm->conf->attr); if (ret < 0) { - goto error; + goto error_open; } lkm->fd = ret; @@ -357,10 +386,12 @@ int kernel_open_metadata(struct ltt_kernel_session *session, char *path) session->metadata = lkm; - DBG("Kernel metadata opened (fd: %d and path: %s)", lkm->fd, lkm->pathname); + DBG("Kernel metadata opened (fd: %d)", lkm->fd); return 0; +error_open: + trace_kernel_destroy_metadata(lkm); error: return -1; } @@ -372,6 +403,8 @@ int kernel_start_session(struct ltt_kernel_session *session) { int ret; + assert(session); + ret = kernctl_start_session(session->fd); if (ret < 0) { PERROR("ioctl start session"); @@ -409,6 +442,8 @@ int kernel_calibrate(int fd, struct lttng_kernel_calibrate *calibrate) { int ret; + assert(calibrate); + ret = kernctl_calibrate(fd, calibrate); if (ret < 0) { PERROR("calibrate ioctl"); @@ -426,9 +461,11 @@ int kernel_metadata_flush_buffer(int fd) { int ret; + DBG("Kernel flushing metadata buffer on fd %d", fd); + ret = kernctl_buffer_flush(fd); if (ret < 0) { - ERR("Fail to flush metadata buffers %d (ret: %d", fd, ret); + ERR("Fail to flush metadata buffers %d (ret: %d)", fd, ret); } return 0; @@ -442,6 +479,8 @@ int kernel_flush_buffer(struct ltt_kernel_channel *channel) int ret; struct ltt_kernel_stream *stream; + assert(channel); + DBG("Flush buffer for channel %s", channel->channel->name); cds_list_for_each_entry(stream, &channel->stream_list.head, list) { @@ -464,6 +503,8 @@ int kernel_stop_session(struct ltt_kernel_session *session) { int ret; + assert(session); + ret = kernctl_stop_session(session->fd); if (ret < 0) { goto error; @@ -485,11 +526,13 @@ error: */ int kernel_open_channel_stream(struct ltt_kernel_channel *channel) { - int ret; + int ret, count = 0; struct ltt_kernel_stream *lks; + assert(channel); + while ((ret = kernctl_create_stream(channel->fd)) >= 0) { - lks = trace_kernel_create_stream(); + lks = trace_kernel_create_stream(channel->channel->name, count); if (lks == NULL) { ret = close(ret); if (ret) { @@ -505,19 +548,18 @@ int kernel_open_channel_stream(struct ltt_kernel_channel *channel) PERROR("fcntl session fd"); } - ret = asprintf(&lks->pathname, "%s/%s_%d", - channel->pathname, channel->channel->name, channel->stream_count); - if (ret < 0) { - PERROR("asprintf kernel create stream"); - goto error; - } + lks->tracefile_size = channel->channel->attr.tracefile_size; + lks->tracefile_count = channel->channel->attr.tracefile_count; /* Add stream to channe stream list */ cds_list_add(&lks->list, &channel->stream_list.head); channel->stream_count++; - DBG("Kernel stream %d created (fd: %d, state: %d, path: %s)", - channel->stream_count, lks->fd, lks->state, lks->pathname); + /* Increment counter which represent CPU number. */ + count++; + + DBG("Kernel stream %s created (fd: %d, state: %d)", lks->name, lks->fd, + lks->state); } return channel->stream_count; @@ -533,6 +575,8 @@ int kernel_open_metadata_stream(struct ltt_kernel_session *session) { int ret; + assert(session); + ret = kernctl_create_stream(session->metadata->fd); if (ret < 0) { PERROR("kernel create metadata stream"); @@ -561,10 +605,11 @@ ssize_t kernel_list_events(int tracer_fd, struct lttng_event **events) int fd, pos, ret; char *event; size_t nbmem, count = 0; - ssize_t size; FILE *fp; struct lttng_event *elist; + assert(events); + fd = kernctl_tracepoint_list(tracer_fd); if (fd < 0) { PERROR("kernel tracepoint list"); @@ -583,24 +628,35 @@ ssize_t kernel_list_events(int tracer_fd, struct lttng_event **events) */ nbmem = KERNEL_EVENT_INIT_LIST_SIZE; elist = zmalloc(sizeof(struct lttng_event) * nbmem); + if (elist == NULL) { + PERROR("alloc list events"); + count = -ENOMEM; + goto end; + } - while ((size = fscanf(fp, "event { name = %m[^;]; };%n\n", &event, &pos)) == 1) { + while (fscanf(fp, "event { name = %m[^;]; };%n\n", &event, &pos) == 1) { if (count >= nbmem) { + struct lttng_event *new_elist; + DBG("Reallocating event list from %zu to %zu bytes", nbmem, nbmem * 2); /* Double the size */ nbmem <<= 1; - elist = realloc(elist, nbmem * sizeof(struct lttng_event)); - if (elist == NULL) { + new_elist = realloc(elist, nbmem * sizeof(struct lttng_event)); + if (new_elist == NULL) { PERROR("realloc list events"); + free(event); + free(elist); count = -ENOMEM; goto end; } + elist = new_elist; } strncpy(elist[count].name, event, LTTNG_SYMBOL_NAME_LEN); elist[count].name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; elist[count].enabled = -1; count++; + free(event); } *events = elist; @@ -685,3 +741,206 @@ int init_kernel_workarounds(void) end_boot_id: return 0; } + +/* + * Complete teardown of a kernel session. + */ +void kernel_destroy_session(struct ltt_kernel_session *ksess) +{ + if (ksess == NULL) { + DBG3("No kernel session when tearing down session"); + return; + } + + DBG("Tearing down kernel session"); + + /* + * Destroy channels on the consumer if in no output mode because the + * streams are in *no* monitor mode so we have to send a command to clean + * them up or else they leaked. + */ + if (!ksess->output_traces) { + int ret; + struct consumer_socket *socket; + struct lttng_ht_iter iter; + + /* For each consumer socket. */ + cds_lfht_for_each_entry(ksess->consumer->socks->ht, &iter.iter, + socket, node.node) { + struct ltt_kernel_channel *chan; + + /* For each channel, ask the consumer to destroy it. */ + cds_list_for_each_entry(chan, &ksess->channel_list.head, list) { + ret = kernel_consumer_destroy_channel(socket, chan); + if (ret < 0) { + /* Consumer is probably dead. Use next socket. */ + continue; + } + } + } + } + + /* Close any relayd session */ + consumer_output_send_destroy_relayd(ksess->consumer); + + trace_kernel_destroy_session(ksess); +} + +/* + * Destroy a kernel channel object. It does not do anything on the tracer side. + */ +void kernel_destroy_channel(struct ltt_kernel_channel *kchan) +{ + struct ltt_kernel_session *ksess = NULL; + + assert(kchan); + assert(kchan->channel); + + DBG3("Kernel destroy channel %s", kchan->channel->name); + + /* Update channel count of associated session. */ + if (kchan->session) { + /* Keep pointer reference so we can update it after the destroy. */ + ksess = kchan->session; + } + + trace_kernel_destroy_channel(kchan); + + /* + * At this point the kernel channel is not visible anymore. This is safe + * since in order to work on a visible kernel session, the tracing session + * lock (ltt_session.lock) MUST be acquired. + */ + if (ksess) { + ksess->channel_count--; + } +} + +/* + * Take a snapshot for a given kernel session. + * + * Return 0 on success or else return a LTTNG_ERR code. + */ +int kernel_snapshot_record(struct ltt_kernel_session *ksess, + struct snapshot_output *output, int wait, unsigned int nb_streams) +{ + int err, ret, saved_metadata_fd; + struct consumer_socket *socket; + struct lttng_ht_iter iter; + struct ltt_kernel_metadata *saved_metadata; + uint64_t max_size_per_stream = 0; + + assert(ksess); + assert(ksess->consumer); + assert(output); + + DBG("Kernel snapshot record started"); + + /* Save current metadata since the following calls will change it. */ + saved_metadata = ksess->metadata; + saved_metadata_fd = ksess->metadata_stream_fd; + + rcu_read_lock(); + + ret = kernel_open_metadata(ksess); + if (ret < 0) { + ret = LTTNG_ERR_KERN_META_FAIL; + goto error; + } + + ret = kernel_open_metadata_stream(ksess); + if (ret < 0) { + ret = LTTNG_ERR_KERN_META_FAIL; + goto error_open_stream; + } + + if (output->max_size > 0 && nb_streams > 0) { + max_size_per_stream = output->max_size / nb_streams; + } + + /* Send metadata to consumer and snapshot everything. */ + cds_lfht_for_each_entry(ksess->consumer->socks->ht, &iter.iter, + socket, node.node) { + struct consumer_output *saved_output; + struct ltt_kernel_channel *chan; + + /* + * Temporarly switch consumer output for our snapshot output. As long + * as the session lock is taken, this is safe. + */ + saved_output = ksess->consumer; + ksess->consumer = output->consumer; + + pthread_mutex_lock(socket->lock); + /* This stream must not be monitored by the consumer. */ + ret = kernel_consumer_add_metadata(socket, ksess, 0); + pthread_mutex_unlock(socket->lock); + /* Put back the saved consumer output into the session. */ + ksess->consumer = saved_output; + if (ret < 0) { + ret = LTTNG_ERR_KERN_CONSUMER_FAIL; + goto error_consumer; + } + + /* For each channel, ask the consumer to snapshot it. */ + cds_list_for_each_entry(chan, &ksess->channel_list.head, list) { + if (max_size_per_stream && + chan->channel->attr.subbuf_size > max_size_per_stream) { + ret = LTTNG_ERR_INVALID; + DBG3("Kernel snapshot record maximum stream size %" PRIu64 + " is smaller than subbuffer size of %" PRIu64, + max_size_per_stream, chan->channel->attr.subbuf_size); + (void) kernel_consumer_destroy_metadata(socket, + ksess->metadata); + goto error_consumer; + } + + pthread_mutex_lock(socket->lock); + ret = consumer_snapshot_channel(socket, chan->fd, output, 0, + ksess->uid, ksess->gid, + DEFAULT_KERNEL_TRACE_DIR, wait, + max_size_per_stream); + pthread_mutex_unlock(socket->lock); + if (ret < 0) { + ret = LTTNG_ERR_KERN_CONSUMER_FAIL; + (void) kernel_consumer_destroy_metadata(socket, + ksess->metadata); + goto error_consumer; + } + } + + /* Snapshot metadata, */ + pthread_mutex_lock(socket->lock); + ret = consumer_snapshot_channel(socket, ksess->metadata->fd, output, + 1, ksess->uid, ksess->gid, + DEFAULT_KERNEL_TRACE_DIR, wait, max_size_per_stream); + pthread_mutex_unlock(socket->lock); + if (ret < 0) { + ret = LTTNG_ERR_KERN_CONSUMER_FAIL; + goto error_consumer; + } + + /* + * The metadata snapshot is done, ask the consumer to destroy it since + * it's not monitored on the consumer side. + */ + (void) kernel_consumer_destroy_metadata(socket, ksess->metadata); + } + +error_consumer: + /* Close newly opened metadata stream. It's now on the consumer side. */ + err = close(ksess->metadata_stream_fd); + if (err < 0) { + PERROR("close snapshot kernel"); + } + +error_open_stream: + trace_kernel_destroy_metadata(ksess->metadata); +error: + /* Restore metadata state.*/ + ksess->metadata = saved_metadata; + ksess->metadata_stream_fd = saved_metadata_fd; + + rcu_read_unlock(); + return ret; +}