xref: /aosp_15_r20/external/trace-cmd/tracecmd/trace-stream.c (revision 58e6ee5f017f6a8912852c892d18457e4bafb554)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2014 Red Hat Inc, Steven Rostedt <[email protected]>
4  *
5  */
6 #include <stdio.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 
11 #include <sys/time.h>
12 #include <sys/types.h>
13 
14 #include "trace-local.h"
15 
16 /*
17  * Stream runs for a single machine. We are going to cheat
18  * and use the trace-output and trace-input code to create
19  * our pevent. First just create a trace.dat file and then read
20  * it to create the pevent and handle.
21  */
22 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)23 trace_stream_init(struct buffer_instance *instance, int cpu, int fd, int cpus,
24 		  struct hook_list *hooks,
25 		  tracecmd_handle_init_func handle_init, int global)
26 {
27 	struct tracecmd_input *trace_input;
28 	struct tracecmd_output *trace_output;
29 	static FILE *fp = NULL;
30 	static int tfd;
31 	static int ofd;
32 	long flags;
33 
34 	if (instance->handle) {
35 		trace_input = instance->handle;
36 		goto make_pipe;
37 	}
38 
39 	if (!fp) {
40 		fp = tmpfile();
41 		if (!fp)
42 			return NULL;
43 		tfd = fileno(fp);
44 
45 		ofd = dup(tfd);
46 		trace_output = tracecmd_output_create_fd(ofd);
47 		if (!trace_output) {
48 			fclose(fp);
49 			return NULL;
50 		}
51 		tracecmd_output_write_headers(trace_output, NULL);
52 		tracecmd_output_free(trace_output);
53 	}
54 
55 	lseek(ofd, 0, SEEK_SET);
56 
57 	trace_input = tracecmd_alloc_fd(ofd, 0);
58 	if (!trace_input) {
59 		close(ofd);
60 		goto fail;
61 	}
62 
63 	if (tracecmd_read_headers(trace_input, TRACECMD_FILE_PRINTK) < 0)
64 		goto fail_free_input;
65 
66 	if (handle_init)
67 		handle_init(trace_input, hooks, global);
68 
69  make_pipe:
70 	/* Do not block on this pipe */
71 	flags = fcntl(fd, F_GETFL);
72 	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
73 
74 	if (tracecmd_make_pipe(trace_input, cpu, fd, cpus) < 0)
75 		goto fail_free_input;
76 
77 	instance->handle = trace_input;
78 
79 	return trace_input;
80 
81  fail_free_input:
82 	tracecmd_close(trace_input);
83  fail:
84 	fclose(fp);
85 
86 	return NULL;
87 }
88 
trace_stream_read(struct pid_record_data * pids,int nr_pids,struct timeval * tv)89 int trace_stream_read(struct pid_record_data *pids, int nr_pids, struct timeval *tv)
90 {
91 	struct tep_record *record;
92 	struct pid_record_data *pid;
93 	struct pid_record_data *last_pid;
94 	fd_set rfds;
95 	int top_rfd = 0;
96 	int nr_fd;
97 	int ret;
98 	int i;
99 
100 	last_pid = NULL;
101 
102  again:
103 	for (i = 0; i < nr_pids; i++) {
104 		pid = &pids[i];
105 
106 		if (!pid->record)
107 			pid->record = tracecmd_read_data(pid->instance->handle, pid->cpu);
108 		record = pid->record;
109 		if (!record && errno == EINVAL)
110 			/* pipe has closed */
111 			pid->closed = 1;
112 
113 		if (record &&
114 		    (!last_pid || record->ts < last_pid->record->ts))
115 			last_pid = pid;
116 	}
117 	if (last_pid) {
118 		trace_show_data(last_pid->instance->handle, last_pid->record);
119 		tracecmd_free_record(last_pid->record);
120 		last_pid->record = NULL;
121 		return 1;
122 	}
123 
124 	nr_fd = 0;
125 	FD_ZERO(&rfds);
126 
127 	for (i = 0; i < nr_pids; i++) {
128 		/* Do not process closed pipes */
129 		if (pids[i].closed)
130 			continue;
131 		nr_fd++;
132 		if (pids[i].brass[0] > top_rfd)
133 			top_rfd = pids[i].brass[0];
134 
135 		FD_SET(pids[i].brass[0], &rfds);
136 	}
137 
138 	if (!nr_fd)
139 		return 0;
140 
141 	ret = select(top_rfd + 1, &rfds, NULL, NULL, tv);
142 
143 	if (ret > 0)
144 		goto again;
145 
146 	return ret;
147 }
148