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