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