148 lines
2.9 KiB
C
148 lines
2.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2014 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
|
|
*
|
|
*/
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "trace-local.h"
|
|
|
|
/*
|
|
* Stream runs for a single machine. We are going to cheat
|
|
* and use the trace-output and trace-input code to create
|
|
* our pevent. First just create a trace.dat file and then read
|
|
* it to create the pevent and handle.
|
|
*/
|
|
struct tracecmd_input *
|
|
trace_stream_init(struct buffer_instance *instance, int cpu, int fd, int cpus,
|
|
struct hook_list *hooks,
|
|
tracecmd_handle_init_func handle_init, int global)
|
|
{
|
|
struct tracecmd_input *trace_input;
|
|
struct tracecmd_output *trace_output;
|
|
static FILE *fp = NULL;
|
|
static int tfd;
|
|
static int ofd;
|
|
long flags;
|
|
|
|
if (instance->handle) {
|
|
trace_input = instance->handle;
|
|
goto make_pipe;
|
|
}
|
|
|
|
if (!fp) {
|
|
fp = tmpfile();
|
|
if (!fp)
|
|
return NULL;
|
|
tfd = fileno(fp);
|
|
|
|
ofd = dup(tfd);
|
|
trace_output = tracecmd_output_create_fd(ofd);
|
|
if (!trace_output) {
|
|
fclose(fp);
|
|
return NULL;
|
|
}
|
|
tracecmd_output_write_headers(trace_output, NULL);
|
|
tracecmd_output_free(trace_output);
|
|
}
|
|
|
|
lseek(ofd, 0, SEEK_SET);
|
|
|
|
trace_input = tracecmd_alloc_fd(ofd, 0);
|
|
if (!trace_input) {
|
|
close(ofd);
|
|
goto fail;
|
|
}
|
|
|
|
if (tracecmd_read_headers(trace_input, TRACECMD_FILE_PRINTK) < 0)
|
|
goto fail_free_input;
|
|
|
|
if (handle_init)
|
|
handle_init(trace_input, hooks, global);
|
|
|
|
make_pipe:
|
|
/* Do not block on this pipe */
|
|
flags = fcntl(fd, F_GETFL);
|
|
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
if (tracecmd_make_pipe(trace_input, cpu, fd, cpus) < 0)
|
|
goto fail_free_input;
|
|
|
|
instance->handle = trace_input;
|
|
|
|
return trace_input;
|
|
|
|
fail_free_input:
|
|
tracecmd_close(trace_input);
|
|
fail:
|
|
fclose(fp);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int trace_stream_read(struct pid_record_data *pids, int nr_pids, struct timeval *tv)
|
|
{
|
|
struct tep_record *record;
|
|
struct pid_record_data *pid;
|
|
struct pid_record_data *last_pid;
|
|
fd_set rfds;
|
|
int top_rfd = 0;
|
|
int nr_fd;
|
|
int ret;
|
|
int i;
|
|
|
|
last_pid = NULL;
|
|
|
|
again:
|
|
for (i = 0; i < nr_pids; i++) {
|
|
pid = &pids[i];
|
|
|
|
if (!pid->record)
|
|
pid->record = tracecmd_read_data(pid->instance->handle, pid->cpu);
|
|
record = pid->record;
|
|
if (!record && errno == EINVAL)
|
|
/* pipe has closed */
|
|
pid->closed = 1;
|
|
|
|
if (record &&
|
|
(!last_pid || record->ts < last_pid->record->ts))
|
|
last_pid = pid;
|
|
}
|
|
if (last_pid) {
|
|
trace_show_data(last_pid->instance->handle, last_pid->record);
|
|
tracecmd_free_record(last_pid->record);
|
|
last_pid->record = NULL;
|
|
return 1;
|
|
}
|
|
|
|
nr_fd = 0;
|
|
FD_ZERO(&rfds);
|
|
|
|
for (i = 0; i < nr_pids; i++) {
|
|
/* Do not process closed pipes */
|
|
if (pids[i].closed)
|
|
continue;
|
|
nr_fd++;
|
|
if (pids[i].brass[0] > top_rfd)
|
|
top_rfd = pids[i].brass[0];
|
|
|
|
FD_SET(pids[i].brass[0], &rfds);
|
|
}
|
|
|
|
if (!nr_fd)
|
|
return 0;
|
|
|
|
ret = select(top_rfd + 1, &rfds, NULL, NULL, tv);
|
|
|
|
if (ret > 0)
|
|
goto again;
|
|
|
|
return ret;
|
|
}
|