• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3  * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
4  *
5  */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <fcntl.h>
9 #include <time.h>
10 #include <poll.h>
11 #include <unistd.h>
12 #include <errno.h>
13 
14 #include "tracefs.h"
15 #include "trace-cmd-private.h"
16 #include "trace-cmd-local.h"
17 #include "event-utils.h"
18 
19 /* F_GETPIPE_SZ was introduced in 2.6.35, older systems don't have it */
20 #ifndef F_GETPIPE_SZ
21 # define F_GETPIPE_SZ	1032 /* The Linux number for the option */
22 #endif
23 
24 #ifndef SPLICE_F_MOVE
25 # define SPLICE_F_MOVE		1
26 # define SPLICE_F_NONBLOCK	2
27 # define SPLICE_F_MORE		4
28 # define SPLICE_F_GIFT		8
29 #endif
30 
31 #define POLL_TIMEOUT_MS		1000
32 
33 struct tracecmd_recorder {
34 	struct tracefs_cpu *tcpu;
35 	int		fd;
36 	int		fd1;
37 	int		fd2;
38 	int		page_size;
39 	int		subbuf_size;
40 	int		cpu;
41 	int		stop;
42 	int		max;
43 	int		pages;
44 	int		count;
45 	unsigned	flags;
46 };
47 
append_file(int size,int dst,int src)48 static int append_file(int size, int dst, int src)
49 {
50 	char buf[size];
51 	int r;
52 
53 	lseek(src, 0, SEEK_SET);
54 
55 	/* If there's an error, then we are pretty much screwed :-p */
56 	do {
57 		r = read(src, buf, size);
58 		if (r < 0)
59 			return r;
60 		r = write(dst, buf, r);
61 		if (r < 0)
62 			return r;
63 	} while (r);
64 	return 0;
65 }
66 
tracecmd_free_recorder(struct tracecmd_recorder * recorder)67 void tracecmd_free_recorder(struct tracecmd_recorder *recorder)
68 {
69 	if (!recorder)
70 		return;
71 
72 	if (recorder->max) {
73 		/* Need to put everything into fd1 */
74 		if (recorder->fd == recorder->fd1) {
75 			int ret;
76 			/*
77 			 * Crap, the older data is in fd2, and we need
78 			 * to append fd1 onto it, and then copy over to fd1
79 			 */
80 			ret = append_file(recorder->page_size,
81 					  recorder->fd2, recorder->fd1);
82 			/* Error on copying, then just keep fd1 */
83 			if (ret) {
84 				lseek(recorder->fd1, 0, SEEK_END);
85 				goto close;
86 			}
87 			lseek(recorder->fd1, 0, SEEK_SET);
88 			ftruncate(recorder->fd1, 0);
89 		}
90 		append_file(recorder->page_size, recorder->fd1, recorder->fd2);
91 	}
92  close:
93 	tracefs_cpu_close(recorder->tcpu);
94 
95 	if (recorder->fd1 >= 0)
96 		close(recorder->fd1);
97 
98 	if (recorder->fd2 >= 0)
99 		close(recorder->fd2);
100 
101 	free(recorder);
102 }
103 
set_nonblock(struct tracecmd_recorder * recorder)104 static int set_nonblock(struct tracecmd_recorder *recorder)
105 {
106 	return tracefs_cpu_stop(recorder->tcpu);
107 }
108 
109 static struct tracecmd_recorder *
create_buffer_recorder_fd2(int fd,int fd2,int cpu,unsigned flags,struct tracefs_instance * instance,int maxkb,int tfd)110 create_buffer_recorder_fd2(int fd, int fd2, int cpu, unsigned flags,
111 			   struct tracefs_instance *instance, int maxkb, int tfd)
112 {
113 	struct tracecmd_recorder *recorder;
114 	bool nonblock = false;
115 
116 	recorder = malloc(sizeof(*recorder));
117 	if (!recorder)
118 		return NULL;
119 
120 	recorder->flags = flags;
121 
122 	recorder->page_size = getpagesize();
123 	if (maxkb) {
124 		int kb_per_page = recorder->page_size >> 10;
125 
126 		if (!kb_per_page)
127 			kb_per_page = 1;
128 		recorder->max = maxkb / kb_per_page;
129 		/* keep max half */
130 		recorder->max >>= 1;
131 		if (!recorder->max)
132 			recorder->max = 1;
133 	} else
134 		recorder->max = 0;
135 
136 	recorder->count = 0;
137 	recorder->pages = 0;
138 
139 	/* fd always points to what to write to */
140 	recorder->fd = fd;
141 	recorder->fd1 = fd;
142 	recorder->fd2 = fd2;
143 
144 	if (recorder->flags & TRACECMD_RECORD_POLL)
145 		nonblock = true;
146 
147 	if (tfd >= 0)
148 		recorder->tcpu = tracefs_cpu_alloc_fd(tfd, recorder->page_size, nonblock);
149 	else
150 		recorder->tcpu = tracefs_cpu_open(instance, cpu, nonblock);
151 
152 	if (!recorder->tcpu)
153 		goto out_free;
154 
155 	recorder->subbuf_size = tracefs_cpu_read_size(recorder->tcpu);
156 	return recorder;
157 
158  out_free:
159 	tracecmd_free_recorder(recorder);
160 	return NULL;
161 }
162 
163 struct tracecmd_recorder *
tracecmd_create_buffer_recorder_fd2(int fd,int fd2,int cpu,unsigned flags,struct tracefs_instance * instance,int maxkb)164 tracecmd_create_buffer_recorder_fd2(int fd, int fd2, int cpu, unsigned flags,
165 				    struct tracefs_instance *instance, int maxkb)
166 {
167 	return create_buffer_recorder_fd2(fd, fd2, cpu, flags, instance, maxkb, -1);
168 }
169 
170 struct tracecmd_recorder *
tracecmd_create_buffer_recorder_fd(int fd,int cpu,unsigned flags,struct tracefs_instance * instance)171 tracecmd_create_buffer_recorder_fd(int fd, int cpu, unsigned flags, struct tracefs_instance *instance)
172 {
173 	return tracecmd_create_buffer_recorder_fd2(fd, -1, cpu, flags, instance, 0);
174 }
175 
176 static struct tracecmd_recorder *
__tracecmd_create_buffer_recorder(const char * file,int cpu,unsigned flags,struct tracefs_instance * instance,int tfd,int maxkb)177 __tracecmd_create_buffer_recorder(const char *file, int cpu, unsigned flags,
178 				  struct tracefs_instance *instance, int tfd, int maxkb)
179 {
180 	struct tracecmd_recorder *recorder;
181 	int fd, fd2 = -1;
182 	char *file2;
183 
184 	fd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
185 	if (fd < 0)
186 		return NULL;
187 
188 	if (maxkb) {
189 		int len = strlen(file);
190 
191 		file2 = malloc(len + 3);
192 		if (!file2)
193 			return NULL;
194 
195 		sprintf(file2, "%s.1", file);
196 
197 		fd2 = open(file2, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
198 		if (fd2 < 0) {
199 			close(fd);
200 			unlink(file);
201 			free(file2);
202 			return NULL;
203 		}
204 	}
205 
206 	recorder = create_buffer_recorder_fd2(fd, fd2, cpu, flags, instance, maxkb, tfd);
207 	if (!recorder) {
208 		close(fd);
209 		unlink(file);
210 		if (fd2 != -1)
211 			close(fd2);
212 	}
213 
214 	if (fd2 != -1) {
215 		/* Unlink file2, we need to add everything to file at the end */
216 		unlink(file2);
217 		free(file2);
218 	}
219 
220 	return recorder;
221 }
222 
223 struct tracecmd_recorder *
tracecmd_create_buffer_recorder_maxkb(const char * file,int cpu,unsigned flags,struct tracefs_instance * instance,int maxkb)224 tracecmd_create_buffer_recorder_maxkb(const char *file, int cpu, unsigned flags,
225 				      struct tracefs_instance *instance, int maxkb)
226 {
227 	struct tracecmd_recorder *recorder = NULL;
228 	char *file2;
229 	int len;
230 	int fd;
231 	int fd2;
232 
233 	if (!maxkb)
234 		return tracecmd_create_buffer_recorder(file, cpu, flags, instance);
235 
236 	len = strlen(file);
237 	file2 = malloc(len + 3);
238 	if (!file2)
239 		return NULL;
240 
241 	sprintf(file2, "%s.1", file);
242 
243 	fd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
244 	if (fd < 0)
245 		goto out;
246 
247 	fd2 = open(file2, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
248 	if (fd2 < 0)
249 		goto err;
250 
251 	recorder = tracecmd_create_buffer_recorder_fd2(fd, fd2, cpu, flags, instance, maxkb);
252 	if (!recorder)
253 		goto err2;
254  out:
255 	/* Unlink file2, we need to add everything to file at the end */
256 	unlink(file2);
257 	free(file2);
258 
259 	return recorder;
260  err2:
261 	close(fd2);
262  err:
263 	close(fd);
264 	unlink(file);
265 	goto out;
266 }
267 
268 struct tracecmd_recorder *
tracecmd_create_buffer_recorder(const char * file,int cpu,unsigned flags,struct tracefs_instance * instance)269 tracecmd_create_buffer_recorder(const char *file, int cpu, unsigned flags,
270 				struct tracefs_instance *instance)
271 {
272 	return __tracecmd_create_buffer_recorder(file, cpu, flags, instance, -1, 0);
273 }
274 
275 /**
276  * tracecmd_create_recorder_virt - Create a recorder reading tracing data
277  * from the trace_fd file descriptor instead of from the local tracefs
278  * @file: output filename where tracing data will be written
279  * @cpu: which CPU is being traced
280  * @flags: flags configuring the recorder (see TRACECMD_RECORDER_* enums)
281  * @trace_fd: file descriptor from where tracing data will be read
282  */
283 struct tracecmd_recorder *
tracecmd_create_recorder_virt(const char * file,int cpu,unsigned flags,int trace_fd,int maxkb)284 tracecmd_create_recorder_virt(const char *file, int cpu, unsigned flags,
285 			      int trace_fd, int maxkb)
286 {
287 	return __tracecmd_create_buffer_recorder(file, cpu, flags, NULL, trace_fd, maxkb);
288 }
289 
tracecmd_create_recorder_fd(int fd,int cpu,unsigned flags)290 struct tracecmd_recorder *tracecmd_create_recorder_fd(int fd, int cpu, unsigned flags)
291 {
292 	return tracecmd_create_buffer_recorder_fd(fd, cpu, flags, NULL);
293 }
294 
tracecmd_create_recorder(const char * file,int cpu,unsigned flags)295 struct tracecmd_recorder *tracecmd_create_recorder(const char *file, int cpu, unsigned flags)
296 {
297 	return tracecmd_create_buffer_recorder(file, cpu, flags, NULL);
298 }
299 
300 struct tracecmd_recorder *
tracecmd_create_recorder_maxkb(const char * file,int cpu,unsigned flags,int maxkb)301 tracecmd_create_recorder_maxkb(const char *file, int cpu, unsigned flags, int maxkb)
302 {
303 	return tracecmd_create_buffer_recorder_maxkb(file, cpu, flags, NULL, maxkb);
304 }
305 
update_fd(struct tracecmd_recorder * recorder,int size)306 static inline void update_fd(struct tracecmd_recorder *recorder, int size)
307 {
308 	int fd;
309 
310 	if (!recorder->max)
311 		return;
312 
313 	recorder->count += size;
314 
315 	if (recorder->count >= recorder->page_size) {
316 		recorder->pages += recorder->count / recorder->page_size;
317 		recorder->count = 0;
318 	}
319 
320 	if (recorder->pages < recorder->max)
321 		return;
322 
323 	recorder->pages = 0;
324 
325 	fd = recorder->fd;
326 
327 	/* Swap fd to next file. */
328 	if (fd == recorder->fd1)
329 		fd = recorder->fd2;
330 	else
331 		fd = recorder->fd1;
332 
333 	/* Zero out the new file we are writing to */
334 	lseek(fd, 0, SEEK_SET);
335 	ftruncate(fd, 0);
336 
337 	recorder->fd = fd;
338 }
339 
340 /*
341  * Returns -1 on error.
342  *          or bytes of data read.
343  */
read_data(struct tracecmd_recorder * recorder)344 static long read_data(struct tracecmd_recorder *recorder)
345 {
346 	bool nonblock = recorder->stop;
347 	char buf[recorder->subbuf_size];
348 	long left;
349 	long r, w;
350 
351 	r = tracefs_cpu_read(recorder->tcpu, buf, nonblock);
352 	if (r < 0)
353 		return r;
354 
355 	left = r;
356 	do {
357 		w = write(recorder->fd, buf + (r - left), left);
358 		if (w > 0) {
359 			left -= w;
360 			update_fd(recorder, w);
361 		}
362 	} while (w >= 0 && left);
363 
364 	if (w < 0)
365 		r = w;
366 
367 	return r;
368 }
369 
370 /*
371  * Returns -1 on error.
372  *          or bytes of data read.
373  */
direct_splice_data(struct tracecmd_recorder * recorder)374 static long direct_splice_data(struct tracecmd_recorder *recorder)
375 {
376 	bool nonblock = recorder->stop;
377 	return tracefs_cpu_pipe(recorder->tcpu, recorder->fd, nonblock);
378 }
379 
move_data(struct tracecmd_recorder * recorder)380 static long move_data(struct tracecmd_recorder *recorder)
381 {
382 	bool nonblock = recorder->stop;
383 	long ret;
384 
385 	if (recorder->flags & TRACECMD_RECORD_NOSPLICE)
386 		return read_data(recorder);
387 
388 	if (recorder->flags & TRACECMD_RECORD_NOBRASS)
389 		return direct_splice_data(recorder);
390 
391 	ret = tracefs_cpu_write(recorder->tcpu, recorder->fd, nonblock);
392 	if (ret > 0)
393 		update_fd(recorder, ret);
394 	return ret;
395 }
396 
tracecmd_flush_recording(struct tracecmd_recorder * recorder,bool finish)397 long tracecmd_flush_recording(struct tracecmd_recorder *recorder, bool finish)
398 {
399 	char buf[recorder->subbuf_size];
400 	long total = 0;
401 	long wrote = 0;
402 	long ret;
403 
404 	if (!recorder)
405 		return 0;
406 
407 	if (!finish)
408 		return tracefs_cpu_flush_write(recorder->tcpu, recorder->fd);
409 
410 	set_nonblock(recorder);
411 
412 	do {
413 		ret = tracefs_cpu_flush_write(recorder->tcpu, recorder->fd);
414 		if (ret > 0)
415 			wrote += ret;
416 	} while (ret > 0);
417 
418 	/* Make sure we finish off with a page size boundary */
419 	wrote &= recorder->subbuf_size - 1;
420 	if (wrote) {
421 		memset(buf, 0, recorder->subbuf_size);
422 		write(recorder->fd, buf, recorder->subbuf_size - wrote);
423 		total += recorder->subbuf_size;
424 	}
425 
426 	return total;
427 }
428 
tracecmd_start_recording(struct tracecmd_recorder * recorder,unsigned long sleep)429 int tracecmd_start_recording(struct tracecmd_recorder *recorder, unsigned long sleep)
430 {
431 	struct timespec req = {
432 		.tv_sec = sleep / 1000000,
433 		.tv_nsec = (sleep % 1000000) * 1000,
434 	};
435 	long read = 1;
436 	long ret;
437 
438 	recorder->stop = 0;
439 
440 	do {
441 		/* Only sleep if we did not read anything last time */
442 		if (!read && sleep)
443 			nanosleep(&req, NULL);
444 
445 		read = 0;
446 		do {
447 			ret = move_data(recorder);
448 			if (ret < 0) {
449 				if (errno == EINTR)
450 					continue;
451 				if ((recorder->flags & TRACECMD_RECORD_POLL) &&
452 				    errno == EAGAIN)
453 					continue;
454 				return ret;
455 			}
456 			read += ret;
457 		} while (ret > 0 && !recorder->stop);
458 	} while (!recorder->stop);
459 
460 	/* Flush out the rest */
461 	ret = tracecmd_flush_recording(recorder, true);
462 
463 	if (ret < 0)
464 		return ret;
465 
466 	return 0;
467 }
468 
tracecmd_stop_recording(struct tracecmd_recorder * recorder)469 int tracecmd_stop_recording(struct tracecmd_recorder *recorder)
470 {
471 	if (!recorder)
472 		return -1;
473 
474 	recorder->stop = 1;
475 
476 	return set_nonblock(recorder);
477 }
478