| // SPDX-License-Identifier: LGPL-2.1 |
| /* |
| * Copyright (C) 2022 Google Inc, Steven Rostedt <rostedt@goodmis.org> |
| */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <dirent.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <limits.h> |
| #include <errno.h> |
| #include <sys/stat.h> |
| #include <sys/ioctl.h> |
| #include <sys/select.h> |
| |
| #include <kbuffer.h> |
| |
| #include "tracefs.h" |
| #include "tracefs-local.h" |
| |
| enum { |
| TC_STOP = 1 << 0, /* Stop reading */ |
| TC_PERM_NONBLOCK = 1 << 1, /* read is always non blocking */ |
| TC_NONBLOCK = 1 << 2, /* read is non blocking */ |
| }; |
| |
| struct tracefs_cpu { |
| int fd; |
| int flags; |
| int nfds; |
| int ctrl_pipe[2]; |
| int splice_pipe[2]; |
| int pipe_size; |
| int subbuf_size; |
| int buffered; |
| int splice_read_flags; |
| struct kbuffer *kbuf; |
| void *buffer; |
| void *mapping; |
| }; |
| |
| /** |
| * tracefs_cpu_alloc_fd - create a tracefs_cpu instance for an existing fd |
| * @fd: The file descriptor to attach the tracefs_cpu to |
| * @subbuf_size: The expected size to read the subbuffer with |
| * @nonblock: If true, the file will be opened in O_NONBLOCK mode |
| * |
| * Return a descriptor that can read the tracefs trace_pipe_raw file |
| * that is associated with the given @fd and must be read in @subbuf_size. |
| * |
| * Returns NULL on error. |
| */ |
| struct tracefs_cpu * |
| tracefs_cpu_alloc_fd(int fd, int subbuf_size, bool nonblock) |
| { |
| struct tracefs_cpu *tcpu; |
| int mode = O_RDONLY; |
| int ret; |
| |
| tcpu = calloc(1, sizeof(*tcpu)); |
| if (!tcpu) |
| return NULL; |
| |
| if (nonblock) { |
| mode |= O_NONBLOCK; |
| tcpu->flags |= TC_NONBLOCK | TC_PERM_NONBLOCK; |
| } |
| |
| tcpu->splice_pipe[0] = -1; |
| tcpu->splice_pipe[1] = -1; |
| |
| tcpu->fd = fd; |
| |
| tcpu->subbuf_size = subbuf_size; |
| |
| if (tcpu->flags & TC_PERM_NONBLOCK) { |
| tcpu->ctrl_pipe[0] = -1; |
| tcpu->ctrl_pipe[1] = -1; |
| } else { |
| /* ctrl_pipe is used to break out of blocked reads */ |
| ret = pipe(tcpu->ctrl_pipe); |
| if (ret < 0) |
| goto fail; |
| if (tcpu->ctrl_pipe[0] > tcpu->fd) |
| tcpu->nfds = tcpu->ctrl_pipe[0] + 1; |
| else |
| tcpu->nfds = tcpu->fd + 1; |
| } |
| |
| return tcpu; |
| fail: |
| free(tcpu); |
| return NULL; |
| } |
| |
| static struct tracefs_cpu *cpu_open(struct tracefs_instance *instance, |
| const char *path_fmt, int cpu, bool nonblock) |
| { |
| struct tracefs_cpu *tcpu; |
| struct tep_handle *tep; |
| struct kbuffer *kbuf; |
| char path[128]; |
| int mode = O_RDONLY; |
| int subbuf_size; |
| int ret; |
| int fd; |
| |
| if (nonblock) |
| mode |= O_NONBLOCK; |
| |
| sprintf(path, path_fmt, cpu); |
| |
| fd = tracefs_instance_file_open(instance, path, mode); |
| if (fd < 0) |
| return NULL; |
| |
| tep = tep_alloc(); |
| if (!tep) |
| goto fail; |
| |
| /* Get the size of the page */ |
| ret = tracefs_load_headers(NULL, tep); |
| if (ret < 0) |
| goto fail; |
| |
| subbuf_size = tep_get_sub_buffer_size(tep); |
| |
| kbuf = tep_kbuffer(tep); |
| if (!kbuf) |
| goto fail; |
| |
| tep_free(tep); |
| tep = NULL; |
| |
| tcpu = tracefs_cpu_alloc_fd(fd, subbuf_size, nonblock); |
| if (!tcpu) |
| goto fail; |
| |
| tcpu->kbuf = kbuf; |
| |
| return tcpu; |
| fail: |
| tep_free(tep); |
| close(fd); |
| return NULL; |
| } |
| |
| /** |
| * tracefs_cpu_open - open an instance raw trace file |
| * @instance: the instance (NULL for toplevel) of the cpu raw file to open |
| * @cpu: The CPU that the raw trace file is associated with |
| * @nonblock: If true, the file will be opened in O_NONBLOCK mode |
| * |
| * Return a descriptor that can read the tracefs trace_pipe_raw file |
| * for a give @cpu in a given @instance. |
| * |
| * Returns NULL on error. |
| */ |
| struct tracefs_cpu * |
| tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock) |
| { |
| return cpu_open(instance, "per_cpu/cpu%d/trace_pipe_raw", cpu, nonblock); |
| } |
| |
| /** |
| * tracefs_cpu_snapshot_open - open an instance snapshot raw trace file |
| * @instance: the instance (NULL for toplevel) of the cpu raw file to open |
| * @cpu: The CPU that the raw trace file is associated with |
| * @nonblock: If true, the file will be opened in O_NONBLOCK mode |
| * |
| * Return a descriptor that can read the tracefs snapshot_raw file |
| * for a give @cpu in a given @instance. |
| * |
| * In nonblock mode, it will block if the snapshot is empty and wake up |
| * when there's a new snapshot. |
| * |
| * Returns NULL on error. |
| */ |
| struct tracefs_cpu * |
| tracefs_cpu_snapshot_open(struct tracefs_instance *instance, int cpu, bool nonblock) |
| { |
| return cpu_open(instance, "per_cpu/cpu%d/snapshot_raw", cpu, nonblock); |
| } |
| |
| /** |
| * tracefs_snapshot_snap - takes a snapshot (allocates if necessary) |
| * @instance: The instance to take a snapshot on |
| * |
| * Takes a snapshot of the current ring buffer. |
| * |
| * Returns 0 on success, -1 on error. |
| */ |
| int tracefs_snapshot_snap(struct tracefs_instance *instance) |
| { |
| int ret; |
| |
| ret = tracefs_instance_file_write(instance, "snapshot", "1"); |
| return ret < 0 ? -1 : 0; |
| } |
| |
| /** |
| * tracefs_snapshot_clear - clears the snapshot |
| * @instance: The instance to clear the snapshot |
| * |
| * Clears the snapshot buffer for the @instance. |
| * |
| * Returns 0 on success, -1 on error. |
| */ |
| int tracefs_snapshot_clear(struct tracefs_instance *instance) |
| { |
| int ret; |
| |
| ret = tracefs_instance_file_write(instance, "snapshot", "2"); |
| return ret < 0 ? -1 : 0; |
| } |
| |
| /** |
| * tracefs_snapshot_free - frees the snapshot |
| * @instance: The instance to free the snapshot |
| * |
| * Frees the snapshot for the given @instance. |
| * |
| * Returns 0 on success, -1 on error. |
| */ |
| int tracefs_snapshot_free(struct tracefs_instance *instance) |
| { |
| int ret; |
| |
| ret = tracefs_instance_file_write(instance, "snapshot", "0"); |
| return ret < 0 ? -1 : 0; |
| } |
| |
| /** |
| * tracefs_cpu_open_mapped - open an instance raw trace file and map it |
| * @instance: the instance (NULL for toplevel) of the cpu raw file to open |
| * @cpu: The CPU that the raw trace file is associated with |
| * @nonblock: If true, the file will be opened in O_NONBLOCK mode |
| * |
| * Return a descriptor that can read the tracefs trace_pipe_raw file |
| * for a give @cpu in a given @instance. |
| * |
| * Returns NULL on error. |
| */ |
| struct tracefs_cpu * |
| tracefs_cpu_open_mapped(struct tracefs_instance *instance, int cpu, bool nonblock) |
| { |
| struct tracefs_cpu *tcpu; |
| |
| tcpu = tracefs_cpu_open(instance, cpu, nonblock); |
| if (!tcpu) |
| return NULL; |
| |
| tracefs_cpu_map(tcpu); |
| |
| return tcpu; |
| } |
| |
| static void close_fd(int fd) |
| { |
| if (fd < 0) |
| return; |
| close(fd); |
| } |
| |
| /** |
| * tracefs_cpu_free_fd - clean up the tracefs_cpu descriptor |
| * @tcpu: The descriptor created with tracefs_cpu_alloc_fd() |
| * |
| * Closes all the internal file descriptors that were opened by |
| * tracefs_cpu_alloc_fd(), and frees the descriptor. |
| */ |
| void tracefs_cpu_free_fd(struct tracefs_cpu *tcpu) |
| { |
| close_fd(tcpu->ctrl_pipe[0]); |
| close_fd(tcpu->ctrl_pipe[1]); |
| close_fd(tcpu->splice_pipe[0]); |
| close_fd(tcpu->splice_pipe[1]); |
| |
| trace_unmap(tcpu->mapping); |
| kbuffer_free(tcpu->kbuf); |
| free(tcpu); |
| } |
| |
| /** |
| * tracefs_cpu_close - clean up and close a raw trace descriptor |
| * @tcpu: The descriptor created with tracefs_cpu_open() |
| * |
| * Closes all the file descriptors associated to the trace_pipe_raw |
| * opened by tracefs_cpu_open(). |
| */ |
| void tracefs_cpu_close(struct tracefs_cpu *tcpu) |
| { |
| if (!tcpu) |
| return; |
| |
| close(tcpu->fd); |
| tracefs_cpu_free_fd(tcpu); |
| } |
| |
| /** |
| * tracefs_cpu_read_size - Return the size of the sub buffer |
| * @tcpu: The descriptor that holds the size of the sub buffer |
| * |
| * A lot of the functions that read the data from the trace_pipe_raw |
| * expect the caller to have allocated enough space to store a full |
| * subbuffer. Calling this function is a requirement to do so. |
| */ |
| int tracefs_cpu_read_size(struct tracefs_cpu *tcpu) |
| { |
| if (!tcpu) |
| return -1; |
| return tcpu->subbuf_size; |
| } |
| |
| bool tracefs_cpu_is_mapped(struct tracefs_cpu *tcpu) |
| { |
| return tcpu->mapping != NULL; |
| } |
| |
| /** |
| * tracefs_mapped_is_supported - find out if memory mapping is supported |
| * |
| * Return true if the ring buffer can be memory mapped, or false on |
| * error or it cannot be. |
| */ |
| bool tracefs_mapped_is_supported(void) |
| { |
| struct tracefs_cpu *tcpu; |
| bool ret; |
| |
| tcpu = tracefs_cpu_open_mapped(NULL, 0, false); |
| if (!tcpu) |
| return false; |
| ret = tracefs_cpu_is_mapped(tcpu); |
| tracefs_cpu_close(tcpu); |
| return ret; |
| } |
| |
| int tracefs_cpu_map(struct tracefs_cpu *tcpu) |
| { |
| if (tcpu->mapping) |
| return 0; |
| |
| tcpu->mapping = trace_mmap(tcpu->fd, tcpu->kbuf); |
| return tcpu->mapping ? 0 : -1; |
| } |
| |
| void tracefs_cpu_unmap(struct tracefs_cpu *tcpu) |
| { |
| if (!tcpu->mapping) |
| return; |
| |
| trace_unmap(tcpu->mapping); |
| } |
| |
| static void set_nonblock(struct tracefs_cpu *tcpu) |
| { |
| long flags; |
| |
| if (tcpu->flags & TC_NONBLOCK) |
| return; |
| |
| flags = fcntl(tcpu->fd, F_GETFL); |
| fcntl(tcpu->fd, F_SETFL, flags | O_NONBLOCK); |
| tcpu->flags |= TC_NONBLOCK; |
| } |
| |
| static void unset_nonblock(struct tracefs_cpu *tcpu) |
| { |
| long flags; |
| |
| if (!(tcpu->flags & TC_NONBLOCK)) |
| return; |
| |
| flags = fcntl(tcpu->fd, F_GETFL); |
| flags &= ~O_NONBLOCK; |
| fcntl(tcpu->fd, F_SETFL, flags); |
| tcpu->flags &= ~TC_NONBLOCK; |
| } |
| |
| /* |
| * If set to blocking mode, block until the watermark has been |
| * reached, or the control has said to stop. If the contol is |
| * set, then nonblock will be set to true on the way out. |
| */ |
| static int wait_on_input(struct tracefs_cpu *tcpu, bool nonblock) |
| { |
| fd_set rfds; |
| int ret; |
| |
| if (tcpu->flags & TC_PERM_NONBLOCK) |
| return 1; |
| |
| if (nonblock) { |
| set_nonblock(tcpu); |
| return 1; |
| } else { |
| unset_nonblock(tcpu); |
| } |
| |
| FD_ZERO(&rfds); |
| FD_SET(tcpu->fd, &rfds); |
| FD_SET(tcpu->ctrl_pipe[0], &rfds); |
| |
| ret = select(tcpu->nfds, &rfds, NULL, NULL, NULL); |
| |
| /* Let the application decide what to do with signals and such */ |
| if (ret < 0) |
| return ret; |
| |
| if (FD_ISSET(tcpu->ctrl_pipe[0], &rfds)) { |
| /* Flush the ctrl pipe */ |
| read(tcpu->ctrl_pipe[0], &ret, 1); |
| |
| /* Make nonblock as it is now stopped */ |
| set_nonblock(tcpu); |
| /* Permanently set unblock */ |
| tcpu->flags |= TC_PERM_NONBLOCK; |
| } |
| |
| return FD_ISSET(tcpu->fd, &rfds); |
| } |
| |
| /* If nonblock is set, set errno to EAGAIN on no data */ |
| static int mmap_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock) |
| { |
| void *mapping = tcpu->mapping; |
| int ret; |
| |
| ret = trace_mmap_read(mapping, buffer); |
| if (ret <= 0) { |
| if (!ret && nonblock) |
| errno = EAGAIN; |
| return ret; |
| } |
| |
| /* Write full sub-buffer size, but zero out empty space */ |
| if (ret < tcpu->subbuf_size) |
| memset(buffer + ret, 0, tcpu->subbuf_size - ret); |
| return tcpu->subbuf_size; |
| } |
| |
| /** |
| * tracefs_cpu_read - read from the raw trace file |
| * @tcpu: The descriptor representing the raw trace file |
| * @buffer: Where to read into (must be at least the size of the subbuffer) |
| * @nonblock: Hint to not block on the read if there's no data. |
| * |
| * Reads the trace_pipe_raw files associated to @tcpu into @buffer. |
| * @buffer must be at least the size of the sub buffer of the ring buffer, |
| * which is returned by tracefs_cpu_read_size(). |
| * |
| * If @nonblock is set, and there's no data available, it will return |
| * immediately. Otherwise depending on how @tcpu was opened, it will |
| * block. If @tcpu was opened with nonblock set, then this @nonblock |
| * will make no difference. |
| * |
| * Returns the amount read or -1 on error. |
| */ |
| int tracefs_cpu_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock) |
| { |
| int ret; |
| |
| /* |
| * If nonblock is set, then the wait_on_input() will return |
| * immediately, if there's nothing in the buffer, with |
| * ret == 0. |
| */ |
| ret = wait_on_input(tcpu, nonblock); |
| if (ret <= 0) |
| return ret; |
| |
| if (tcpu->mapping) |
| return mmap_read(tcpu, buffer, nonblock); |
| |
| ret = read(tcpu->fd, buffer, tcpu->subbuf_size); |
| |
| /* It's OK if there's no data to read */ |
| if (ret < 0 && errno == EAGAIN) { |
| /* Reset errno */ |
| errno = 0; |
| ret = 0; |
| } |
| |
| return ret; |
| } |
| |
| static bool get_buffer(struct tracefs_cpu *tcpu) |
| { |
| if (!tcpu->buffer) { |
| tcpu->buffer = malloc(tcpu->subbuf_size); |
| if (!tcpu->buffer) |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * tracefs_cpu_read_buf - read from the raw trace file and return kbuffer |
| * @tcpu: The descriptor representing the raw trace file |
| * @nonblock: Hint to not block on the read if there's no data. |
| * |
| * Reads the trace_pipe_raw files associated to @tcpu and returns a kbuffer |
| * associated with the read that can be used to parse events. |
| * |
| * If @nonblock is set, and there's no data available, it will return |
| * immediately. Otherwise depending on how @tcpu was opened, it will |
| * block. If @tcpu was opened with nonblock set, then this @nonblock |
| * will make no difference. |
| * |
| * Returns a kbuffer associated to the next sub-buffer or NULL on error |
| * or no data to read with nonblock set (EAGAIN will be set). |
| * |
| * The kbuffer returned should not be freed! |
| */ |
| struct kbuffer *tracefs_cpu_read_buf(struct tracefs_cpu *tcpu, bool nonblock) |
| { |
| int ret; |
| |
| /* If mapping is enabled, just use it directly */ |
| if (tcpu->mapping) { |
| ret = wait_on_input(tcpu, nonblock); |
| if (ret <= 0) |
| return NULL; |
| |
| ret = trace_mmap_load_subbuf(tcpu->mapping, tcpu->kbuf); |
| return ret > 0 ? tcpu->kbuf : NULL; |
| } |
| |
| if (!get_buffer(tcpu)) |
| return NULL; |
| |
| ret = tracefs_cpu_read(tcpu, tcpu->buffer, nonblock); |
| if (ret <= 0) |
| return NULL; |
| |
| kbuffer_load_subbuffer(tcpu->kbuf, tcpu->buffer); |
| return tcpu->kbuf; |
| } |
| |
| static int init_splice(struct tracefs_cpu *tcpu) |
| { |
| char *buf; |
| int ret; |
| |
| if (tcpu->splice_pipe[0] >= 0) |
| return 0; |
| |
| ret = pipe(tcpu->splice_pipe); |
| if (ret < 0) |
| return ret; |
| |
| if (str_read_file("/proc/sys/fs/pipe-max-size", &buf, false)) { |
| int size = atoi(buf); |
| fcntl(tcpu->splice_pipe[0], F_SETPIPE_SZ, &size); |
| free(buf); |
| } |
| |
| ret = fcntl(tcpu->splice_pipe[0], F_GETPIPE_SZ, &tcpu->pipe_size); |
| /* |
| * F_GETPIPE_SZ was introduced in 2.6.35, ftrace was introduced |
| * in 2.6.31. If we are running on an older kernel, just fall |
| * back to using subbuf_size for splice(). It could also return |
| * the size of the pipe and not set pipe_size. |
| */ |
| if (ret > 0 && !tcpu->pipe_size) |
| tcpu->pipe_size = ret; |
| else if (ret < 0) |
| tcpu->pipe_size = tcpu->subbuf_size; |
| |
| tcpu->splice_read_flags = SPLICE_F_MOVE; |
| if (tcpu->flags & TC_NONBLOCK) |
| tcpu->splice_read_flags |= SPLICE_F_NONBLOCK; |
| |
| return 0; |
| } |
| |
| /** |
| * tracefs_cpu_buffered_read - Read the raw trace data buffering through a pipe |
| * @tcpu: The descriptor representing the raw trace file |
| * @buffer: Where to read into (must be at least the size of the subbuffer) |
| * @nonblock: Hint to not block on the read if there's no data. |
| * |
| * This is basically the same as tracefs_cpu_read() except that it uses |
| * a pipe through splice to buffer reads. This will batch reads keeping |
| * the reading from the ring buffer less intrusive to the system, as |
| * just reading all the time can cause quite a disturbance. |
| * |
| * Note, one difference between this and tracefs_cpu_read() is that it |
| * will read only in sub buffer pages. If the ring buffer has not filled |
| * a page, then it will not return anything, even with @nonblock set. |
| * Calls to tracefs_cpu_flush() should be done to read the rest of |
| * the file at the end of the trace. |
| * |
| * Returns the amount read or -1 on error. |
| */ |
| int tracefs_cpu_buffered_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock) |
| { |
| int mode = SPLICE_F_MOVE; |
| int ret; |
| |
| if (tcpu->buffered < 0) |
| tcpu->buffered = 0; |
| |
| if (tcpu->buffered) |
| goto do_read; |
| |
| ret = wait_on_input(tcpu, nonblock); |
| if (ret <= 0) |
| return ret; |
| |
| if (tcpu->mapping) |
| return mmap_read(tcpu, buffer, nonblock); |
| |
| if (tcpu->flags & TC_NONBLOCK) |
| mode |= SPLICE_F_NONBLOCK; |
| |
| ret = init_splice(tcpu); |
| if (ret < 0) |
| return ret; |
| |
| ret = splice(tcpu->fd, NULL, tcpu->splice_pipe[1], NULL, |
| tcpu->pipe_size, mode); |
| if (ret <= 0) |
| return ret; |
| |
| tcpu->buffered = ret; |
| |
| do_read: |
| ret = read(tcpu->splice_pipe[0], buffer, tcpu->subbuf_size); |
| if (ret > 0) |
| tcpu->buffered -= ret; |
| return ret; |
| } |
| |
| /** |
| * tracefs_cpu_buffered_read_buf - Read the raw trace data buffering through a pipe |
| * @tcpu: The descriptor representing the raw trace file |
| * @nonblock: Hint to not block on the read if there's no data. |
| * |
| * This is basically the same as tracefs_cpu_read() except that it uses |
| * a pipe through splice to buffer reads. This will batch reads keeping |
| * the reading from the ring buffer less intrusive to the system, as |
| * just reading all the time can cause quite a disturbance. |
| * |
| * Note, one difference between this and tracefs_cpu_read() is that it |
| * will read only in sub buffer pages. If the ring buffer has not filled |
| * a page, then it will not return anything, even with @nonblock set. |
| * Calls to tracefs_cpu_flush() should be done to read the rest of |
| * the file at the end of the trace. |
| * |
| * Returns a kbuffer associated to the next sub-buffer or NULL on error |
| * or no data to read with nonblock set (EAGAIN will be set). |
| * |
| * The kbuffer returned should not be freed! |
| */ |
| struct kbuffer *tracefs_cpu_buffered_read_buf(struct tracefs_cpu *tcpu, bool nonblock) |
| { |
| int ret; |
| |
| /* If mapping is enabled, just use it directly */ |
| if (tcpu->mapping) { |
| ret = wait_on_input(tcpu, nonblock); |
| if (ret <= 0) |
| return NULL; |
| |
| ret = trace_mmap_load_subbuf(tcpu->mapping, tcpu->kbuf); |
| return ret > 0 ? tcpu->kbuf : NULL; |
| } |
| |
| if (!get_buffer(tcpu)) |
| return NULL; |
| |
| ret = tracefs_cpu_buffered_read(tcpu, tcpu->buffer, nonblock); |
| if (ret <= 0) |
| return NULL; |
| |
| kbuffer_load_subbuffer(tcpu->kbuf, tcpu->buffer); |
| return tcpu->kbuf; |
| } |
| |
| /** |
| * tracefs_cpu_stop - Stop a blocked read of the raw tracing file |
| * @tcpu: The descriptor representing the raw trace file |
| * |
| * This will attempt to unblock a task blocked on @tcpu reading it. |
| * On older kernels, it may not do anything for the pipe reads, as |
| * older kernels do not wake up tasks waiting on the ring buffer. |
| * |
| * Returns 0 if the tasks reading the raw tracing file does not |
| * need a nudge. |
| * |
| * Returns 1 if that tasks may need a nudge (send a signal). |
| * |
| * Returns negative on error. |
| */ |
| int tracefs_cpu_stop(struct tracefs_cpu *tcpu) |
| { |
| int ret = 1; |
| |
| if (tcpu->flags & TC_PERM_NONBLOCK) |
| return 0; |
| |
| ret = write(tcpu->ctrl_pipe[1], &ret, 1); |
| if (ret < 0) |
| return ret; |
| |
| /* Calling ioctl() on recent kernels will wake up the waiters */ |
| ret = ioctl(tcpu->fd, 0); |
| if (ret < 0) |
| ret = 1; |
| else |
| ret = 0; |
| |
| set_nonblock(tcpu); |
| |
| return ret; |
| } |
| |
| /** |
| * tracefs_cpu_flush - Finish out and read the rest of the raw tracing file |
| * @tcpu: The descriptor representing the raw trace file |
| * @buffer: Where to read into (must be at least the size of the subbuffer) |
| * |
| * Reads the trace_pipe_raw file associated by the @tcpu and puts it |
| * into @buffer, which must be the size of the sub buffer which is retrieved. |
| * by tracefs_cpu_read_size(). This should be called at the end of tracing |
| * to get the rest of the data. |
| * |
| * This will set the file descriptor for reading to non-blocking mode. |
| * |
| * Returns the number of bytes read, or negative on error. |
| */ |
| int tracefs_cpu_flush(struct tracefs_cpu *tcpu, void *buffer) |
| { |
| int ret; |
| |
| /* Make sure that reading is now non blocking */ |
| set_nonblock(tcpu); |
| |
| if (tcpu->buffered < 0) |
| tcpu->buffered = 0; |
| |
| if (tcpu->mapping) |
| return mmap_read(tcpu, buffer, false); |
| |
| if (tcpu->buffered) { |
| ret = read(tcpu->splice_pipe[0], buffer, tcpu->subbuf_size); |
| if (ret > 0) |
| tcpu->buffered -= ret; |
| return ret; |
| } |
| |
| ret = read(tcpu->fd, buffer, tcpu->subbuf_size); |
| if (ret > 0 && tcpu->buffered) |
| tcpu->buffered -= ret; |
| |
| /* It's OK if there's no data to read */ |
| if (ret < 0 && errno == EAGAIN) { |
| /* Reset errno */ |
| errno = 0; |
| ret = 0; |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * tracefs_cpu_flush_buf - Finish out and read the rest of the raw tracing file |
| * @tcpu: The descriptor representing the raw trace file |
| * |
| * Reads the trace_pipe_raw file associated by the @tcpu and puts it |
| * into @buffer, which must be the size of the sub buffer which is retrieved. |
| * by tracefs_cpu_read_size(). This should be called at the end of tracing |
| * to get the rest of the data. |
| * |
| * This will set the file descriptor for reading to non-blocking mode. |
| */ |
| struct kbuffer *tracefs_cpu_flush_buf(struct tracefs_cpu *tcpu) |
| { |
| int ret; |
| |
| if (!get_buffer(tcpu)) |
| return NULL; |
| |
| if (tcpu->mapping) { |
| /* Make sure that reading is now non blocking */ |
| set_nonblock(tcpu); |
| ret = trace_mmap_load_subbuf(tcpu->mapping, tcpu->kbuf); |
| return ret > 0 ? tcpu->kbuf : NULL; |
| } |
| |
| ret = tracefs_cpu_flush(tcpu, tcpu->buffer); |
| if (ret <= 0) |
| return NULL; |
| |
| kbuffer_load_subbuffer(tcpu->kbuf, tcpu->buffer); |
| return tcpu->kbuf; |
| } |
| |
| /** |
| * tracefs_cpu_flush_write - Finish out and read the rest of the raw tracing file |
| * @tcpu: The descriptor representing the raw trace file |
| * @wfd: The write file descriptor to write the data to |
| * |
| * Reads the trace_pipe_raw file associated by the @tcpu and writes it to |
| * @wfd. This should be called at the end of tracing to get the rest of the data. |
| * |
| * Returns the number of bytes written, or negative on error. |
| */ |
| int tracefs_cpu_flush_write(struct tracefs_cpu *tcpu, int wfd) |
| { |
| char buffer[tcpu->subbuf_size]; |
| int ret; |
| |
| ret = tracefs_cpu_flush(tcpu, buffer); |
| if (ret > 0) |
| ret = write(wfd, buffer, ret); |
| |
| /* It's OK if there's no data to read */ |
| if (ret < 0 && errno == EAGAIN) |
| ret = 0; |
| |
| return ret; |
| } |
| |
| /** |
| * tracefs_cpu_write - Write the raw trace file into a file descriptor |
| * @tcpu: The descriptor representing the raw trace file |
| * @wfd: The write file descriptor to write the data to |
| * @nonblock: Hint to not block on the read if there's no data. |
| * |
| * This will pipe the data from the trace_pipe_raw file associated with @tcpu |
| * into the @wfd file descriptor. If @nonblock is set, then it will not |
| * block on if there's nothing to write. Note, it will only write sub buffer |
| * size data to @wfd. Calls to tracefs_cpu_flush_write() are needed to |
| * write out the rest. |
| * |
| * Returns the number of bytes read or negative on error. |
| */ |
| int tracefs_cpu_write(struct tracefs_cpu *tcpu, int wfd, bool nonblock) |
| { |
| char buffer[tcpu->subbuf_size]; |
| int mode = SPLICE_F_MOVE; |
| int tot_write = 0; |
| int tot; |
| int ret; |
| |
| if (tcpu->mapping) { |
| int r = tracefs_cpu_read(tcpu, buffer, nonblock); |
| if (r < 0) |
| return r; |
| do { |
| ret = write(wfd, buffer, r); |
| if (ret < 0) |
| return ret; |
| r -= ret; |
| tot_write += ret; |
| } while (r > 0); |
| return tot_write; |
| } |
| |
| ret = wait_on_input(tcpu, nonblock); |
| if (ret <= 0) |
| return ret; |
| |
| if (tcpu->flags & TC_NONBLOCK) |
| mode |= SPLICE_F_NONBLOCK; |
| |
| ret = init_splice(tcpu); |
| if (ret < 0) |
| return ret; |
| |
| tot = splice(tcpu->fd, NULL, tcpu->splice_pipe[1], NULL, |
| tcpu->pipe_size, mode); |
| if (tot < 0) |
| return tot; |
| |
| if (tot == 0) |
| return 0; |
| |
| ret = splice(tcpu->splice_pipe[0], NULL, wfd, NULL, |
| tot, SPLICE_F_MOVE | SPLICE_F_NONBLOCK); |
| |
| if (ret >= 0) |
| return ret; |
| |
| /* Some file systems do not allow splicing, try writing instead */ |
| do { |
| int r = tcpu->subbuf_size; |
| |
| if (r > tot) |
| r = tot; |
| |
| ret = read(tcpu->splice_pipe[0], buffer, r); |
| if (ret > 0) { |
| tot -= ret; |
| ret = write(wfd, buffer, ret); |
| } |
| if (ret > 0) |
| tot_write += ret; |
| } while (ret > 0); |
| |
| if (ret < 0) |
| return ret; |
| |
| return tot_write; |
| } |
| |
| /** |
| * tracefs_cpu_pipe - Write the raw trace file into a pipe descriptor |
| * @tcpu: The descriptor representing the raw trace file |
| * @wfd: The write file descriptor to write the data to (must be a pipe) |
| * @nonblock: Hint to not block on the read if there's no data. |
| * |
| * This will splice directly the file descriptor of the trace_pipe_raw |
| * file to the given @wfd, which must be a pipe. This can also be used |
| * if @tcpu was created with tracefs_cpu_create_fd() where the passed |
| * in @fd there was a pipe, then @wfd does not need to be a pipe. |
| * |
| * Returns the number of bytes read or negative on error. |
| */ |
| int tracefs_cpu_pipe(struct tracefs_cpu *tcpu, int wfd, bool nonblock) |
| { |
| int mode = SPLICE_F_MOVE; |
| int ret; |
| |
| if (tcpu->mapping) |
| return tracefs_cpu_write(tcpu, wfd, nonblock); |
| |
| ret = wait_on_input(tcpu, nonblock); |
| if (ret <= 0) |
| return ret; |
| |
| if (tcpu->flags & TC_NONBLOCK) |
| mode |= SPLICE_F_NONBLOCK; |
| |
| ret = splice(tcpu->fd, NULL, wfd, NULL, |
| tcpu->pipe_size, mode); |
| return ret; |
| } |