• 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 #define _LARGEFILE64_SOURCE
7 #include <dirent.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <getopt.h>
12 #include <stdarg.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <sys/wait.h>
16 #include <sys/mman.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <ctype.h>
20 #include <errno.h>
21 #include <glob.h>
22 
23 #include "tracefs.h"
24 #include "trace-cmd.h"
25 #include "trace-cmd-local.h"
26 #include "trace-write-local.h"
27 #include "list.h"
28 #include "trace-msg.h"
29 
30 /* We can't depend on the host size for size_t, all must be 64 bit */
31 typedef unsigned long long	tsize_t;
32 typedef long long		stsize_t;
33 
34 struct tracecmd_option {
35 	unsigned short	id;
36 	int		size;
37 	void		*data;
38 	tsize_t		offset;
39 	struct list_head list;
40 };
41 
42 struct tracecmd_buffer {
43 	int				cpus;
44 	void				*name;
45 	tsize_t				offset;
46 	struct tracecmd_option		*option;
47 	struct list_head		list;
48 };
49 
50 enum {
51 	OUTPUT_FL_SEND_META	= (1 << 0),
52 };
53 
54 struct tracecmd_output {
55 	int			fd;
56 	int			page_size;
57 	int			cpus;
58 	struct tep_handle	*pevent;
59 	char			*tracing_dir;
60 	char			*kallsyms;
61 	int			nr_options;
62 	bool			quiet;
63 	unsigned long		file_state;
64 	unsigned long		file_version;
65 
66 	/* size of meta-data strings, not yet stored in the file */
67 	unsigned long		strings_p;
68 	/* current virtual offset of meta-data string */
69 	unsigned long		strings_offs;
70 
71 	unsigned long long	options_start;
72 	bool			big_endian;
73 	bool			do_compress;
74 	struct tracecmd_compression *compress;
75 
76 	struct list_head	options;
77 	struct list_head	buffers;
78 	struct tracecmd_msg_handle *msg_handle;
79 	char			*trace_clock;
80 
81 	/* meta-data strings, not yet stored in the file */
82 	char			*strings;
83 };
84 
85 struct list_event {
86 	struct list_event		*next;
87 	char				*name;
88 	char				*file;
89 };
90 
91 struct list_event_system {
92 	struct list_event_system	*next;
93 	struct list_event		*events;
94 	char				*name;
95 };
96 
97 #define HAS_SECTIONS(H) ((H)->file_version >= FILE_VERSION_SECTIONS)
98 
99 static int write_options(struct tracecmd_output *handle);
100 static int save_string_section(struct tracecmd_output *handle, bool compress);
101 
102 __hidden long long
do_write_check(struct tracecmd_output * handle,const void * data,long long size)103 do_write_check(struct tracecmd_output *handle, const void *data, long long size)
104 {
105 	if (handle->do_compress)
106 		return tracecmd_compress_buffer_write(handle->compress, data, size);
107 
108 	if (handle->msg_handle)
109 		return tracecmd_msg_data_send(handle->msg_handle, data, size);
110 
111 	return __do_write_check(handle->fd, data, size);
112 }
113 
do_lseek(struct tracecmd_output * handle,off_t offset,int whence)114 static inline off64_t do_lseek(struct tracecmd_output *handle, off_t offset, int whence)
115 {
116 	if (handle->do_compress)
117 		return tracecmd_compress_lseek(handle->compress, offset, whence);
118 
119 	if (handle->msg_handle)
120 		return msg_lseek(handle->msg_handle, offset, whence);
121 
122 	return lseek64(handle->fd, offset, whence);
123 }
124 
do_preed(struct tracecmd_output * handle,void * dst,int len,off_t offset)125 static inline int do_preed(struct tracecmd_output *handle, void *dst, int len, off_t offset)
126 {
127 	if (handle->do_compress)
128 		return tracecmd_compress_pread(handle->compress, dst, len, offset);
129 
130 	return pread(handle->fd, dst, len, offset);
131 }
132 
convert_endian_2(struct tracecmd_output * handle,short val)133 static short convert_endian_2(struct tracecmd_output *handle, short val)
134 {
135 	if (!handle->pevent)
136 		return val;
137 
138 	return tep_read_number(handle->pevent, &val, 2);
139 }
140 
convert_endian_4(struct tracecmd_output * handle,int val)141 static int convert_endian_4(struct tracecmd_output *handle, int val)
142 {
143 	if (!handle->pevent)
144 		return val;
145 
146 	return tep_read_number(handle->pevent, &val, 4);
147 }
148 
convert_endian_8(struct tracecmd_output * handle,unsigned long long val)149 static unsigned long long convert_endian_8(struct tracecmd_output *handle,
150 					   unsigned long long val)
151 {
152 	if (!handle->pevent)
153 		return val;
154 
155 	return tep_read_number(handle->pevent, &val, 8);
156 }
157 
out_compression_reset(struct tracecmd_output * handle,bool compress)158 __hidden void out_compression_reset(struct tracecmd_output *handle, bool compress)
159 {
160 	if (!compress || !handle->compress)
161 		return;
162 
163 	tracecmd_compress_reset(handle->compress);
164 	handle->do_compress = false;
165 }
166 
out_uncompress_block(struct tracecmd_output * handle)167 __hidden int out_uncompress_block(struct tracecmd_output *handle)
168 {
169 	int ret = 0;
170 
171 	if (!handle->compress)
172 		return 0;
173 
174 	ret = tracecmd_uncompress_block(handle->compress);
175 	if (!ret)
176 		handle->do_compress = true;
177 
178 	return ret;
179 }
180 
out_compression_start(struct tracecmd_output * handle,bool compress)181 __hidden int out_compression_start(struct tracecmd_output *handle, bool compress)
182 {
183 	if (!compress || !handle->compress)
184 		return 0;
185 
186 	tracecmd_compress_reset(handle->compress);
187 	handle->do_compress = true;
188 
189 	return 0;
190 }
191 
out_compression_end(struct tracecmd_output * handle,bool compress)192 __hidden int out_compression_end(struct tracecmd_output *handle, bool compress)
193 {
194 	if (!compress || !handle->compress)
195 		return 0;
196 
197 	handle->do_compress = false;
198 	return tracecmd_compress_block(handle->compress);
199 }
200 
add_string(struct tracecmd_output * handle,const char * string)201 static long add_string(struct tracecmd_output *handle, const char *string)
202 {
203 	int size = strlen(string) + 1;
204 	int pos = handle->strings_p;
205 	char *strings;
206 
207 	strings = realloc(handle->strings, pos + size);
208 	if (!strings)
209 		return -1;
210 	handle->strings = strings;
211 	memcpy(handle->strings + pos, string, size);
212 	handle->strings_p += size;
213 
214 	return handle->strings_offs + pos;
215 }
216 
217 /**
218  * tracecmd_set_quiet - Set if to print output to the screen
219  * @quiet: If non zero, print no output to the screen
220  *
221  */
tracecmd_set_quiet(struct tracecmd_output * handle,bool set_quiet)222 void tracecmd_set_quiet(struct tracecmd_output *handle, bool set_quiet)
223 {
224 	if (handle)
225 		handle->quiet = set_quiet;
226 }
227 
tracecmd_set_out_clock(struct tracecmd_output * handle,const char * clock)228 void tracecmd_set_out_clock(struct tracecmd_output *handle, const char *clock)
229 {
230 	if (handle && clock) {
231 		free(handle->trace_clock);
232 		handle->trace_clock = strdup(clock);
233 	}
234 }
235 
236 /**
237  * tracecmd_get_quiet - Get if to print output to the screen
238  * Returns non zero, if no output to the screen should be printed
239  *
240  */
tracecmd_get_quiet(struct tracecmd_output * handle)241 bool tracecmd_get_quiet(struct tracecmd_output *handle)
242 {
243 	if (handle)
244 		return handle->quiet;
245 	return false;
246 }
247 
tracecmd_output_free(struct tracecmd_output * handle)248 void tracecmd_output_free(struct tracecmd_output *handle)
249 {
250 	struct tracecmd_option *option;
251 	struct tracecmd_buffer *buffer;
252 
253 	if (!handle)
254 		return;
255 
256 	if (handle->tracing_dir)
257 		free(handle->tracing_dir);
258 
259 	if (handle->pevent)
260 		tep_unref(handle->pevent);
261 
262 	while (!list_empty(&handle->buffers)) {
263 		buffer = container_of(handle->buffers.next,
264 				      struct tracecmd_buffer, list);
265 		list_del(&buffer->list);
266 		free(buffer->name);
267 		free(buffer);
268 	}
269 	while (!list_empty(&handle->options)) {
270 		option = container_of(handle->options.next,
271 				      struct tracecmd_option, list);
272 		list_del(&option->list);
273 		free(option->data);
274 		free(option);
275 	}
276 
277 	free(handle->strings);
278 	free(handle->trace_clock);
279 	tracecmd_compress_destroy(handle->compress);
280 	free(handle);
281 }
282 
tracecmd_output_close(struct tracecmd_output * handle)283 void tracecmd_output_close(struct tracecmd_output *handle)
284 {
285 	if (!handle)
286 		return;
287 
288 	if (HAS_SECTIONS(handle)) {
289 		/* write any unsaved options at the end of trace files with sections */
290 		write_options(handle);
291 
292 		/* write strings section */
293 		save_string_section(handle, true);
294 	}
295 
296 	if (handle->fd >= 0) {
297 		close(handle->fd);
298 		handle->fd = -1;
299 	}
300 
301 	tracecmd_output_free(handle);
302 }
get_size_fd(int fd)303 static unsigned long get_size_fd(int fd)
304 {
305 	unsigned long long size = 0;
306 	char buf[BUFSIZ];
307 	int r;
308 
309 	do {
310 		r = read(fd, buf, BUFSIZ);
311 		if (r > 0)
312 			size += r;
313 	} while (r > 0);
314 
315 	lseek(fd, 0, SEEK_SET);
316 
317 	return size;
318 }
319 
get_size(const char * file)320 static unsigned long get_size(const char *file)
321 {
322 	unsigned long long size = 0;
323 	int fd;
324 
325 	fd = open(file, O_RDONLY);
326 	if (fd < 0) {
327 		tracecmd_warning("Can't read '%s'", file);
328 		return 0; /* Caller will fail with zero */
329 	}
330 	size = get_size_fd(fd);
331 	close(fd);
332 
333 	return size;
334 }
335 
copy_file_fd(struct tracecmd_output * handle,int fd,unsigned long long max)336 static tsize_t copy_file_fd(struct tracecmd_output *handle, int fd, unsigned long long max)
337 {
338 	tsize_t rsize = BUFSIZ;
339 	tsize_t size = 0;
340 	char buf[BUFSIZ];
341 	stsize_t r;
342 
343 	do {
344 		if (max && rsize > max)
345 			rsize = max;
346 
347 		r = read(fd, buf, rsize);
348 		if (r > 0) {
349 			size += r;
350 			if (do_write_check(handle, buf, r))
351 				return 0;
352 			if (max) {
353 				max -= r;
354 				if (!max)
355 					break;
356 			}
357 		}
358 	} while (r > 0);
359 
360 	return size;
361 }
362 
copy_file(struct tracecmd_output * handle,const char * file)363 static tsize_t copy_file(struct tracecmd_output *handle,
364 				    const char *file)
365 {
366 	tsize_t size = 0;
367 	int fd;
368 
369 	fd = open(file, O_RDONLY);
370 	if (fd < 0) {
371 		tracecmd_warning("Can't read '%s'", file);
372 		return 0;
373 	}
374 	size = copy_file_fd(handle, fd, 0);
375 	close(fd);
376 
377 	return size;
378 }
379 
380 #define PAGES_IN_CHUNK 10
out_copy_fd_compress(struct tracecmd_output * handle,int fd,unsigned long long max,unsigned long long * write_size,int page)381 __hidden unsigned long long out_copy_fd_compress(struct tracecmd_output *handle,
382 						 int fd, unsigned long long max,
383 						 unsigned long long *write_size,
384 						 int page)
385 {
386 	unsigned long long rsize = 0;
387 	unsigned long long wsize = 0;
388 	unsigned long long size;
389 	int ret;
390 
391 	if (handle->compress) {
392 		rsize = max;
393 		ret = tracecmd_compress_copy_from(handle->compress, fd,
394 						  PAGES_IN_CHUNK * page,
395 						  &rsize, &wsize);
396 		if (ret < 0)
397 			return 0;
398 
399 		size = rsize;
400 		if (write_size)
401 			*write_size = wsize;
402 	} else {
403 		size = copy_file_fd(handle, fd, max);
404 		if (write_size)
405 			*write_size = size;
406 	}
407 
408 	return size;
409 }
410 
copy_file_compress(struct tracecmd_output * handle,const char * file,unsigned long long * write_size)411 static tsize_t copy_file_compress(struct tracecmd_output *handle,
412 				  const char *file, unsigned long long *write_size)
413 {
414 	int ret;
415 	int fd;
416 
417 	fd = open(file, O_RDONLY);
418 	if (fd < 0) {
419 		tracecmd_warning("Can't read '%s'", file);
420 		return 0;
421 	}
422 
423 	ret = out_copy_fd_compress(handle, fd, 0, write_size, getpagesize());
424 	if (!ret)
425 		tracecmd_warning("Can't compress '%s'", file);
426 
427 	close(fd);
428 	return ret;
429 }
430 
431 /*
432  * Finds the path to the debugfs/tracing
433  * Allocates the string and stores it.
434  */
find_tracing_dir(struct tracecmd_output * handle)435 static const char *find_tracing_dir(struct tracecmd_output *handle)
436 {
437 	if (!handle->tracing_dir) {
438 		const char *dir = tracefs_tracing_dir();
439 
440 		if (dir)
441 			handle->tracing_dir = strdup(dir);
442 	}
443 	return handle->tracing_dir;
444 }
445 
get_tracing_file(struct tracecmd_output * handle,const char * name)446 static char *get_tracing_file(struct tracecmd_output *handle, const char *name)
447 {
448 	const char *tracing;
449 	char *file;
450 	int ret;
451 
452 	tracing = find_tracing_dir(handle);
453 	if (!tracing)
454 		return NULL;
455 
456 	ret = asprintf(&file, "%s/%s", tracing, name);
457 	if (ret < 0)
458 		return NULL;
459 
460 	return file;
461 }
462 
put_tracing_file(char * file)463 static void put_tracing_file(char *file)
464 {
465 	free(file);
466 }
467 
tracecmd_ftrace_enable(int set)468 int tracecmd_ftrace_enable(int set)
469 {
470 	struct stat buf;
471 	char *path = "/proc/sys/kernel/ftrace_enabled";
472 	int fd;
473 	char *val = set ? "1" : "0";
474 	int ret = 0;
475 
476 	/* if ftace_enable does not exist, simply ignore it */
477 	fd = stat(path, &buf);
478 	if (fd < 0)
479 		return ENODEV;
480 
481 	fd = open(path, O_WRONLY);
482 	if (fd < 0) {
483 		tracecmd_warning("Can't %s ftrace", set ? "enable" : "disable");
484 		return EIO;
485 	}
486 
487 	if (write(fd, val, 1) < 0)
488 		ret = -1;
489 	close(fd);
490 
491 	return ret;
492 }
493 
494 __hidden unsigned long long
out_write_section_header(struct tracecmd_output * handle,unsigned short header_id,char * description,int flags,bool option)495 out_write_section_header(struct tracecmd_output *handle, unsigned short header_id,
496 			 char *description, int flags, bool option)
497 {
498 	tsize_t endian8;
499 	tsize_t offset;
500 	long long size;
501 	short endian2;
502 	int endian4;
503 	int desc;
504 
505 	if (header_id >= TRACECMD_OPTION_MAX)
506 		return -1;
507 	if (!HAS_SECTIONS(handle))
508 		return 0;
509 	if (!handle->compress)
510 		flags &= ~TRACECMD_SEC_FL_COMPRESS;
511 	offset = do_lseek(handle, 0, SEEK_CUR);
512 	if (option) {
513 		endian8 = convert_endian_8(handle, offset);
514 		if (!tracecmd_add_option(handle, header_id, 8, &endian8))
515 			return -1;
516 	}
517 	/* Section ID */
518 	endian2 = convert_endian_2(handle, header_id);
519 	if (do_write_check(handle, &endian2, 2))
520 		return (off64_t)-1;
521 
522 	/* Section flags */
523 	endian2 = convert_endian_2(handle, flags);
524 	if (do_write_check(handle, &endian2, 2))
525 		return (off64_t)-1;
526 
527 	/* Section description */
528 	if (description)
529 		desc = add_string(handle, description);
530 	else
531 		desc = -1;
532 	endian4 = convert_endian_4(handle, desc);
533 	if (do_write_check(handle, &endian4, 4))
534 		return (off64_t)-1;
535 
536 	offset = do_lseek(handle, 0, SEEK_CUR);
537 	size = 0;
538 	/* Reserve for section size */
539 	if (do_write_check(handle, &size, 8))
540 		return (off64_t)-1;
541 	return offset;
542 }
543 
out_update_section_header(struct tracecmd_output * handle,tsize_t offset)544 __hidden int out_update_section_header(struct tracecmd_output *handle, tsize_t offset)
545 {
546 	tsize_t current;
547 	tsize_t endian8;
548 	tsize_t size;
549 
550 	if (!HAS_SECTIONS(handle) || offset == 0)
551 		return 0;
552 
553 	current = do_lseek(handle, 0, SEEK_CUR);
554 	/* The real size is the difference between the saved offset and
555 	 * the current offset - 8 bytes, the reserved space for the section size.
556 	 */
557 	size = current - offset;
558 	if (size < 8)
559 		return -1;
560 	size -= 8;
561 	if (do_lseek(handle, offset, SEEK_SET) == (off64_t)-1)
562 		return -1;
563 
564 	endian8 = convert_endian_8(handle, size);
565 	if (do_write_check(handle, &endian8, 8))
566 		return -1;
567 	if (do_lseek(handle, current, SEEK_SET) == (off64_t)-1)
568 		return -1;
569 	return 0;
570 }
571 
save_string_section(struct tracecmd_output * handle,bool compress)572 static int save_string_section(struct tracecmd_output *handle, bool compress)
573 {
574 	enum tracecmd_section_flags flags = 0;
575 	tsize_t offset;
576 
577 	if (!handle->strings || !handle->strings_p)
578 		return 0;
579 
580 	if (!check_out_state(handle, TRACECMD_OPTION_STRINGS)) {
581 		tracecmd_warning("Cannot write strings, unexpected state 0x%X",
582 				 handle->file_state);
583 		return -1;
584 	}
585 
586 	if (compress)
587 		flags |= TRACECMD_SEC_FL_COMPRESS;
588 	offset = out_write_section_header(handle, TRACECMD_OPTION_STRINGS, "strings", flags, false);
589 	if (offset == (off64_t)-1)
590 		return -1;
591 
592 	out_compression_start(handle, compress);
593 
594 	if (do_write_check(handle, handle->strings, handle->strings_p))
595 		goto error;
596 
597 	if (out_compression_end(handle, compress))
598 		goto error;
599 
600 	if (out_update_section_header(handle, offset))
601 		return -1;
602 
603 	handle->strings_offs += handle->strings_p;
604 	free(handle->strings);
605 	handle->strings = NULL;
606 	handle->strings_p = 0;
607 	handle->file_state = TRACECMD_OPTION_STRINGS;
608 	return 0;
609 
610 error:
611 	out_compression_reset(handle, compress);
612 	return -1;
613 }
614 
read_header_files(struct tracecmd_output * handle,bool compress)615 static int read_header_files(struct tracecmd_output *handle, bool compress)
616 {
617 	enum tracecmd_section_flags flags = 0;
618 	tsize_t size, check_size, endian8;
619 	struct stat st;
620 	tsize_t offset;
621 	char *path;
622 	int fd = -1;
623 	int ret;
624 
625 	if (!check_out_state(handle, TRACECMD_FILE_HEADERS)) {
626 		tracecmd_warning("Cannot read header files, unexpected state 0x%X",
627 				 handle->file_state);
628 		return -1;
629 	}
630 
631 	path = get_tracing_file(handle, "events/header_page");
632 	if (!path)
633 		return -1;
634 
635 	if (compress)
636 		flags |= TRACECMD_SEC_FL_COMPRESS;
637 	offset = out_write_section_header(handle, TRACECMD_OPTION_HEADER_INFO,
638 					  "headers", flags, true);
639 	if (offset == (off64_t)-1)
640 		return -1;
641 
642 	out_compression_start(handle, compress);
643 	ret = stat(path, &st);
644 	if (ret < 0) {
645 		/* old style did not show this info, just add zero */
646 		put_tracing_file(path);
647 		if (do_write_check(handle, "header_page", 12))
648 			goto out_close;
649 		size = 0;
650 		if (do_write_check(handle, &size, 8))
651 			goto out_close;
652 		if (do_write_check(handle, "header_event", 13))
653 			goto out_close;
654 		if (do_write_check(handle, &size, 8))
655 			goto out_close;
656 		if (out_compression_end(handle, compress))
657 			goto out_close;
658 		if (out_update_section_header(handle, offset))
659 			goto out_close;
660 		return 0;
661 	}
662 
663 	fd = open(path, O_RDONLY);
664 	if (fd < 0) {
665 		tracecmd_warning("can't read '%s'", path);
666 		goto out_close;
667 	}
668 
669 	/* unfortunately, you can not stat debugfs files for size */
670 	size = get_size_fd(fd);
671 
672 	if (do_write_check(handle, "header_page", 12))
673 		goto out_close;
674 	endian8 = convert_endian_8(handle, size);
675 	if (do_write_check(handle, &endian8, 8))
676 		goto out_close;
677 	check_size = copy_file_fd(handle, fd, 0);
678 	close(fd);
679 	if (size != check_size) {
680 		tracecmd_warning("wrong size for '%s' size=%lld read=%lld", path, size, check_size);
681 		errno = EINVAL;
682 		goto out_close;
683 	}
684 	put_tracing_file(path);
685 
686 	path = get_tracing_file(handle, "events/header_event");
687 	if (!path)
688 		goto out_close;
689 
690 	fd = open(path, O_RDONLY);
691 	if (fd < 0) {
692 		tracecmd_warning("can't read '%s'", path);
693 		goto out_close;
694 	}
695 
696 	size = get_size_fd(fd);
697 
698 	if (do_write_check(handle, "header_event", 13))
699 		goto out_close;
700 	endian8 = convert_endian_8(handle, size);
701 	if (do_write_check(handle, &endian8, 8))
702 		goto out_close;
703 	check_size = copy_file_fd(handle, fd, 0);
704 	close(fd);
705 	if (size != check_size) {
706 		tracecmd_warning("wrong size for '%s'", path);
707 		goto out_close;
708 	}
709 	put_tracing_file(path);
710 	if (out_compression_end(handle, compress))
711 		goto out_close;
712 
713 	if (out_update_section_header(handle, offset))
714 		goto out_close;
715 	handle->file_state = TRACECMD_FILE_HEADERS;
716 
717 	return 0;
718 
719  out_close:
720 	out_compression_reset(handle, compress);
721 	if (fd >= 0)
722 		close(fd);
723 	return -1;
724 }
725 
copy_event_system(struct tracecmd_output * handle,struct list_event_system * slist)726 static int copy_event_system(struct tracecmd_output *handle,
727 			     struct list_event_system *slist)
728 {
729 	struct list_event *elist;
730 	unsigned long long size, check_size, endian8;
731 	struct stat st;
732 	char *format;
733 	int endian4;
734 	int count = 0;
735 	int ret;
736 
737 	for (elist = slist->events; elist; elist = elist->next)
738 		count++;
739 
740 	endian4 = convert_endian_4(handle, count);
741 	if (do_write_check(handle, &endian4, 4))
742 		return -1;
743 
744 	for (elist = slist->events; elist; elist = elist->next) {
745 		format = elist->file;
746 		ret = stat(format, &st);
747 
748 		if (ret >= 0) {
749 			/* unfortunately, you can not stat debugfs files for size */
750 			size = get_size(format);
751 			endian8 = convert_endian_8(handle, size);
752 			if (do_write_check(handle, &endian8, 8))
753 				return -1;
754 			check_size = copy_file(handle, format);
755 			if (size != check_size) {
756 				tracecmd_warning("error in size of file '%s'", format);
757 				return -1;
758 			}
759 		}
760 	}
761 
762 	return 0;
763 }
764 
add_list_event_system(struct list_event_system ** systems,const char * system,const char * event,const char * path)765 static void add_list_event_system(struct list_event_system **systems,
766 				  const char *system,
767 				  const char *event,
768 				  const char *path)
769 {
770 	struct list_event_system *slist;
771 	struct list_event *elist;
772 
773 	for (slist = *systems; slist; slist = slist->next)
774 		if (strcmp(slist->name, system) == 0)
775 			break;
776 
777 	if (!slist) {
778 		slist = malloc(sizeof(*slist));
779 		if (!slist)
780 			goto err_mem;
781 		slist->name = strdup(system);
782 		if (!slist->name) {
783 			free(slist);
784 			goto err_mem;
785 		}
786 		slist->next = *systems;
787 		slist->events = NULL;
788 		*systems = slist;
789 	}
790 
791 	for (elist = slist->events; elist; elist = elist->next)
792 		if (strcmp(elist->name, event) == 0)
793 			break;
794 
795 	if (!elist) {
796 		elist = malloc(sizeof(*elist));
797 		if (!elist)
798 			goto err_mem;
799 		elist->name = strdup(event);
800 		elist->file = strdup(path);
801 		if (!elist->name || !elist->file) {
802 			free(elist->name);
803 			free(elist->file);
804 			free(elist);
805 			goto err_mem;
806 		}
807 		elist->next = slist->events;
808 		slist->events = elist;
809 	}
810 	return;
811  err_mem:
812 	 tracecmd_warning("Insufficient memory");
813 }
814 
free_list_events(struct list_event_system * list)815 static void free_list_events(struct list_event_system *list)
816 {
817 	struct list_event_system *slist;
818 	struct list_event *elist;
819 
820 	while (list) {
821 		slist = list;
822 		list = list->next;
823 		while (slist->events) {
824 			elist = slist->events;
825 			slist->events = elist->next;
826 			free(elist->name);
827 			free(elist->file);
828 			free(elist);
829 		}
830 		free(slist->name);
831 		free(slist);
832 	}
833 }
834 
glob_events(struct tracecmd_output * handle,struct list_event_system ** systems,const char * str)835 static void glob_events(struct tracecmd_output *handle,
836 			struct list_event_system **systems,
837 			const char *str)
838 {
839 	glob_t globbuf;
840 	char *events_path;
841 	char *system;
842 	char *event;
843 	char *path;
844 	char *file;
845 	char *ptr;
846 	int do_ftrace = 0;
847 	int events_len;
848 	int ret;
849 	int i;
850 
851 	if (strncmp(str, "ftrace/", 7) == 0)
852 		do_ftrace = 1;
853 
854 	events_path = get_tracing_file(handle, "events");
855 	events_len = strlen(events_path);
856 
857 	path = malloc(events_len + strlen(str) +
858 		      strlen("/format") + 2);
859 	if (!path)
860 		return;
861 	path[0] = '\0';
862 	strcat(path, events_path);
863 	strcat(path, "/");
864 	strcat(path, str);
865 	strcat(path, "/format");
866 	put_tracing_file(events_path);
867 
868 	globbuf.gl_offs = 0;
869 	ret = glob(path, 0, NULL, &globbuf);
870 	free(path);
871 	if (ret < 0)
872 		return;
873 
874 	for (i = 0; i < globbuf.gl_pathc; i++) {
875 		file = globbuf.gl_pathv[i];
876 		system = strdup(file + events_len + 1);
877 		system = strtok_r(system, "/", &ptr);
878 		if (!ptr) {
879 			/* ?? should we warn? */
880 			free(system);
881 			continue;
882 		}
883 
884 		if (!do_ftrace && strcmp(system, "ftrace") == 0) {
885 			free(system);
886 			continue;
887 		}
888 
889 		event = strtok_r(NULL, "/", &ptr);
890 		if (!ptr) {
891 			/* ?? should we warn? */
892 			free(system);
893 			continue;
894 		}
895 
896 		add_list_event_system(systems, system, event, file);
897 		free(system);
898 	}
899 	globfree(&globbuf);
900 }
901 
902 static void
create_event_list_item(struct tracecmd_output * handle,struct list_event_system ** systems,struct tracecmd_event_list * list)903 create_event_list_item(struct tracecmd_output *handle,
904 		       struct list_event_system **systems,
905 		       struct tracecmd_event_list *list)
906 {
907 	char *ptr;
908 	char *str;
909 
910 	str = strdup(list->glob);
911 	if (!str)
912 		goto err_mem;
913 
914 	/* system and event names are separated by a ':' */
915 	ptr = strchr(str, ':');
916 	if (ptr)
917 		*ptr = '/';
918 	else
919 		/* system and event may also be separated by a '/' */
920 		ptr = strchr(str, '/');
921 
922 	if (ptr) {
923 		glob_events(handle, systems, str);
924 		free(str);
925 		return;
926 	}
927 
928 	ptr = str;
929 	str = malloc(strlen(ptr) + 3);
930 	if (!str)
931 		goto err_mem;
932 	str[0] = '\0';
933 	strcat(str, ptr);
934 	strcat(str, "/*");
935 	glob_events(handle, systems, str);
936 
937 	str[0] = '\0';
938 	strcat(str, "*/");
939 	strcat(str, ptr);
940 	glob_events(handle, systems, str);
941 
942 	free(ptr);
943 	free(str);
944 	return;
945  err_mem:
946 	 tracecmd_warning("Insufficient memory");
947 }
948 
read_ftrace_files(struct tracecmd_output * handle,bool compress)949 static int read_ftrace_files(struct tracecmd_output *handle, bool compress)
950 {
951 	enum tracecmd_section_flags flags = 0;
952 	struct list_event_system *systems = NULL;
953 	struct tracecmd_event_list list = { .glob = "ftrace/*" };
954 	tsize_t offset;
955 	int ret;
956 
957 	if (!check_out_state(handle, TRACECMD_FILE_FTRACE_EVENTS)) {
958 		tracecmd_warning("Cannot read ftrace files, unexpected state 0x%X",
959 				 handle->file_state);
960 		return -1;
961 	}
962 
963 	if (compress)
964 		flags |= TRACECMD_SEC_FL_COMPRESS;
965 	offset = out_write_section_header(handle, TRACECMD_OPTION_FTRACE_EVENTS,
966 					  "ftrace events", flags, true);
967 	if (offset == (off64_t)-1)
968 		return -1;
969 
970 	create_event_list_item(handle, &systems, &list);
971 	out_compression_start(handle, compress);
972 
973 	ret = copy_event_system(handle, systems);
974 	if (!ret)
975 		ret = out_compression_end(handle, compress);
976 	else
977 		out_compression_reset(handle, compress);
978 
979 	free_list_events(systems);
980 	if (ret)
981 		return ret;
982 	if (out_update_section_header(handle, offset))
983 		return -1;
984 
985 	handle->file_state = TRACECMD_FILE_FTRACE_EVENTS;
986 
987 	return ret;
988 }
989 
990 static struct list_event_system *
create_event_list(struct tracecmd_output * handle,struct tracecmd_event_list * event_list)991 create_event_list(struct tracecmd_output *handle,
992 		  struct tracecmd_event_list *event_list)
993 {
994 	struct list_event_system *systems = NULL;
995 	struct tracecmd_event_list *list;
996 
997 	for (list = event_list; list; list = list->next)
998 		create_event_list_item(handle, &systems, list);
999 
1000 	return systems;
1001 }
1002 
read_event_files(struct tracecmd_output * handle,struct tracecmd_event_list * event_list,bool compress)1003 static int read_event_files(struct tracecmd_output *handle,
1004 			    struct tracecmd_event_list *event_list, bool compress)
1005 {
1006 	enum tracecmd_section_flags flags = 0;
1007 	struct list_event_system *systems;
1008 	struct list_event_system *slist;
1009 	struct tracecmd_event_list *list;
1010 	struct tracecmd_event_list all_events = { .glob = "*/*" };
1011 	int count = 0;
1012 	tsize_t offset;
1013 	int endian4;
1014 	int ret;
1015 
1016 	if (!check_out_state(handle, TRACECMD_FILE_ALL_EVENTS)) {
1017 		tracecmd_warning("Cannot read event files, unexpected state 0x%X",
1018 				 handle->file_state);
1019 		return -1;
1020 	}
1021 
1022 	if (compress)
1023 		flags |= TRACECMD_SEC_FL_COMPRESS;
1024 	offset = out_write_section_header(handle, TRACECMD_OPTION_EVENT_FORMATS,
1025 					  "events format", flags, true);
1026 	if (offset == (off64_t)-1)
1027 		return -1;
1028 	/*
1029 	 * If any of the list is the special keyword "all" then
1030 	 * just do all files.
1031 	 */
1032 	for (list = event_list; list; list = list->next) {
1033 		if (strcmp(list->glob, "all") == 0)
1034 			break;
1035 	}
1036 	/* all events are listed, use a global glob */
1037 	if (!event_list || list)
1038 		event_list = &all_events;
1039 
1040 	systems = create_event_list(handle, event_list);
1041 
1042 	for (slist = systems; slist; slist = slist->next)
1043 		count++;
1044 	out_compression_start(handle, compress);
1045 	ret = -1;
1046 	endian4 = convert_endian_4(handle, count);
1047 	if (do_write_check(handle, &endian4, 4))
1048 		goto out_free;
1049 
1050 	ret = 0;
1051 	for (slist = systems; !ret && slist; slist = slist->next) {
1052 		if (do_write_check(handle, slist->name,
1053 				   strlen(slist->name) + 1)) {
1054 			ret = -1;
1055 			continue;
1056 		}
1057 		ret = copy_event_system(handle, slist);
1058 	}
1059 	if (ret)
1060 		goto out_free;
1061 
1062 	ret = out_compression_end(handle, compress);
1063 	if (ret)
1064 		goto out_free;
1065 	ret = out_update_section_header(handle, offset);
1066 
1067  out_free:
1068 	if (!ret)
1069 		handle->file_state = TRACECMD_FILE_ALL_EVENTS;
1070 	else
1071 		out_compression_reset(handle, compress);
1072 
1073 	free_list_events(systems);
1074 
1075 	return ret;
1076 }
1077 
1078 #define KPTR_UNINITIALIZED 'X'
1079 
set_proc_kptr_restrict(int reset)1080 static void set_proc_kptr_restrict(int reset)
1081 {
1082 	char *path = "/proc/sys/kernel/kptr_restrict";
1083 	static char saved = KPTR_UNINITIALIZED;
1084 	int fd, ret = -1;
1085 	struct stat st;
1086 	char buf;
1087 
1088 	if ((reset && saved == KPTR_UNINITIALIZED) ||
1089 	    (stat(path, &st) < 0))
1090 		return;
1091 
1092 	fd = open(path, O_RDONLY);
1093 	if (fd < 0)
1094 		goto err;
1095 
1096 	if (reset) {
1097 		buf = saved;
1098 	} else {
1099 		if (read(fd, &buf, 1) < 0)
1100 			goto err;
1101 		saved = buf;
1102 		buf = '0';
1103 	}
1104 	close(fd);
1105 
1106 	fd = open(path, O_WRONLY);
1107 	if (fd < 0)
1108 		goto err;
1109 	if (write(fd, &buf, 1) > 0)
1110 		ret = 0;
1111 err:
1112 	if (fd > 0)
1113 		close(fd);
1114 	if (ret)
1115 		tracecmd_warning("can't set kptr_restrict");
1116 }
1117 
read_proc_kallsyms(struct tracecmd_output * handle,bool compress)1118 static int read_proc_kallsyms(struct tracecmd_output *handle, bool compress)
1119 {
1120 	enum tracecmd_section_flags flags = 0;
1121 	unsigned int size, check_size, endian4;
1122 	const char *path = "/proc/kallsyms";
1123 	tsize_t offset;
1124 	struct stat st;
1125 	int ret;
1126 
1127 	if (!check_out_state(handle, TRACECMD_FILE_KALLSYMS)) {
1128 		tracecmd_warning("Cannot read kallsyms, unexpected state 0x%X",
1129 				 handle->file_state);
1130 		return -1;
1131 	}
1132 
1133 	if (handle->kallsyms)
1134 		path = handle->kallsyms;
1135 
1136 	if (compress)
1137 		flags |= TRACECMD_SEC_FL_COMPRESS;
1138 	offset = out_write_section_header(handle, TRACECMD_OPTION_KALLSYMS,
1139 					  "kallsyms", flags, true);
1140 	if (offset == (off64_t)-1)
1141 		return -1;
1142 
1143 	out_compression_start(handle, compress);
1144 	ret = stat(path, &st);
1145 	if (ret < 0) {
1146 		/* not found */
1147 		size = 0;
1148 		endian4 = convert_endian_4(handle, size);
1149 		ret = do_write_check(handle, &endian4, 4);
1150 		goto out;
1151 	}
1152 	size = get_size(path);
1153 	endian4 = convert_endian_4(handle, size);
1154 	ret = do_write_check(handle, &endian4, 4);
1155 	if (ret)
1156 		goto out;
1157 
1158 	set_proc_kptr_restrict(0);
1159 	check_size = copy_file(handle, path);
1160 	if (size != check_size) {
1161 		errno = EINVAL;
1162 		tracecmd_warning("error in size of file '%s'", path);
1163 		set_proc_kptr_restrict(1);
1164 		ret = -1;
1165 		goto out;
1166 	}
1167 	set_proc_kptr_restrict(1);
1168 
1169 	ret = out_compression_end(handle, compress);
1170 	if (ret)
1171 		goto out;
1172 
1173 	ret = out_update_section_header(handle, offset);
1174 out:
1175 	if (!ret)
1176 		handle->file_state = TRACECMD_FILE_KALLSYMS;
1177 	else
1178 		out_compression_reset(handle, compress);
1179 	return ret;
1180 }
1181 
read_ftrace_printk(struct tracecmd_output * handle,bool compress)1182 static int read_ftrace_printk(struct tracecmd_output *handle, bool compress)
1183 {
1184 	enum tracecmd_section_flags flags = 0;
1185 	unsigned int size, check_size, endian4;
1186 	tsize_t offset;
1187 	struct stat st;
1188 	char *path;
1189 	int ret;
1190 
1191 	if (!check_out_state(handle, TRACECMD_FILE_PRINTK)) {
1192 		tracecmd_warning("Cannot read printk, unexpected state 0x%X",
1193 				 handle->file_state);
1194 		return -1;
1195 	}
1196 
1197 	path = get_tracing_file(handle, "printk_formats");
1198 	if (!path)
1199 		return -1;
1200 
1201 	if (compress)
1202 		flags |= TRACECMD_SEC_FL_COMPRESS;
1203 	offset = out_write_section_header(handle, TRACECMD_OPTION_PRINTK, "printk", flags, true);
1204 	if (offset == (off64_t)-1)
1205 		return -1;
1206 
1207 	out_compression_start(handle, compress);
1208 	ret = stat(path, &st);
1209 	if (ret < 0) {
1210 		/* not found */
1211 		size = 0;
1212 		endian4 = convert_endian_4(handle, size);
1213 		if (do_write_check(handle, &endian4, 4))
1214 			goto fail;
1215 		goto out;
1216 	}
1217 	size = get_size(path);
1218 	endian4 = convert_endian_4(handle, size);
1219 	if (do_write_check(handle, &endian4, 4))
1220 		goto fail;
1221 	check_size = copy_file(handle, path);
1222 	if (size != check_size) {
1223 		errno = EINVAL;
1224 		tracecmd_warning("error in size of file '%s'", path);
1225 		goto fail;
1226 	}
1227 
1228  out:
1229 	put_tracing_file(path);
1230 	if (out_compression_end(handle, compress))
1231 		return -1;
1232 
1233 	if (out_update_section_header(handle, offset))
1234 		return -1;
1235 	handle->file_state = TRACECMD_FILE_PRINTK;
1236 	return 0;
1237  fail:
1238 	put_tracing_file(path);
1239 	out_compression_reset(handle, compress);
1240 	return -1;
1241 }
1242 
save_tracing_file_data(struct tracecmd_output * handle,const char * filename)1243 static int save_tracing_file_data(struct tracecmd_output *handle,
1244 				  const char *filename)
1245 {
1246 	unsigned long long endian8;
1247 	char *file = NULL;
1248 	struct stat st;
1249 	off64_t check_size;
1250 	off64_t size;
1251 	int ret = -1;
1252 
1253 	file = get_tracing_file(handle, filename);
1254 	if (!file)
1255 		return -1;
1256 
1257 	ret = stat(file, &st);
1258 	if (ret >= 0) {
1259 		size = get_size(file);
1260 		endian8 = convert_endian_8(handle, size);
1261 		if (do_write_check(handle, &endian8, 8))
1262 			goto out_free;
1263 		check_size = copy_file(handle, file);
1264 		if (size != check_size) {
1265 			errno = EINVAL;
1266 			tracecmd_warning("error in size of file '%s'", file);
1267 			goto out_free;
1268 		}
1269 	} else {
1270 		size = 0;
1271 		endian8 = convert_endian_8(handle, size);
1272 		if (do_write_check(handle, &endian8, 8))
1273 			goto out_free;
1274 	}
1275 	ret = 0;
1276 
1277 out_free:
1278 	put_tracing_file(file);
1279 	return ret;
1280 }
1281 
write_compression_header(struct tracecmd_output * handle)1282 static int write_compression_header(struct tracecmd_output *handle)
1283 {
1284 	const char *name = NULL;
1285 	const char *ver = NULL;
1286 	int ret;
1287 
1288 	ret = tracecmd_compress_proto_get_name(handle->compress, &name, &ver);
1289 	if (ret < 0 || !name || !ver) {
1290 		name = "none";
1291 		ver = "";
1292 	}
1293 
1294 	if (do_write_check(handle, name, strlen(name) + 1))
1295 		return -1;
1296 
1297 	if (do_write_check(handle, ver, strlen(ver) + 1))
1298 		return -1;
1299 
1300 	return 0;
1301 }
1302 
get_trace_page_size(struct tracecmd_output * handle,const char * name)1303 static int get_trace_page_size(struct tracecmd_output *handle, const char *name)
1304 {
1305 	struct tracefs_instance *instance;
1306 	struct tep_handle *tep = NULL;
1307 	int psize, size;
1308 	char *buff = NULL;
1309 
1310 	/* In case of an error, return user space page size */
1311 	psize = getpagesize();
1312 
1313 	instance = tracefs_instance_alloc(find_tracing_dir(handle), name);
1314 	if (!instance)
1315 		goto out;
1316 
1317 	buff = tracefs_instance_file_read(instance, "events/header_page", &size);
1318 	if (!buff)
1319 		goto out;
1320 
1321 	tep = tep_alloc();
1322 	if (!tep)
1323 		goto out;
1324 
1325 	if (tep_parse_header_page(tep, buff, size, sizeof(long long)))
1326 		goto out;
1327 
1328 	psize = tep_get_sub_buffer_size(tep);
1329 
1330 out:
1331 	tracefs_instance_free(instance);
1332 	tep_free(tep);
1333 	free(buff);
1334 
1335 	return psize;
1336 }
1337 
1338 /**
1339  * tracecmd_output_create_fd - allocate new output handle to a trace file
1340  * @fd: File descriptor for the handle to write to.
1341  *
1342  * Allocate a tracecmd_output descriptor and perform minimal initialization.
1343  * @fd will be set as the file descriptor for the handle. Nothing is
1344  * written in the file yet, and if @fd is -1, then all writes will be ignored.
1345  *
1346  * Returns a pointer to a newly allocated file descriptor for the use of creating
1347  * a tracecmd data file. In case of an error, NULL is returned. The returned
1348  * handle must be freed with tracecmd_output_close() or tracecmd_output_free()
1349  */
tracecmd_output_create_fd(int fd)1350 struct tracecmd_output *tracecmd_output_create_fd(int fd)
1351 {
1352 	struct tracecmd_output *handle;
1353 
1354 	handle = calloc(1, sizeof(*handle));
1355 	if (!handle)
1356 		return NULL;
1357 
1358 	handle->fd = fd;
1359 
1360 	handle->file_version = FILE_VERSION_DEFAULT;
1361 
1362 	handle->page_size = get_trace_page_size(handle, NULL);
1363 	handle->big_endian = tracecmd_host_bigendian();
1364 
1365 	list_head_init(&handle->options);
1366 	list_head_init(&handle->buffers);
1367 
1368 	handle->file_state = TRACECMD_FILE_ALLOCATED;
1369 
1370 	return handle;
1371 }
1372 
1373 /**
1374  * tracecmd_output_set_msg - associated an output file handle with network message handle
1375  * @handle: output handle to a trace file.
1376  * @msg_handle: network handle, allocated by tracecmd_msg_handle_alloc()
1377  *
1378  * Associate an output file handle (@handle) to a network stream (@msg_handle).
1379  * All subsequent calls to @handle will send data over the network using @msg_handle
1380  * instead of writing to a file.
1381  *
1382  * This must be called after the handle file version is set and before calling
1383  * tracecmd_output_write_headers().
1384  *
1385  * Returns 0 on success, or -1 if the output file handle is not allocated or not
1386  * in the expected state.
1387  */
tracecmd_output_set_msg(struct tracecmd_output * handle,struct tracecmd_msg_handle * msg_handle)1388 int tracecmd_output_set_msg(struct tracecmd_output *handle, struct tracecmd_msg_handle *msg_handle)
1389 {
1390 	if (!handle || handle->file_state != TRACECMD_FILE_ALLOCATED)
1391 		return -1;
1392 
1393 	handle->msg_handle = msg_handle;
1394 	/* Force messages to be cached in a temp file before sending through the socket */
1395 	if (handle->msg_handle && HAS_SECTIONS(handle))
1396 		tracecmd_msg_handle_cache(handle->msg_handle);
1397 
1398 	return 0;
1399 }
1400 
1401 /**
1402  * tracecmd_output_set_trace_dir - Set a custom tracing dir, instead of system default
1403  * @handle: output handle to a trace file.
1404  * @tracing_dir: full path to a directory with tracing files
1405  *
1406  * Associate the output file handle (@handle) with a custom tracing directory
1407  * (@tracing_dir), to be used when creating the trace file instead of using the
1408  * system default tracig directory.
1409  *
1410  * Must be called before tracecmd_output_write_headers().
1411  *
1412  * Returns 0 on success, or -1 if the output file handle is not allocated or not
1413  * in the expected state.
1414  */
tracecmd_output_set_trace_dir(struct tracecmd_output * handle,const char * tracing_dir)1415 int tracecmd_output_set_trace_dir(struct tracecmd_output *handle, const char *tracing_dir)
1416 {
1417 	if (!handle || handle->file_state != TRACECMD_FILE_ALLOCATED)
1418 		return -1;
1419 
1420 	free(handle->tracing_dir);
1421 	if (tracing_dir) {
1422 		handle->tracing_dir = strdup(tracing_dir);
1423 		if (!handle->tracing_dir)
1424 			return -1;
1425 	} else
1426 		handle->tracing_dir = NULL;
1427 
1428 	return 0;
1429 }
1430 
1431 /**
1432  * tracecmd_output_set_kallsyms - Set a custom kernel symbols file
1433  * @handle: output handle to a trace file.
1434  * @tracing_dir: full path to a file with kernel symbols
1435  *
1436  * Have the output file handle (@handle) use a custom kernel symbols file instead
1437  * of the default /proc/kallsyms.
1438  *
1439  * Must be called before tracecmd_output_write_headers().
1440  *
1441  * Returns 0 on success, or -1 if the output file handle is not allocated or
1442  * not in the expected state.
1443  */
tracecmd_output_set_kallsyms(struct tracecmd_output * handle,const char * kallsyms)1444 int tracecmd_output_set_kallsyms(struct tracecmd_output *handle, const char *kallsyms)
1445 {
1446 	if (!handle || handle->file_state != TRACECMD_FILE_ALLOCATED)
1447 		return -1;
1448 
1449 	free(handle->kallsyms);
1450 	if (kallsyms) {
1451 		handle->kallsyms = strdup(kallsyms);
1452 		if (!handle->kallsyms)
1453 			return -1;
1454 	} else
1455 		handle->kallsyms = NULL;
1456 
1457 	return 0;
1458 }
1459 
1460 /**
1461  * tracecmd_output_set_from_input - Inherit parameters from an existing trace file
1462  * @handle: output handle to a trace file.
1463  * @ihandle: input handle to an existing trace file.
1464  *
1465  * Have the output file handle (@handle) inherit the properties of a given
1466  * input file handle (@ihandle).
1467  *
1468  * The parameters that are copied are:
1469  *  - tep handle
1470  *  - page size
1471  *  - file endian
1472  *  - file version
1473  *  - file compression protocol
1474  *
1475  * Must be called before tracecmd_output_write_headers().
1476  *
1477  * Returns 0 on success, or -1 if the output file handle is not allocated or
1478  * not in expected state.
1479  */
tracecmd_output_set_from_input(struct tracecmd_output * handle,struct tracecmd_input * ihandle)1480 int tracecmd_output_set_from_input(struct tracecmd_output *handle, struct tracecmd_input *ihandle)
1481 {
1482 	const char *cname = NULL;
1483 	const char *cver = NULL;
1484 
1485 	if (!handle || !ihandle || handle->file_state != TRACECMD_FILE_ALLOCATED)
1486 		return -1;
1487 
1488 	/* get endian, page size, file version and compression */
1489 	/* Use the pevent of the ihandle for later writes */
1490 	handle->pevent = tracecmd_get_tep(ihandle);
1491 	tep_ref(handle->pevent);
1492 	handle->page_size = tracecmd_page_size(ihandle);
1493 	handle->file_version = tracecmd_get_in_file_version(ihandle);
1494 	handle->big_endian = tep_is_file_bigendian(handle->pevent);
1495 
1496 	if (!tracecmd_get_file_compress_proto(ihandle, &cname, &cver)) {
1497 		handle->compress = tracecmd_compress_alloc(cname, cver, handle->fd,
1498 							    handle->pevent, handle->msg_handle);
1499 		if (!handle->compress)
1500 			return -1;
1501 
1502 		if (handle->file_version < FILE_VERSION_COMPRESSION)
1503 			handle->file_version = FILE_VERSION_COMPRESSION;
1504 	}
1505 
1506 	return 0;
1507 }
1508 
1509 /**
1510  * tracecmd_output_set_version - Set file version of the output handle
1511  * @handle: output handle to a trace file.
1512  * @file_version: desired file version
1513  *
1514  * This API must be called before tracecmd_output_write_headers().
1515  *
1516  * Returns 0 on success, or -1 if the output file handle is not allocated or not in expected state.
1517  */
tracecmd_output_set_version(struct tracecmd_output * handle,int file_version)1518 int tracecmd_output_set_version(struct tracecmd_output *handle, int file_version)
1519 {
1520 	if (!handle || handle->file_state != TRACECMD_FILE_ALLOCATED)
1521 		return -1;
1522 	if (file_version < FILE_VERSION_MIN || file_version > FILE_VERSION_MAX)
1523 		return -1;
1524 	handle->file_version = file_version;
1525 	if (handle->file_version < FILE_VERSION_COMPRESSION)
1526 		handle->compress = NULL;
1527 	return 0;
1528 }
1529 
1530 /**
1531  * tracecmd_output_set_compression - Set file compression algorithm of the output handle
1532  * @handle: output handle to a trace file.
1533  * @compression: name of the desired compression algorithm. Can be one of:
1534  *		 - "none" - do not use compression
1535  *		 - "all" - use the best available compression algorithm
1536  *		 - or specific name of the desired compression algorithm
1537  *
1538  * This API must be called before tracecmd_output_write_headers().
1539  *
1540  * Returns 0 on success, or -1 in case of an error:
1541  *   - the output file handle is not allocated or not in expected state.
1542  *   - the specified compression algorithm is not available
1543  */
tracecmd_output_set_compression(struct tracecmd_output * handle,const char * compression)1544 int tracecmd_output_set_compression(struct tracecmd_output *handle, const char *compression)
1545 {
1546 	if (!handle || handle->file_state != TRACECMD_FILE_ALLOCATED)
1547 		return -1;
1548 
1549 	handle->compress = NULL;
1550 	if (compression && strcmp(compression, "none")) {
1551 		if (!strcmp(compression, "any")) {
1552 			handle->compress = tracecmd_compress_alloc(NULL, NULL, handle->fd,
1553 								    handle->pevent,
1554 								    handle->msg_handle);
1555 			if (!handle->compress)
1556 				tracecmd_warning("No compression algorithms are supported");
1557 		} else {
1558 			handle->compress = tracecmd_compress_alloc(compression, NULL, handle->fd,
1559 								    handle->pevent,
1560 								    handle->msg_handle);
1561 			if (!handle->compress) {
1562 				tracecmd_warning("Compression algorithm %s is not supported",
1563 						  compression);
1564 				return -1;
1565 			}
1566 		}
1567 	}
1568 	if (handle->compress && handle->file_version < FILE_VERSION_COMPRESSION) {
1569 		handle->file_version = FILE_VERSION_COMPRESSION;
1570 		if (handle->msg_handle)
1571 			tracecmd_msg_handle_cache(handle->msg_handle);
1572 	}
1573 
1574 	return 0;
1575 }
1576 
1577 /**
1578  * output_write_init - Write the initial data into the trace file
1579  * @handle: output handle to a trace file.
1580  *
1581  * Must be called after tracecmd_output_set_*() functions and before writing
1582  * anything else.
1583  *
1584  * The initial information to be written into the file:
1585  *  - initial file magic bytes
1586  *  - file version
1587  *  - data endian
1588  *  - long size
1589  *  - page size
1590  *  - compression header
1591  *
1592  * Returns 0 on success, or -1 if the output file handle is not allocated or
1593  * not in the expected state.
1594  */
output_write_init(struct tracecmd_output * handle)1595 static int output_write_init(struct tracecmd_output *handle)
1596 {
1597 	unsigned long long offset;
1598 	char buf[BUFSIZ];
1599 	int endian4;
1600 
1601 	if (!handle || handle->file_state != TRACECMD_FILE_ALLOCATED)
1602 		return -1;
1603 
1604 	buf[0] = 23;
1605 	buf[1] = 8;
1606 	buf[2] = 68;
1607 	memcpy(buf + 3, "tracing", 7);
1608 
1609 	if (do_write_check(handle, buf, 10))
1610 		return -1;
1611 
1612 	sprintf(buf, "%lu", handle->file_version);
1613 	if (do_write_check(handle, buf, strlen(buf) + 1))
1614 		return -1;
1615 
1616 	if (handle->big_endian)
1617 		buf[0] = 1;
1618 	else
1619 		buf[0] = 0;
1620 	if (do_write_check(handle, buf, 1))
1621 		return -1;
1622 
1623 	/* save size of long (this may not be what the kernel is) */
1624 	buf[0] = sizeof(long);
1625 	if (do_write_check(handle, buf, 1))
1626 		return -1;
1627 
1628 	endian4 = convert_endian_4(handle, handle->page_size);
1629 	if (do_write_check(handle, &endian4, 4))
1630 		return -1;
1631 
1632 	if (handle->file_version >= FILE_VERSION_COMPRESSION) {
1633 		if (write_compression_header(handle))
1634 			return -1;
1635 	}
1636 
1637 	if (HAS_SECTIONS(handle)) {
1638 		/* Write 0 as options offset and save its location */
1639 		offset = 0;
1640 		handle->options_start = do_lseek(handle, 0, SEEK_CUR);
1641 		if (do_write_check(handle, &offset, 8))
1642 			return -1;
1643 	}
1644 
1645 	handle->file_state = TRACECMD_FILE_INIT;
1646 	return 0;
1647 }
1648 
1649 /**
1650  * tracecmd_output_write_headers - Write the trace file headers
1651  * @handle: output handle to a trace file.
1652  * @list: desired events that will be included in the trace file.
1653  *	  It can be NULL for all available events
1654  *
1655  * These headers are written in the file:
1656  *  - header files from the tracing directory
1657  *  - ftrace events from the tracing directory
1658  *  - event file from the tracing directory - all or only the one from @list
1659  *  - kernel symbols from the tracing directory
1660  *  - kernel printk strings from the tracing directory
1661  *
1662  * Returns 0 on success, or -1 in case of an error.
1663  */
tracecmd_output_write_headers(struct tracecmd_output * handle,struct tracecmd_event_list * list)1664 int tracecmd_output_write_headers(struct tracecmd_output *handle,
1665 				  struct tracecmd_event_list *list)
1666 {
1667 	bool compress = false;
1668 
1669 	if (!handle || handle->file_state < TRACECMD_FILE_ALLOCATED)
1670 		return -1;
1671 
1672 	/* Write init data, if not written yet */
1673 	if (handle->file_state < TRACECMD_FILE_INIT && output_write_init(handle))
1674 		return -1;
1675 	if (handle->compress)
1676 		compress = true;
1677 	if (read_header_files(handle, compress))
1678 		return -1;
1679 	if (read_ftrace_files(handle, compress))
1680 		return -1;
1681 	if (read_event_files(handle, list, compress))
1682 		return -1;
1683 	if (read_proc_kallsyms(handle, compress))
1684 		return -1;
1685 	if (read_ftrace_printk(handle, compress))
1686 		return -1;
1687 	return 0;
1688 }
1689 
1690 /**
1691  * tracecmd_add_option_v - add options to the file
1692  * @handle: the output file handle name
1693  * @id: the id of the option
1694  * @size: the size of the option data
1695  * @data: the data to write to the file
1696  * @vector: array of vectors, pointing to the data to write in the file
1697  * @count: number of items in the vector array
1698  *
1699  *
1700  * Returns handle to update option if needed.
1701  *  Just the content can be updated, with smaller or equal to
1702  *  content than the specified size.
1703  */
1704 struct tracecmd_option *
tracecmd_add_option_v(struct tracecmd_output * handle,unsigned short id,const struct iovec * vector,int count)1705 tracecmd_add_option_v(struct tracecmd_output *handle,
1706 		      unsigned short id, const struct iovec *vector, int count)
1707 
1708 {
1709 	struct tracecmd_option *option;
1710 	char *data = NULL;
1711 	int i, size = 0;
1712 
1713 	/*
1714 	 * We can only add options before tracing data were written.
1715 	 * This may change in the future.
1716 	 */
1717 	if (!HAS_SECTIONS(handle) && handle->file_state > TRACECMD_FILE_OPTIONS)
1718 		return NULL;
1719 
1720 	for (i = 0; i < count; i++)
1721 		size += vector[i].iov_len;
1722 	/* Some IDs (like TRACECMD_OPTION_TRACECLOCK) pass vector with 0 / NULL data */
1723 	if (size) {
1724 		data = malloc(size);
1725 		if (!data) {
1726 			tracecmd_warning("Insufficient memory");
1727 			return NULL;
1728 		}
1729 	}
1730 	option = calloc(1, sizeof(*option));
1731 	if (!option) {
1732 		tracecmd_warning("Could not allocate space for option");
1733 		free(data);
1734 		return NULL;
1735 	}
1736 
1737 	handle->nr_options++;
1738 	option->data = data;
1739 	for (i = 0; i < count; i++) {
1740 		if (vector[i].iov_base && vector[i].iov_len) {
1741 			memcpy(data, vector[i].iov_base, vector[i].iov_len);
1742 			data += vector[i].iov_len;
1743 		}
1744 	}
1745 
1746 	option->size = size;
1747 	option->id = id;
1748 
1749 	list_add_tail(&option->list, &handle->options);
1750 
1751 	return option;
1752 }
1753 
1754 /**
1755  * tracecmd_add_option - add options to the file
1756  * @handle: the output file handle name
1757  * @id: the id of the option
1758  * @size: the size of the option data
1759  * @data: the data to write to the file
1760  *
1761  * Returns handle to update option if needed
1762  *  Just the content can be updated, with smaller or equal to
1763  *  content than the specified size
1764  */
1765 struct tracecmd_option *
tracecmd_add_option(struct tracecmd_output * handle,unsigned short id,int size,const void * data)1766 tracecmd_add_option(struct tracecmd_output *handle,
1767 		    unsigned short id, int size, const void *data)
1768 {
1769 	struct iovec vect;
1770 
1771 	vect.iov_base = (void *) data;
1772 	vect.iov_len = size;
1773 	return tracecmd_add_option_v(handle, id, &vect, 1);
1774 }
1775 
tracecmd_write_cpus(struct tracecmd_output * handle,int cpus)1776 int tracecmd_write_cpus(struct tracecmd_output *handle, int cpus)
1777 {
1778 	int ret;
1779 
1780 	if (!check_out_state(handle, TRACECMD_FILE_CPU_COUNT)) {
1781 		tracecmd_warning("Cannot write CPU count into the file, unexpected state 0x%X",
1782 				 handle->file_state);
1783 		return -1;
1784 	}
1785 
1786 	if (!HAS_SECTIONS(handle)) {
1787 		cpus = convert_endian_4(handle, cpus);
1788 		ret = do_write_check(handle, &cpus, 4);
1789 		if (ret < 0)
1790 			return ret;
1791 	} else {
1792 		tracecmd_add_option(handle, TRACECMD_OPTION_CPUCOUNT, sizeof(int), &cpus);
1793 	}
1794 
1795 	handle->file_state = TRACECMD_FILE_CPU_COUNT;
1796 	return 0;
1797 }
1798 
write_options_v6(struct tracecmd_output * handle)1799 static int write_options_v6(struct tracecmd_output *handle)
1800 {
1801 	struct tracecmd_option *options;
1802 	unsigned short option;
1803 	unsigned short endian2;
1804 	unsigned int endian4;
1805 
1806 	/* If already written, ignore */
1807 	if (handle->file_state == TRACECMD_FILE_OPTIONS)
1808 		return 0;
1809 	if (!check_out_state(handle, TRACECMD_FILE_OPTIONS)) {
1810 		tracecmd_warning("Cannot write options into the file, unexpected state 0x%X",
1811 				 handle->file_state);
1812 		return -1;
1813 	}
1814 
1815 	if (do_write_check(handle, "options  ", 10))
1816 		return -1;
1817 	handle->options_start = do_lseek(handle, 0, SEEK_CUR);
1818 	list_for_each_entry(options, &handle->options, list) {
1819 		endian2 = convert_endian_2(handle, options->id);
1820 		if (do_write_check(handle, &endian2, 2))
1821 			return -1;
1822 
1823 		endian4 = convert_endian_4(handle, options->size);
1824 		if (do_write_check(handle, &endian4, 4))
1825 			return -1;
1826 
1827 		/* Save the data location in case it needs to be updated */
1828 		options->offset = do_lseek(handle, 0, SEEK_CUR);
1829 
1830 		if (do_write_check(handle, options->data,
1831 				   options->size))
1832 			return -1;
1833 	}
1834 
1835 	option = TRACECMD_OPTION_DONE;
1836 
1837 	if (do_write_check(handle, &option, 2))
1838 		return -1;
1839 
1840 	handle->file_state = TRACECMD_FILE_OPTIONS;
1841 	return 0;
1842 }
1843 
write_options(struct tracecmd_output * handle)1844 static int write_options(struct tracecmd_output *handle)
1845 {
1846 	struct tracecmd_option *options;
1847 	unsigned long long endian8;
1848 	unsigned short endian2;
1849 	unsigned int endian4;
1850 	bool new = false;
1851 	tsize_t offset;
1852 
1853 	/* Check if there are unsaved options */
1854 	list_for_each_entry(options, &handle->options, list) {
1855 		if (!options->offset) {
1856 			new = true;
1857 			break;
1858 		}
1859 	}
1860 	if (!new)
1861 		return 0;
1862 	offset = do_lseek(handle, 0, SEEK_CUR);
1863 
1864 	/* Append to the previous options section, if any */
1865 	if (handle->options_start) {
1866 		if (do_lseek(handle, handle->options_start, SEEK_SET) == (off64_t)-1)
1867 			return -1;
1868 		endian8 = convert_endian_8(handle, offset);
1869 		if (do_write_check(handle, &endian8, 8))
1870 			return -1;
1871 		if (do_lseek(handle, offset, SEEK_SET) == (off_t)-1)
1872 			return -1;
1873 	}
1874 
1875 	offset = out_write_section_header(handle, TRACECMD_OPTION_DONE, "options", 0, false);
1876 	if (offset == (off_t)-1)
1877 		return -1;
1878 
1879 	list_for_each_entry(options, &handle->options, list) {
1880 		/* Option is already saved, skip it */
1881 		if (options->offset)
1882 			continue;
1883 		endian2 = convert_endian_2(handle, options->id);
1884 		if (do_write_check(handle, &endian2, 2))
1885 			return -1;
1886 		endian4 = convert_endian_4(handle, options->size);
1887 		if (do_write_check(handle, &endian4, 4))
1888 			return -1;
1889 		/* Save the data location */
1890 		options->offset = do_lseek(handle, 0, SEEK_CUR);
1891 		if (do_write_check(handle, options->data, options->size))
1892 			return -1;
1893 	}
1894 
1895 	endian2 = convert_endian_2(handle, TRACECMD_OPTION_DONE);
1896 	if (do_write_check(handle, &endian2, 2))
1897 		return -1;
1898 	endian4 = convert_endian_4(handle, 8);
1899 	if (do_write_check(handle, &endian4, 4))
1900 		return -1;
1901 	endian8 = 0;
1902 	handle->options_start = do_lseek(handle, 0, SEEK_CUR);
1903 	if (do_write_check(handle, &endian8, 8))
1904 		return -1;
1905 	if (out_update_section_header(handle, offset))
1906 		return -1;
1907 
1908 	return 0;
1909 }
1910 
tracecmd_write_meta_strings(struct tracecmd_output * handle)1911 int tracecmd_write_meta_strings(struct tracecmd_output *handle)
1912 {
1913 	if (!HAS_SECTIONS(handle))
1914 		return 0;
1915 
1916 	return save_string_section(handle, true);
1917 }
1918 
tracecmd_write_options(struct tracecmd_output * handle)1919 int tracecmd_write_options(struct tracecmd_output *handle)
1920 {
1921 	if (!HAS_SECTIONS(handle))
1922 		return write_options_v6(handle);
1923 	return write_options(handle);
1924 }
1925 
append_options_v6(struct tracecmd_output * handle)1926 static int append_options_v6(struct tracecmd_output *handle)
1927 {
1928 	struct tracecmd_option *options;
1929 	unsigned short option;
1930 	unsigned short endian2;
1931 	unsigned int endian4;
1932 	off_t offset;
1933 	int r;
1934 
1935 	/*
1936 	 * We can append only if options are already written and tracing data
1937 	 * is not yet written
1938 	 */
1939 	if (handle->file_state != TRACECMD_FILE_OPTIONS)
1940 		return -1;
1941 
1942 	if (do_lseek(handle, 0, SEEK_END) == (off_t)-1)
1943 		return -1;
1944 	offset = do_lseek(handle, -2, SEEK_CUR);
1945 	if (offset == (off_t)-1)
1946 		return -1;
1947 
1948 	r = do_preed(handle, &option, 2, offset);
1949 	if (r != 2 || option != TRACECMD_OPTION_DONE)
1950 		return -1;
1951 
1952 	list_for_each_entry(options, &handle->options, list) {
1953 		endian2 = convert_endian_2(handle, options->id);
1954 		if (do_write_check(handle, &endian2, 2))
1955 			return -1;
1956 
1957 		endian4 = convert_endian_4(handle, options->size);
1958 		if (do_write_check(handle, &endian4, 4))
1959 			return -1;
1960 
1961 		/* Save the data location in case it needs to be updated */
1962 		options->offset = do_lseek(handle, 0, SEEK_CUR);
1963 
1964 		if (do_write_check(handle, options->data,
1965 				   options->size))
1966 			return -1;
1967 	}
1968 
1969 	option = TRACECMD_OPTION_DONE;
1970 
1971 	if (do_write_check(handle, &option, 2))
1972 		return -1;
1973 
1974 	return 0;
1975 }
1976 
tracecmd_append_options(struct tracecmd_output * handle)1977 int tracecmd_append_options(struct tracecmd_output *handle)
1978 {
1979 	if (!HAS_SECTIONS(handle))
1980 		return append_options_v6(handle);
1981 	return write_options(handle);
1982 }
1983 
1984 static struct tracecmd_option *
add_buffer_option_v6(struct tracecmd_output * handle,const char * name,int cpus)1985 add_buffer_option_v6(struct tracecmd_output *handle, const char *name, int cpus)
1986 {
1987 	struct tracecmd_option *option;
1988 	char *buf;
1989 	int size = 8 + strlen(name) + 1;
1990 
1991 	buf = calloc(1, size);
1992 	if (!buf) {
1993 		tracecmd_warning("Failed to malloc buffer");
1994 		return NULL;
1995 	}
1996 	*(tsize_t *)buf = 0;
1997 	strcpy(buf + 8, name);
1998 
1999 	option = tracecmd_add_option(handle, TRACECMD_OPTION_BUFFER, size, buf);
2000 	free(buf);
2001 
2002 	/*
2003 	 * In case a buffer instance has different number of CPUs as the
2004 	 * local machine.
2005 	 */
2006 	if (cpus)
2007 		tracecmd_add_option(handle, TRACECMD_OPTION_CPUCOUNT,
2008 				    sizeof(int), &cpus);
2009 
2010 	return option;
2011 }
2012 
tracecmd_add_buffer_info(struct tracecmd_output * handle,const char * name,int cpus)2013 int tracecmd_add_buffer_info(struct tracecmd_output *handle, const char *name, int cpus)
2014 {
2015 	struct tracecmd_buffer *buf;
2016 
2017 	buf = calloc(1, sizeof(struct tracecmd_buffer));
2018 	if (!buf)
2019 		return -1;
2020 	buf->name = strdup(name);
2021 	buf->cpus = cpus;
2022 	if (!buf->name) {
2023 		free(buf);
2024 		return -1;
2025 	}
2026 	list_add_tail(&buf->list, &handle->buffers);
2027 	return 0;
2028 }
2029 
tracecmd_write_buffer_info(struct tracecmd_output * handle)2030 int tracecmd_write_buffer_info(struct tracecmd_output *handle)
2031 {
2032 	struct tracecmd_option *option;
2033 	struct tracecmd_buffer *buf;
2034 
2035 	if (HAS_SECTIONS(handle))
2036 		return 0;
2037 
2038 	list_for_each_entry(buf, &handle->buffers, list) {
2039 		option = add_buffer_option_v6(handle, buf->name, buf->cpus);
2040 		if (!option)
2041 			return -1;
2042 		buf->option = option;
2043 	}
2044 
2045 	return 0;
2046 }
2047 
get_buffer_file_offset(struct tracecmd_output * handle,const char * name)2048 static tsize_t get_buffer_file_offset(struct tracecmd_output *handle, const char *name)
2049 {
2050 	struct tracecmd_buffer *buf;
2051 
2052 	list_for_each_entry(buf, &handle->buffers, list) {
2053 		if (!strcmp(name, buf->name)) {
2054 			if (!buf->option)
2055 				break;
2056 			return buf->option->offset;
2057 		}
2058 	}
2059 	return 0;
2060 }
2061 
tracecmd_write_cmdlines(struct tracecmd_output * handle)2062 int tracecmd_write_cmdlines(struct tracecmd_output *handle)
2063 {
2064 	enum tracecmd_section_flags flags = 0;
2065 	bool compress = false;
2066 	tsize_t offset;
2067 	int ret;
2068 
2069 	if (!check_out_state(handle, TRACECMD_FILE_CMD_LINES)) {
2070 		tracecmd_warning("Cannot write command lines into the file, unexpected state 0x%X",
2071 				 handle->file_state);
2072 		return -1;
2073 	}
2074 
2075 	if (handle->compress)
2076 		compress = true;
2077 
2078 	if (compress)
2079 		flags |= TRACECMD_SEC_FL_COMPRESS;
2080 	offset = out_write_section_header(handle, TRACECMD_OPTION_CMDLINES,
2081 					  "command lines", flags, true);
2082 	if (offset == (off64_t)-1)
2083 		return -1;
2084 
2085 	out_compression_start(handle, compress);
2086 
2087 	ret = save_tracing_file_data(handle, "saved_cmdlines");
2088 	if (ret < 0) {
2089 		out_compression_reset(handle, compress);
2090 		return ret;
2091 	}
2092 
2093 	if (out_compression_end(handle, compress))
2094 		return -1;
2095 
2096 	if (out_update_section_header(handle, offset))
2097 		return -1;
2098 
2099 	handle->file_state = TRACECMD_FILE_CMD_LINES;
2100 	return 0;
2101 }
2102 
get_clock(struct tracecmd_output * handle)2103 static char *get_clock(struct tracecmd_output *handle)
2104 {
2105 	struct tracefs_instance *inst;
2106 
2107 	if (handle->trace_clock)
2108 		return handle->trace_clock;
2109 
2110 	/*
2111 	 * If no clock is set on this handle, get the trace clock of
2112 	 * the top instance in the handle's tracing dir
2113 	 */
2114 	if (!handle->tracing_dir) {
2115 		handle->trace_clock = tracefs_get_clock(NULL);
2116 		return handle->trace_clock;
2117 	}
2118 
2119 	inst = tracefs_instance_alloc(handle->tracing_dir, NULL);
2120 	if (!inst)
2121 		return NULL;
2122 	handle->trace_clock = tracefs_get_clock(inst);
2123 	tracefs_instance_free(inst);
2124 	return handle->trace_clock;
2125 }
2126 
2127 __hidden struct tracecmd_option *
out_add_buffer_option(struct tracecmd_output * handle,const char * name,unsigned short id,unsigned long long data_offset,int cpus,struct data_file_write * cpu_data,int page_size)2128 out_add_buffer_option(struct tracecmd_output *handle, const char *name,
2129 		      unsigned short id, unsigned long long data_offset,
2130 		      int cpus, struct data_file_write *cpu_data, int page_size)
2131 {
2132 	struct tracecmd_option *option;
2133 	int i, j = 0, k = 0;
2134 	int *cpu_ids = NULL;
2135 	struct iovec *vect;
2136 	char *clock;
2137 
2138 	if (!HAS_SECTIONS(handle))
2139 		return NULL;
2140 
2141 	clock = get_clock(handle);
2142 	if (!clock) {
2143 		tracecmd_warning("Could not find clock, set to 'local'");
2144 		clock = "local";
2145 	}
2146 
2147 	/*
2148 	 * Buffer flyrecord option:
2149 	 *  - trace data offset in the file
2150 	 *  - buffer name
2151 	 *  - buffer clock
2152 	 *  - page size
2153 	 *  - CPU count
2154 	 *  - for each CPU:
2155 	 *    - CPU id
2156 	 *    - CPU trace data offset in the file
2157 	 *    - CPU trace data size
2158 	 */
2159 
2160 	/*
2161 	 * Buffer latency option:
2162 	 *  - trace data offset in the file
2163 	 *  - buffer name
2164 	 *  - buffer clock
2165 	 */
2166 
2167 	/*
2168 	 * 5 : offset, name, clock, page size, count
2169 	 * 3 : cpu offset, name, clock
2170 	 */
2171 	vect = calloc(5 + (cpus * 3), sizeof(struct iovec));
2172 	if (!vect)
2173 		return NULL;
2174 	if (cpus) {
2175 		cpu_ids = calloc(cpus, sizeof(int));
2176 		if (!cpu_ids) {
2177 			free(vect);
2178 			return NULL;
2179 		}
2180 	}
2181 	vect[j].iov_base = (void *) &data_offset;
2182 	vect[j++].iov_len = 8;
2183 	vect[j].iov_base = (void *) name;
2184 	vect[j++].iov_len = strlen(name) + 1;
2185 	vect[j].iov_base = (void *) clock;
2186 	vect[j++].iov_len = strlen(clock) + 1;
2187 	if (id == TRACECMD_OPTION_BUFFER) {
2188 		vect[j].iov_base = &page_size;
2189 		vect[j++].iov_len = 4;
2190 		vect[j].iov_base = (void *) &k;
2191 		vect[j++].iov_len = 4;
2192 		for (i = 0; i < cpus; i++) {
2193 			if (!cpu_data[i].file_size)
2194 				continue;
2195 			cpu_ids[i] = i;
2196 			vect[j].iov_base = &cpu_ids[i];
2197 			vect[j++].iov_len = 4;
2198 			vect[j].iov_base = &cpu_data[i].data_offset;
2199 			vect[j++].iov_len = 8;
2200 			vect[j].iov_base = &cpu_data[i].write_size;
2201 			vect[j++].iov_len = 8;
2202 			k++;
2203 		}
2204 	}
2205 
2206 	option = tracecmd_add_option_v(handle, id, vect, j);
2207 	free(vect);
2208 	free(cpu_ids);
2209 
2210 	return option;
2211 }
2212 
tracecmd_create_file_latency(const char * output_file,int cpus,int file_version,const char * compression)2213 struct tracecmd_output *tracecmd_create_file_latency(const char *output_file, int cpus,
2214 						     int file_version, const char *compression)
2215 {
2216 	enum tracecmd_section_flags flags = 0;
2217 	struct tracecmd_output *handle;
2218 	tsize_t offset;
2219 	char *path;
2220 
2221 	handle = tracecmd_output_create(output_file);
2222 	if (!handle)
2223 		return NULL;
2224 
2225 	if (file_version && tracecmd_output_set_version(handle, file_version))
2226 		goto out_free;
2227 
2228 	if (compression) {
2229 		if (tracecmd_output_set_compression(handle, compression))
2230 			goto out_free;
2231 	} else if (file_version >= FILE_VERSION_COMPRESSION) {
2232 		tracecmd_output_set_compression(handle, "any");
2233 	}
2234 
2235 	if (tracecmd_output_write_headers(handle, NULL))
2236 		goto out_free;
2237 	/*
2238 	 * Save the command lines;
2239 	 */
2240 	if (tracecmd_write_cmdlines(handle) < 0)
2241 		goto out_free;
2242 
2243 	if (tracecmd_write_cpus(handle, cpus) < 0)
2244 		goto out_free;
2245 	if (tracecmd_write_buffer_info(handle) < 0)
2246 		goto out_free;
2247 	if (tracecmd_write_options(handle) < 0)
2248 		goto out_free;
2249 
2250 	if (!check_out_state(handle, TRACECMD_FILE_CPU_LATENCY)) {
2251 		tracecmd_warning("Cannot write latency data into the file, unexpected state 0x%X",
2252 				 handle->file_state);
2253 		goto out_free;
2254 	}
2255 
2256 	if (!HAS_SECTIONS(handle) && do_write_check(handle, "latency  ", 10))
2257 		goto out_free;
2258 
2259 	path = get_tracing_file(handle, "trace");
2260 	if (!path)
2261 		goto out_free;
2262 
2263 	offset = do_lseek(handle, 0, SEEK_CUR);
2264 	if (HAS_SECTIONS(handle) &&
2265 	    !out_add_buffer_option(handle, "", TRACECMD_OPTION_BUFFER_TEXT,
2266 				   offset, 0, NULL, getpagesize()))
2267 		goto out_free;
2268 	if (handle->compress)
2269 		flags |= TRACECMD_SEC_FL_COMPRESS;
2270 
2271 	offset = out_write_section_header(handle, TRACECMD_OPTION_BUFFER_TEXT,
2272 					  "buffer latency", flags, false);
2273 
2274 	copy_file_compress(handle, path, NULL);
2275 	if (out_update_section_header(handle, offset))
2276 		goto out_free;
2277 
2278 	put_tracing_file(path);
2279 
2280 	handle->file_state = TRACECMD_FILE_CPU_LATENCY;
2281 
2282 	if (HAS_SECTIONS(handle))
2283 		tracecmd_write_options(handle);
2284 
2285 	return handle;
2286 
2287 out_free:
2288 	tracecmd_output_close(handle);
2289 	return NULL;
2290 }
2291 
save_clock(struct tracecmd_output * handle,char * clock)2292 static int save_clock(struct tracecmd_output *handle, char *clock)
2293 {
2294 	unsigned long long endian8;
2295 	char *str = NULL;
2296 	int ret;
2297 
2298 	ret = asprintf(&str, "[%s]", clock);
2299 	if (ret < 0)
2300 		return -1;
2301 
2302 	endian8 = convert_endian_8(handle, strlen(str));
2303 	ret = do_write_check(handle, &endian8, 8);
2304 	if (ret)
2305 		goto out;
2306 	ret = do_write_check(handle, str, strlen(str));
2307 
2308 out:
2309 	free(str);
2310 	return ret;
2311 }
2312 
update_buffer_cpu_offset_v6(struct tracecmd_output * handle,const char * name,tsize_t offset)2313 static int update_buffer_cpu_offset_v6(struct tracecmd_output *handle,
2314 				       const char *name, tsize_t offset)
2315 {
2316 	tsize_t b_offset;
2317 	tsize_t current;
2318 
2319 	if (!name)
2320 		name = "";
2321 
2322 	b_offset = get_buffer_file_offset(handle, name);
2323 	if (!b_offset) {
2324 		tracecmd_warning("Cannot find description for buffer %s", name);
2325 		return -1;
2326 	}
2327 
2328 	current = do_lseek(handle, 0, SEEK_CUR);
2329 
2330 	/* Go to the option data, where will write the offest */
2331 	if (do_lseek(handle, b_offset, SEEK_SET) == (off64_t)-1) {
2332 		tracecmd_warning("could not seek to %lld", b_offset);
2333 		return -1;
2334 	}
2335 
2336 	if (do_write_check(handle, &offset, 8))
2337 		return -1;
2338 
2339 	/* Go back to end of file */
2340 	if (do_lseek(handle, current, SEEK_SET) == (off64_t)-1) {
2341 		tracecmd_warning("could not seek to %lld", offset);
2342 		return -1;
2343 	}
2344 	return 0;
2345 }
2346 
out_write_emty_cpu_data(struct tracecmd_output * handle,int cpus)2347 __hidden int out_write_emty_cpu_data(struct tracecmd_output *handle, int cpus)
2348 {
2349 	unsigned long long zero = 0;
2350 	char *clock;
2351 	int ret;
2352 	int i;
2353 
2354 	if (HAS_SECTIONS(handle))
2355 		return 0;
2356 
2357 	ret = handle->file_state == TRACECMD_FILE_CPU_FLYRECORD ? 0 :
2358 				    check_file_state(handle->file_version,
2359 						     handle->file_state,
2360 						     TRACECMD_FILE_CPU_FLYRECORD);
2361 	if (ret < 0) {
2362 		tracecmd_warning("Cannot write trace data into the file, unexpected state 0x%X",
2363 				 handle->file_state);
2364 		return ret;
2365 	}
2366 
2367 	if (do_write_check(handle, "flyrecord", 10))
2368 		return -1;
2369 
2370 	for (i = 0; i < cpus; i++) {
2371 		/* Write 0 for trace data offset and size */
2372 		if (do_write_check(handle, &zero, 8))
2373 			return -1;
2374 
2375 		if (do_write_check(handle, &zero, 8))
2376 			return -1;
2377 	}
2378 	clock = get_clock(handle);
2379 	if (clock && save_clock(handle, clock))
2380 		return -1;
2381 
2382 	handle->file_state = TRACECMD_FILE_CPU_FLYRECORD;
2383 	return 0;
2384 }
2385 
out_write_cpu_data(struct tracecmd_output * handle,int cpus,struct cpu_data_source * data,const char * buff_name)2386 __hidden int out_write_cpu_data(struct tracecmd_output *handle,
2387 				int cpus, struct cpu_data_source *data, const char *buff_name)
2388 {
2389 	struct data_file_write *data_files = NULL;
2390 	enum tracecmd_section_flags flags = 0;
2391 	tsize_t data_offs, offset;
2392 	unsigned long long endian8;
2393 	unsigned long long read_size;
2394 	int page_size;
2395 	char *clock;
2396 	char *str;
2397 	int ret;
2398 	int i;
2399 
2400 	/* This can be called multiple times (when recording instances) */
2401 	ret = handle->file_state == TRACECMD_FILE_CPU_FLYRECORD ? 0 :
2402 				    check_file_state(handle->file_version,
2403 						     handle->file_state,
2404 						     TRACECMD_FILE_CPU_FLYRECORD);
2405 	if (ret < 0) {
2406 		tracecmd_warning("Cannot write trace data into the file, unexpected state 0x%X",
2407 				 handle->file_state);
2408 		goto out_free;
2409 	}
2410 
2411 	if (*buff_name == '\0')
2412 		page_size = handle->page_size;
2413 	else
2414 		page_size = get_trace_page_size(handle, buff_name);
2415 
2416 	data_offs = do_lseek(handle, 0, SEEK_CUR);
2417 	if (!HAS_SECTIONS(handle) && do_write_check(handle, "flyrecord", 10))
2418 		goto out_free;
2419 
2420 	if (handle->compress)
2421 		flags |= TRACECMD_SEC_FL_COMPRESS;
2422 	if (asprintf(&str, "buffer flyrecord %s", buff_name) < 1)
2423 		goto out_free;
2424 	offset = out_write_section_header(handle, TRACECMD_OPTION_BUFFER, str, flags, false);
2425 	free(str);
2426 	if (offset == (off_t)-1)
2427 		goto out_free;
2428 
2429 	data_files = calloc(cpus, sizeof(*data_files));
2430 	if (!data_files)
2431 		goto out_free;
2432 
2433 	for (i = 0; i < cpus; i++) {
2434 		data_files[i].file_size = data[i].size;
2435 		/*
2436 		 * Place 0 for the data offset and size, and save the offsets to
2437 		 * updated them with the correct data later.
2438 		 */
2439 		if (!HAS_SECTIONS(handle)) {
2440 			endian8 = 0;
2441 			data_files[i].file_data_offset = do_lseek(handle, 0, SEEK_CUR);
2442 			if (do_write_check(handle, &endian8, 8))
2443 				goto out_free;
2444 			data_files[i].file_write_size = do_lseek(handle, 0, SEEK_CUR);
2445 			if (do_write_check(handle, &endian8, 8))
2446 				goto out_free;
2447 		}
2448 	}
2449 
2450 	if (!HAS_SECTIONS(handle)) {
2451 		update_buffer_cpu_offset_v6(handle, buff_name, data_offs);
2452 		clock = get_clock(handle);
2453 		if (clock && save_clock(handle, clock))
2454 			goto out_free;
2455 	}
2456 
2457 	for (i = 0; i < cpus; i++) {
2458 		data_files[i].data_offset = do_lseek(handle, 0, SEEK_CUR);
2459 		/* Page align offset */
2460 		data_files[i].data_offset += page_size - 1;
2461 		data_files[i].data_offset &= ~(page_size - 1);
2462 
2463 		ret = do_lseek(handle, data_files[i].data_offset, SEEK_SET);
2464 		if (ret == (off64_t)-1)
2465 			goto out_free;
2466 
2467 		if (!tracecmd_get_quiet(handle))
2468 			fprintf(stderr, "CPU%d data recorded at offset=0x%llx\n",
2469 				i, (unsigned long long)data_files[i].data_offset);
2470 
2471 		if (data[i].size) {
2472 			if (lseek64(data[i].fd, data[i].offset, SEEK_SET) == (off64_t)-1)
2473 				goto out_free;
2474 			read_size = out_copy_fd_compress(handle, data[i].fd,
2475 							 data[i].size, &data_files[i].write_size,
2476 							 page_size);
2477 
2478 			if (read_size != data_files[i].file_size) {
2479 				errno = EINVAL;
2480 				tracecmd_warning("did not match size of %lld to %lld",
2481 						 read_size, data_files[i].file_size);
2482 				goto out_free;
2483 			}
2484 		} else {
2485 			data_files[i].write_size = 0;
2486 		}
2487 
2488 		if (!HAS_SECTIONS(handle)) {
2489 			/* Write the real CPU data offset in the file */
2490 			if (do_lseek(handle, data_files[i].file_data_offset, SEEK_SET) == (off64_t)-1)
2491 				goto out_free;
2492 			endian8 = convert_endian_8(handle, data_files[i].data_offset);
2493 			if (do_write_check(handle, &endian8, 8))
2494 				goto out_free;
2495 			/* Write the real CPU data size in the file */
2496 			if (do_lseek(handle, data_files[i].file_write_size, SEEK_SET) == (off64_t)-1)
2497 				goto out_free;
2498 			endian8 = convert_endian_8(handle, data_files[i].write_size);
2499 			if (do_write_check(handle, &endian8, 8))
2500 				goto out_free;
2501 			offset = data_files[i].data_offset + data_files[i].write_size;
2502 			if (do_lseek(handle, offset, SEEK_SET) == (off64_t)-1)
2503 				goto out_free;
2504 		}
2505 		if (!tracecmd_get_quiet(handle)) {
2506 			fprintf(stderr, "    %llu bytes in size",
2507 				(unsigned long long)data_files[i].write_size);
2508 			if (flags & TRACECMD_SEC_FL_COMPRESS)
2509 				fprintf(stderr, " (%llu uncompressed)",
2510 					(unsigned long long)data_files[i].file_size);
2511 			fprintf(stderr, "\n");
2512 		}
2513 	}
2514 
2515 	if (HAS_SECTIONS(handle) &&
2516 	    !out_add_buffer_option(handle, buff_name,  TRACECMD_OPTION_BUFFER,
2517 				   data_offs, cpus, data_files, page_size))
2518 		goto out_free;
2519 
2520 	free(data_files);
2521 	if (do_lseek(handle, 0, SEEK_END) == (off64_t)-1)
2522 		return -1;
2523 
2524 	if (out_update_section_header(handle, offset))
2525 		goto out_free;
2526 
2527 	handle->file_state = TRACECMD_FILE_CPU_FLYRECORD;
2528 
2529 	if (HAS_SECTIONS(handle))
2530 		tracecmd_write_options(handle);
2531 
2532 	return 0;
2533 
2534  out_free:
2535 	do_lseek(handle, 0, SEEK_END);
2536 	free(data_files);
2537 	return -1;
2538 }
2539 
tracecmd_write_cpu_data(struct tracecmd_output * handle,int cpus,char * const * cpu_data_files,const char * buff_name)2540 int tracecmd_write_cpu_data(struct tracecmd_output *handle,
2541 			    int cpus, char * const *cpu_data_files, const char *buff_name)
2542 {
2543 	struct cpu_data_source *data;
2544 	struct stat st;
2545 	int size = 0;
2546 	int ret;
2547 	int i;
2548 
2549 	if (!buff_name)
2550 		buff_name = "";
2551 
2552 	data = calloc(cpus, sizeof(struct cpu_data_source));
2553 	if (!data)
2554 		return -1;
2555 
2556 	for (i = 0; i < cpus; i++) {
2557 		ret = stat(cpu_data_files[i], &st);
2558 		if (ret < 0) {
2559 			tracecmd_warning("can not stat '%s'", cpu_data_files[i]);
2560 			break;
2561 		}
2562 		data[i].fd = open(cpu_data_files[i], O_RDONLY);
2563 		if (data[i].fd < 0) {
2564 			tracecmd_warning("Can't read '%s'", data[i].fd);
2565 			break;
2566 		}
2567 
2568 		data[i].size = st.st_size;
2569 		data[i].offset = 0;
2570 		size += st.st_size;
2571 	}
2572 
2573 	if (i < cpus)
2574 		ret = -1;
2575 	else
2576 		ret = out_write_cpu_data(handle, cpus, data, buff_name);
2577 
2578 	for (i--; i >= 0; i--)
2579 		close(data[i].fd);
2580 
2581 	free(data);
2582 	return ret;
2583 }
2584 
tracecmd_append_cpu_data(struct tracecmd_output * handle,int cpus,char * const * cpu_data_files)2585 int tracecmd_append_cpu_data(struct tracecmd_output *handle,
2586 			     int cpus, char * const *cpu_data_files)
2587 {
2588 	int ret;
2589 
2590 	ret = tracecmd_write_cpus(handle, cpus);
2591 	if (ret)
2592 		return ret;
2593 	ret = tracecmd_write_buffer_info(handle);
2594 	if (ret)
2595 		return ret;
2596 	ret = tracecmd_write_options(handle);
2597 	if (ret)
2598 		return ret;
2599 
2600 	return tracecmd_write_cpu_data(handle, cpus, cpu_data_files, NULL);
2601 }
2602 
tracecmd_append_buffer_cpu_data(struct tracecmd_output * handle,const char * name,int cpus,char * const * cpu_data_files)2603 int tracecmd_append_buffer_cpu_data(struct tracecmd_output *handle,
2604 				    const char *name, int cpus, char * const *cpu_data_files)
2605 {
2606 	return tracecmd_write_cpu_data(handle, cpus, cpu_data_files, name);
2607 }
2608 
tracecmd_get_output_handle_fd(int fd)2609 struct tracecmd_output *tracecmd_get_output_handle_fd(int fd)
2610 {
2611 	struct tracecmd_output *handle = NULL;
2612 	struct tracecmd_input *ihandle;
2613 	const char *cname = NULL;
2614 	const char *cver = NULL;
2615 	int fd2;
2616 
2617 	/* Move the file descriptor to the beginning */
2618 	if (lseek(fd, 0, SEEK_SET) == (off_t)-1)
2619 		return NULL;
2620 
2621 	/* dup fd to be used by the ihandle bellow */
2622 	fd2 = dup(fd);
2623 	if (fd2 < 0)
2624 		return NULL;
2625 
2626 	/* get a input handle from this */
2627 	ihandle = tracecmd_alloc_fd(fd2, TRACECMD_FL_LOAD_NO_PLUGINS);
2628 	if (!ihandle)
2629 		return NULL;
2630 	tracecmd_read_headers(ihandle, 0);
2631 
2632 	/* move the file descriptor to the end */
2633 	if (lseek(fd, 0, SEEK_END) == (off_t)-1)
2634 		goto out_free;
2635 
2636 	/* create a partial output handle */
2637 	handle = calloc(1, sizeof(*handle));
2638 	if (!handle)
2639 		goto out_free;
2640 
2641 	handle->fd = fd;
2642 
2643 	/* get tep, state, endian and page size */
2644 	handle->file_state = tracecmd_get_file_state(ihandle);
2645 	/* Use the tep of the ihandle for later writes */
2646 	handle->pevent = tracecmd_get_tep(ihandle);
2647 	tep_ref(handle->pevent);
2648 	handle->page_size = tracecmd_page_size(ihandle);
2649 	handle->file_version = tracecmd_get_in_file_version(ihandle);
2650 	handle->options_start = get_last_option_offset(ihandle);
2651 	handle->strings_offs = get_meta_strings_size(ihandle);
2652 	list_head_init(&handle->options);
2653 	list_head_init(&handle->buffers);
2654 
2655 	if (!tracecmd_get_file_compress_proto(ihandle, &cname, &cver)) {
2656 		handle->compress = tracecmd_compress_alloc(cname, cver, handle->fd,
2657 							   handle->pevent, handle->msg_handle);
2658 		if (!handle->compress)
2659 			goto out_free;
2660 	}
2661 	tracecmd_close(ihandle);
2662 
2663 	return handle;
2664 
2665  out_free:
2666 	tracecmd_close(ihandle);
2667 	free(handle);
2668 	return NULL;
2669 }
2670 
2671 /**
2672  * tracecmd_output_create - Create new output handle to a trace file with given name
2673  * @output_file: Name of the trace file that will be created.
2674  *
2675  * The @output_file parameter can be NULL. In this case the output handle is created
2676  * and initialized, but is not associated with a file.
2677  *
2678  * Returns pointer to created outpuy handle, or NULL in case of an error.
2679  */
tracecmd_output_create(const char * output_file)2680 struct tracecmd_output *tracecmd_output_create(const char *output_file)
2681 {
2682 	struct tracecmd_output *out;
2683 	int fd = -1;
2684 
2685 	if (output_file) {
2686 		fd = open(output_file, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
2687 		if (fd < 0)
2688 			return NULL;
2689 	}
2690 	out = tracecmd_output_create_fd(fd);
2691 	if (!out && fd >= 0) {
2692 		close(fd);
2693 		unlink(output_file);
2694 	}
2695 
2696 	return out;
2697 }
2698 
2699 /**
2700  * tracecmd_copy - copy the headers of one trace.dat file for another
2701  * @ihandle: input handle of the trace.dat file to copy
2702  * @file: the trace.dat file to create
2703  * @state: what data will be copied from the source handle
2704  * @file_version: version of the output file
2705  * @compression: compression of the output file, can be one of:
2706  *		 NULL - inherit compression from the input file
2707  *		 "any" - compress the output file with the best available algorithm
2708  *		 "none" - do not compress the output file
2709  *		 algorithm_name - compress the output file with specified algorithm
2710  *
2711  * Reads the header information and creates a new trace data file
2712  * with the same characteristics (events and all) and returns
2713  * tracecmd_output handle to this new file.
2714  */
tracecmd_copy(struct tracecmd_input * ihandle,const char * file,enum tracecmd_file_states state,int file_version,const char * compression)2715 struct tracecmd_output *tracecmd_copy(struct tracecmd_input *ihandle, const char *file,
2716 				      enum tracecmd_file_states state, int file_version,
2717 				      const char *compression)
2718 {
2719 	enum tracecmd_file_states fstate;
2720 	struct tracecmd_output *handle;
2721 
2722 	handle = tracecmd_output_create(file);
2723 	if (!handle)
2724 		return NULL;
2725 
2726 	if (tracecmd_output_set_from_input(handle, ihandle))
2727 		goto out_free;
2728 
2729 	if (file_version >= FILE_VERSION_MIN)
2730 		tracecmd_output_set_version(handle, file_version);
2731 
2732 	if (compression && tracecmd_output_set_compression(handle, compression))
2733 		goto out_free;
2734 
2735 	output_write_init(handle);
2736 	fstate = state > TRACECMD_FILE_CPU_COUNT ? TRACECMD_FILE_CPU_COUNT : state;
2737 	if (tracecmd_copy_headers(ihandle, handle, 0, fstate) < 0)
2738 		goto out_free;
2739 
2740 	if (tracecmd_copy_buffer_descr(ihandle, handle) < 0)
2741 		goto out_free;
2742 
2743 	if (state >= TRACECMD_FILE_OPTIONS &&
2744 	    tracecmd_copy_options(ihandle, handle) < 0)
2745 		goto out_free;
2746 
2747 	if (state >= TRACECMD_FILE_CPU_LATENCY &&
2748 	    tracecmd_copy_trace_data(ihandle, handle) < 0)
2749 		goto out_free;
2750 
2751 	if (HAS_SECTIONS(handle))
2752 		tracecmd_write_options(handle);
2753 
2754 	/* The file is all ready to have cpu data attached */
2755 	return handle;
2756 
2757 out_free:
2758 	if (handle)
2759 		tracecmd_output_close(handle);
2760 
2761 	unlink(file);
2762 	return NULL;
2763 }
2764 
out_set_file_state(struct tracecmd_output * handle,int new_state)2765 __hidden void out_set_file_state(struct tracecmd_output *handle, int new_state)
2766 {
2767 	handle->file_state = new_state;
2768 }
2769 
check_out_state(struct tracecmd_output * handle,int new_state)2770 __hidden bool check_out_state(struct tracecmd_output *handle, int new_state)
2771 {
2772 	return check_file_state(handle->file_version, handle->file_state, new_state);
2773 }
2774 
out_check_compression(struct tracecmd_output * handle)2775 __hidden bool out_check_compression(struct tracecmd_output *handle)
2776 {
2777 	return (handle->compress != NULL);
2778 }
2779 
out_save_options_offset(struct tracecmd_output * handle,unsigned long long start)2780 __hidden int out_save_options_offset(struct tracecmd_output *handle, unsigned long long start)
2781 {
2782 	unsigned long long new, en8;
2783 
2784 	if (HAS_SECTIONS(handle)) {
2785 		/* Append to the previous options section, if any */
2786 		if (!handle->options_start)
2787 			return -1;
2788 
2789 		new = do_lseek(handle, 0, SEEK_CUR);
2790 		if (do_lseek(handle, handle->options_start, SEEK_SET) == (off64_t)-1)
2791 			return -1;
2792 
2793 		en8 = convert_endian_8(handle, start);
2794 		if (do_write_check(handle, &en8, 8))
2795 			return -1;
2796 
2797 		handle->options_start = new;
2798 		if (do_lseek(handle, new, SEEK_SET) == (off64_t)-1)
2799 			return -1;
2800 	} else {
2801 		handle->options_start = start;
2802 	}
2803 
2804 	return 0;
2805 }
2806 
2807 /**
2808  * tracecmd_get_out_file_version - return the trace.dat file version
2809  * @handle: output handle for the trace.dat file
2810  */
tracecmd_get_out_file_version(struct tracecmd_output * handle)2811 unsigned long tracecmd_get_out_file_version(struct tracecmd_output *handle)
2812 {
2813 	return handle->file_version;
2814 }
2815 
tracecmd_get_out_file_offset(struct tracecmd_output * handle)2816 unsigned long long tracecmd_get_out_file_offset(struct tracecmd_output *handle)
2817 {
2818 	return do_lseek(handle, 0, SEEK_CUR);
2819 }
2820