#include "consumer.h"
#include "consumer-stream.h"
+#include "consumer-testpoint.h"
struct lttng_consumer_global_data consumer_data = {
.stream_count = 0,
return channel;
}
-static void free_stream_rcu(struct rcu_head *head)
-{
- struct lttng_ht_node_u64 *node =
- caa_container_of(head, struct lttng_ht_node_u64, head);
- struct lttng_consumer_stream *stream =
- caa_container_of(node, struct lttng_consumer_stream, node);
-
- free(stream);
-}
-
static void free_channel_rcu(struct rcu_head *head)
{
struct lttng_ht_node_u64 *node =
}
}
- while (len > 0) {
- ret = lttng_write(outfd, mmap_base + mmap_offset, len);
- DBG("Consumer mmap write() ret %zd (len %lu)", ret, len);
- if (ret < len) {
- /*
- * This is possible if the fd is closed on the other side (outfd)
- * or any write problem. It can be verbose a bit for a normal
- * execution if for instance the relayd is stopped abruptly. This
- * can happen so set this to a DBG statement.
- */
- DBG("Error in file write mmap");
- if (written == 0) {
- written = -errno;
- }
- /* Socket operation failed. We consider the relayd dead */
- if (errno == EPIPE || errno == EINVAL) {
- relayd_hang_up = 1;
- goto write_error;
- }
- goto end;
- } else if (ret > len) {
- PERROR("Error in file write (ret %zd > len %lu)", ret, len);
- written += ret;
- goto end;
+ /*
+ * This call guarantee that len or less is returned. It's impossible to
+ * receive a ret value that is bigger than len.
+ */
+ ret = lttng_write(outfd, mmap_base + mmap_offset, len);
+ DBG("Consumer mmap write() ret %zd (len %lu)", ret, len);
+ if (ret < 0 || ((size_t) ret != len)) {
+ /*
+ * Report error to caller if nothing was written else at least send the
+ * amount written.
+ */
+ if (ret < 0) {
+ written = -errno;
} else {
- len -= ret;
- mmap_offset += ret;
+ written = ret;
}
- /* This call is useless on a socket so better save a syscall. */
- if (!relayd) {
- /* This won't block, but will start writeout asynchronously */
- lttng_sync_file_range(outfd, stream->out_fd_offset, ret,
- SYNC_FILE_RANGE_WRITE);
- stream->out_fd_offset += ret;
+ /* Socket operation failed. We consider the relayd dead */
+ if (errno == EPIPE || errno == EINVAL) {
+ /*
+ * This is possible if the fd is closed on the other side
+ * (outfd) or any write problem. It can be verbose a bit for a
+ * normal execution if for instance the relayd is stopped
+ * abruptly. This can happen so set this to a DBG statement.
+ */
+ DBG("Consumer mmap write detected relayd hang up");
+ relayd_hang_up = 1;
+ goto write_error;
}
- stream->output_written += ret;
- written += ret;
+
+ /* Unhandled error, print it and stop function right now. */
+ PERROR("Error in write mmap (ret %zd != len %lu)", ret, len);
+ goto end;
+ }
+ stream->output_written += ret;
+ written = ret;
+
+ /* This call is useless on a socket so better save a syscall. */
+ if (!relayd) {
+ /* This won't block, but will start writeout asynchronously */
+ lttng_sync_file_range(outfd, stream->out_fd_offset, len,
+ SYNC_FILE_RANGE_WRITE);
+ stream->out_fd_offset += len;
}
lttng_consumer_sync_trace_file(stream, orig_offset);
SPLICE_F_MOVE | SPLICE_F_MORE);
DBG("splice chan to pipe, ret %zd", ret_splice);
if (ret_splice < 0) {
- PERROR("Error in relay splice");
+ ret = errno;
if (written == 0) {
written = ret_splice;
}
- ret = errno;
+ PERROR("Error in relay splice");
goto splice_error;
}
ret_splice, SPLICE_F_MOVE | SPLICE_F_MORE);
DBG("Consumer splice pipe to file, ret %zd", ret_splice);
if (ret_splice < 0) {
- PERROR("Error in file splice");
+ ret = errno;
if (written == 0) {
written = ret_splice;
}
/* Socket operation failed. We consider the relayd dead */
- if (errno == EBADF || errno == EPIPE) {
+ if (errno == EBADF || errno == EPIPE || errno == ESPIPE) {
WARN("Remote relayd disconnected. Stopping");
relayd_hang_up = 1;
goto write_error;
}
- ret = errno;
+ PERROR("Error in file splice");
goto splice_error;
} else if (ret_splice > len) {
- errno = EINVAL;
- PERROR("Wrote more data than requested %zd (len: %lu)",
- ret_splice, len);
+ /*
+ * We don't expect this code path to be executed but you never know
+ * so this is an extra protection agains a buggy splice().
+ */
written += ret_splice;
ret = errno;
+ PERROR("Wrote more data than requested %zd (len: %lu)", ret_splice,
+ len);
goto splice_error;
+ } else {
+ /* All good, update current len and continue. */
+ len -= ret_splice;
}
- len -= ret_splice;
/* This call is useless on a socket so better save a syscall. */
if (!relayd) {
written += ret_splice;
}
lttng_consumer_sync_trace_file(stream, orig_offset);
-
- ret = ret_splice;
-
goto end;
write_error:
}
}
-void lttng_consumer_close_metadata(void)
+void lttng_consumer_close_all_metadata(void)
{
switch (consumer_data.type) {
case LTTNG_CONSUMER_KERNEL:
* because at this point we are sure that the metadata producer is
* either dead or blocked.
*/
- lttng_ustconsumer_close_metadata(metadata_ht);
+ lttng_ustconsumer_close_all_metadata(metadata_ht);
break;
default:
ERR("Unknown consumer_data type");
void consumer_del_metadata_stream(struct lttng_consumer_stream *stream,
struct lttng_ht *ht)
{
- int ret;
- struct lttng_ht_iter iter;
struct lttng_consumer_channel *free_chan = NULL;
- struct consumer_relayd_sock_pair *relayd;
assert(stream);
/*
DBG3("Consumer delete metadata stream %d", stream->wait_fd);
- if (ht == NULL) {
- /* Means the stream was allocated but not successfully added */
- goto free_stream_rcu;
- }
-
pthread_mutex_lock(&consumer_data.lock);
pthread_mutex_lock(&stream->chan->lock);
pthread_mutex_lock(&stream->lock);
- switch (consumer_data.type) {
- case LTTNG_CONSUMER_KERNEL:
- if (stream->mmap_base != NULL) {
- ret = munmap(stream->mmap_base, stream->mmap_len);
- if (ret != 0) {
- PERROR("munmap metadata stream");
- }
- }
- if (stream->wait_fd >= 0) {
- ret = close(stream->wait_fd);
- if (ret < 0) {
- PERROR("close kernel metadata wait_fd");
- }
- }
- break;
- case LTTNG_CONSUMER32_UST:
- case LTTNG_CONSUMER64_UST:
- if (stream->monitor) {
- /* close the write-side in close_metadata */
- ret = close(stream->ust_metadata_poll_pipe[0]);
- if (ret < 0) {
- PERROR("Close UST metadata read-side poll pipe");
- }
- }
- lttng_ustconsumer_del_stream(stream);
- break;
- default:
- ERR("Unknown consumer_data type");
- assert(0);
- goto end;
- }
-
- rcu_read_lock();
- iter.iter.node = &stream->node.node;
- ret = lttng_ht_del(ht, &iter);
- assert(!ret);
-
- iter.iter.node = &stream->node_channel_id.node;
- ret = lttng_ht_del(consumer_data.stream_per_chan_id_ht, &iter);
- assert(!ret);
-
- iter.iter.node = &stream->node_session_id.node;
- ret = lttng_ht_del(consumer_data.stream_list_ht, &iter);
- assert(!ret);
- rcu_read_unlock();
+ /* Remove any reference to that stream. */
+ consumer_stream_delete(stream, ht);
- if (stream->out_fd >= 0) {
- ret = close(stream->out_fd);
- if (ret) {
- PERROR("close");
- }
- }
-
- /* Check and cleanup relayd */
- rcu_read_lock();
- relayd = consumer_find_relayd(stream->net_seq_idx);
- if (relayd != NULL) {
- uatomic_dec(&relayd->refcount);
- assert(uatomic_read(&relayd->refcount) >= 0);
-
- /* Closing streams requires to lock the control socket. */
- pthread_mutex_lock(&relayd->ctrl_sock_mutex);
- ret = relayd_send_close_stream(&relayd->control_sock,
- stream->relayd_stream_id, stream->next_net_seq_num - 1);
- pthread_mutex_unlock(&relayd->ctrl_sock_mutex);
- if (ret < 0) {
- DBG("Unable to close stream on the relayd. Continuing");
- /*
- * Continue here. There is nothing we can do for the relayd.
- * Chances are that the relayd has closed the socket so we just
- * continue cleaning up.
- */
- }
-
- /* Both conditions are met, we destroy the relayd. */
- if (uatomic_read(&relayd->refcount) == 0 &&
- uatomic_read(&relayd->destroy_flag)) {
- consumer_destroy_relayd(relayd);
- }
- }
- rcu_read_unlock();
+ /* Close down everything including the relayd if one. */
+ consumer_stream_close(stream);
+ /* Destroy tracer buffers of the stream. */
+ consumer_stream_destroy_buffers(stream);
/* Atomically decrement channel refcount since other threads can use it. */
if (!uatomic_sub_return(&stream->chan->refcount, 1)
free_chan = stream->chan;
}
-end:
/*
* Nullify the stream reference so it is not used after deletion. The
- * channel lock MUST be acquired before being able to check for
- * a NULL pointer value.
+ * channel lock MUST be acquired before being able to check for a NULL
+ * pointer value.
*/
stream->chan->metadata_stream = NULL;
consumer_del_channel(free_chan);
}
-free_stream_rcu:
- call_rcu(&stream->node.head, free_stream_rcu);
+ consumer_stream_free(stream);
}
/*
health_register(health_consumerd, HEALTH_CONSUMERD_TYPE_METADATA);
+ if (testpoint(consumerd_thread_metadata)) {
+ goto error_testpoint;
+ }
+
health_code_update();
DBG("Thread metadata poll started");
/* Add metadata stream to the global poll events list */
lttng_poll_add(&events, stream->wait_fd,
- LPOLLIN | LPOLLPRI);
+ LPOLLIN | LPOLLPRI | LPOLLHUP);
}
/* Handle other stream */
lttng_poll_clean(&events);
end_poll:
+error_testpoint:
if (err) {
health_error();
ERR("Health error occurred in %s", __func__);
health_register(health_consumerd, HEALTH_CONSUMERD_TYPE_DATA);
+ if (testpoint(consumerd_thread_data)) {
+ goto error_testpoint;
+ }
+
health_code_update();
local_stream = zmalloc(sizeof(struct lttng_consumer_stream *));
*/
(void) lttng_pipe_write_close(ctx->consumer_metadata_pipe);
+error_testpoint:
if (err) {
health_error();
ERR("Health error occurred in %s", __func__);
health_register(health_consumerd, HEALTH_CONSUMERD_TYPE_CHANNEL);
+ if (testpoint(consumerd_thread_channel)) {
+ goto error_testpoint;
+ }
+
health_code_update();
channel_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
end_poll:
destroy_channel_ht(channel_ht);
end_ht:
+error_testpoint:
DBG("Channel poll thread exiting");
if (err) {
health_error();
health_register(health_consumerd, HEALTH_CONSUMERD_TYPE_SESSIOND);
+ if (testpoint(consumerd_thread_sessiond)) {
+ goto error_testpoint;
+ }
+
health_code_update();
DBG("Creating command socket %s", ctx->consumer_command_sock_path);
*
* NOTE: for now, this only applies to the UST tracer.
*/
- lttng_consumer_close_metadata();
+ lttng_consumer_close_all_metadata();
/*
* when all fds have hung up, the polling thread
}
}
+error_testpoint:
if (err) {
health_error();
ERR("Health error occurred in %s", __func__);