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