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 <stdbool.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <sys/mman.h>
11 #include <regex.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #include <errno.h>
15
16 #include <linux/time64.h>
17
18 #include "trace-write-local.h"
19 #include "trace-cmd-local.h"
20 #include "trace-local.h"
21 #include "kbuffer.h"
22 #include "list.h"
23
24 #define _STRINGIFY(x) #x
25 #define STRINGIFY(x) _STRINGIFY(x)
26
27 #define MISSING_EVENTS (1 << 31)
28 #define MISSING_STORED (1 << 30)
29
30 #define COMMIT_MASK ((1 << 27) - 1)
31
32 /* force uncompressing in memory */
33 #define INMEMORY_DECOMPRESS
34
35 /* for debugging read instead of mmap */
36 static int force_read = 0;
37
38 struct page_map {
39 struct list_head list;
40 off64_t offset;
41 off64_t size;
42 void *map;
43 int ref_count;
44 };
45
46 struct page {
47 struct list_head list;
48 off64_t offset;
49 struct tracecmd_input *handle;
50 struct page_map *page_map;
51 void *map;
52 int ref_count;
53 int cpu;
54 long long lost_events;
55 #if DEBUG_RECORD
56 struct tep_record *records;
57 #endif
58 };
59
60 struct zchunk_cache {
61 struct list_head list;
62 struct tracecmd_compress_chunk *chunk;
63 void *map;
64 int ref;
65 };
66
67 struct cpu_zdata {
68 /* uncompressed cpu data */
69 int fd;
70 #ifdef __ANDROID__
71 char file[37]; /* strlen(COMPR_TEMP_FILE) */
72 #else /* !__ANDROID__ */
73 char file[26]; /* strlen(COMPR_TEMP_FILE) */
74 #endif /* __ANDROID__ */
75
76 unsigned int count;
77 unsigned int last_chunk;
78 struct list_head cache;
79 struct tracecmd_compress_chunk *chunks;
80 };
81
82 #ifdef __ANDROID__
83 #define COMPR_TEMP_FILE "/data/local/tmp/trace_cpu_dataXXXXXX"
84 #else /* !__ANDROID__ */
85 #define COMPR_TEMP_FILE "/tmp/trace_cpu_dataXXXXXX"
86 #endif /* __ANDROID__ */
87
88 struct cpu_data {
89 /* the first two never change */
90 unsigned long long file_offset;
91 unsigned long long file_size;
92 unsigned long long offset;
93 unsigned long long size;
94 unsigned long long timestamp;
95 unsigned long long first_ts;
96 struct list_head page_maps;
97 struct page_map *page_map;
98 struct page **pages;
99 struct tep_record *next;
100 struct page *page;
101 struct kbuffer *kbuf;
102 int nr_pages;
103 int page_cnt;
104 int cpu;
105 int pipe_fd;
106 struct cpu_zdata compress;
107 };
108
109 struct cpu_file_data {
110 int cpu;
111 unsigned long long offset;
112 unsigned long long size;
113 };
114
115 struct input_buffer_instance {
116 char *name;
117 size_t offset;
118 char *clock;
119 bool latency;
120 int page_size;
121 int cpus;
122 struct cpu_file_data *cpu_data;
123 };
124
125 struct ts_offset_sample {
126 long long time;
127 long long offset;
128 long long scaling;
129 long long fraction;
130 };
131
132 struct guest_trace_info {
133 struct guest_trace_info *next;
134 char *name;
135 unsigned long long trace_id;
136 int vcpu_count;
137 int *cpu_pid;
138 };
139
140 struct timesync_offsets {
141 int ts_samples_count;
142 struct ts_offset_sample *ts_samples;
143 };
144
145 struct host_trace_info {
146 unsigned long long peer_trace_id;
147 unsigned int flags;
148 bool sync_enable;
149 int ts_samples_count;
150 struct ts_offset_sample *ts_samples;
151 int cpu_count;
152 struct timesync_offsets *ts_offsets;
153 };
154
155 struct tsc2nsec {
156 int mult;
157 int shift;
158 unsigned long long offset;
159 };
160
161 struct file_section {
162 unsigned long long section_offset;
163 unsigned long long data_offset;
164 int id;
165 int flags;
166 struct file_section *next;
167 };
168
169 struct tracecmd_input {
170 struct tep_handle *pevent;
171 struct tep_plugin_list *plugin_list;
172 struct tracecmd_input *parent;
173 unsigned long file_state;
174 unsigned long long trace_id;
175 unsigned long long next_offset;
176 unsigned long flags;
177 int fd;
178 int long_size;
179 int page_size;
180 int page_map_size;
181 int max_cpu;
182 int cpus;
183 int ref;
184 int nr_buffers; /* buffer instances */
185 bool use_trace_clock;
186 bool read_page;
187 bool use_pipe;
188 bool read_zpage; /* uncompress pages in memory, do not use tmp files */
189 bool cpu_compressed;
190 int file_version;
191 unsigned int cpustats_size;
192 struct cpu_zdata latz;
193 struct cpu_data *cpu_data;
194 long long ts_offset;
195 struct tsc2nsec tsc_calc;
196
197 unsigned int strings_size; /* size of the metadata strings */
198 char *strings; /* metadata strings */
199
200 bool read_compress;
201 struct tracecmd_compression *compress;
202
203 struct host_trace_info host;
204 double ts2secs;
205 char * cpustats;
206 char * uname;
207 char * version;
208 char * trace_clock;
209 struct input_buffer_instance top_buffer;
210 struct input_buffer_instance *buffers;
211 int parsing_failures;
212 struct guest_trace_info *guest;
213
214 struct tracecmd_ftrace finfo;
215
216 struct hook_list *hooks;
217 struct pid_addr_maps *pid_maps;
218 /* file information */
219 struct file_section *sections;
220 bool options_init;
221 unsigned long long options_start;
222 unsigned long long options_last_offset;
223 size_t total_file_size;
224
225 /* For custom profilers. */
226 tracecmd_show_data_func show_data_func;
227 };
228
229 __thread struct tracecmd_input *tracecmd_curr_thread_handle;
230
231 #define CHECK_READ_STATE(H, S) ((H)->file_version < FILE_VERSION_SECTIONS && (H)->file_state >= (S))
232 #define HAS_SECTIONS(H) ((H)->flags & TRACECMD_FL_SECTIONED)
233 #define HAS_COMPRESSION(H) ((H)->flags & TRACECMD_FL_COMPRESSION)
234
235 static int read_options_type(struct tracecmd_input *handle);
236
tracecmd_set_flag(struct tracecmd_input * handle,int flag)237 void tracecmd_set_flag(struct tracecmd_input *handle, int flag)
238 {
239 handle->flags |= flag;
240 }
241
tracecmd_clear_flag(struct tracecmd_input * handle,int flag)242 void tracecmd_clear_flag(struct tracecmd_input *handle, int flag)
243 {
244 handle->flags &= ~flag;
245 }
246
tracecmd_get_flags(struct tracecmd_input * handle)247 unsigned long tracecmd_get_flags(struct tracecmd_input *handle)
248 {
249 return handle->flags;
250 }
251
tracecmd_get_file_state(struct tracecmd_input * handle)252 enum tracecmd_file_states tracecmd_get_file_state(struct tracecmd_input *handle)
253 {
254 return handle->file_state;
255 }
256
257 #if DEBUG_RECORD
remove_record(struct page * page,struct tep_record * record)258 static void remove_record(struct page *page, struct tep_record *record)
259 {
260 if (record->prev)
261 record->prev->next = record->next;
262 else
263 page->records = record->next;
264 if (record->next)
265 record->next->prev = record->prev;
266 }
add_record(struct page * page,struct tep_record * record)267 static void add_record(struct page *page, struct tep_record *record)
268 {
269 if (page->records)
270 page->records->prev = record;
271 record->next = page->records;
272 record->prev = NULL;
273 page->records = record;
274 }
show_records(struct page ** pages,int nr_pages)275 static const char *show_records(struct page **pages, int nr_pages)
276 {
277 static char buf[BUFSIZ + 1];
278 struct tep_record *record;
279 struct page *page;
280 int len;
281 int i;
282
283 memset(buf, 0, sizeof(buf));
284 len = 0;
285 for (i = 0; i < nr_pages; i++) {
286 page = pages[i];
287 if (!page)
288 continue;
289 for (record = page->records; record; record = record->next) {
290 int n;
291 n = snprintf(buf+len, BUFSIZ - len, " 0x%lx", record->alloc_addr);
292 len += n;
293 if (len >= BUFSIZ)
294 break;
295 }
296 }
297 return buf;
298 }
299 #else
remove_record(struct page * page,struct tep_record * record)300 static inline void remove_record(struct page *page, struct tep_record *record) {}
add_record(struct page * page,struct tep_record * record)301 static inline void add_record(struct page *page, struct tep_record *record) {}
show_records(struct page ** pages,int nr_pages)302 static const char *show_records(struct page **pages, int nr_pages)
303 {
304 return "";
305 }
306 #endif
307
308 static int init_cpu(struct tracecmd_input *handle, int cpu);
309
do_read_fd(int fd,void * data,size_t size)310 static ssize_t do_read_fd(int fd, void *data, size_t size)
311 {
312 ssize_t tot = 0;
313 ssize_t r;
314
315 do {
316 r = read(fd, data + tot, size - tot);
317 tot += r;
318
319 if (!r)
320 break;
321 if (r < 0)
322 return r;
323 } while (tot != size);
324
325 return tot;
326 }
327
do_lseek(struct tracecmd_input * handle,int offset,int whence)328 static inline int do_lseek(struct tracecmd_input *handle, int offset, int whence)
329 {
330 if (handle->read_compress)
331 return tracecmd_compress_lseek(handle->compress, offset, whence);
332 else
333 return lseek(handle->fd, offset, whence);
334 }
335
do_read(struct tracecmd_input * handle,void * data,size_t size)336 static inline ssize_t do_read(struct tracecmd_input *handle, void *data, size_t size)
337 {
338 if (handle->read_compress)
339 return tracecmd_compress_buffer_read(handle->compress, data, size);
340 else
341 return do_read_fd(handle->fd, data, size);
342 }
343
344 static ssize_t
do_read_check(struct tracecmd_input * handle,void * data,size_t size)345 do_read_check(struct tracecmd_input *handle, void *data, size_t size)
346 {
347 ssize_t ret;
348
349 ret = do_read(handle, data, size);
350 if (ret < 0)
351 return ret;
352 if (ret != size)
353 return -1;
354
355 return 0;
356 }
357
read_string(struct tracecmd_input * handle)358 static char *read_string(struct tracecmd_input *handle)
359 {
360 char buf[BUFSIZ];
361 char *str = NULL;
362 size_t size = 0;
363 ssize_t i;
364 ssize_t r;
365
366 for (;;) {
367 r = do_read(handle, buf, BUFSIZ);
368 if (r <= 0)
369 goto fail;
370
371 for (i = 0; i < r; i++) {
372 if (!buf[i])
373 break;
374 }
375 if (i < r)
376 break;
377
378 if (str) {
379 size += BUFSIZ;
380 str = realloc(str, size);
381 if (!str)
382 return NULL;
383 memcpy(str + (size - BUFSIZ), buf, BUFSIZ);
384 } else {
385 size = BUFSIZ;
386 str = malloc(size);
387 if (!str)
388 return NULL;
389 memcpy(str, buf, size);
390 }
391 }
392
393 /* move the file descriptor to the end of the string */
394 r = do_lseek(handle, -(r - (i+1)), SEEK_CUR);
395 if (r < 0)
396 goto fail;
397
398 if (str) {
399 size += i + 1;
400 str = realloc(str, size);
401 if (!str)
402 return NULL;
403 memcpy(str + (size - i), buf, i);
404 str[size] = 0;
405 } else {
406 size = i + 1;
407 str = malloc(size);
408 if (!str)
409 return NULL;
410 memcpy(str, buf, i);
411 str[i] = 0;
412 }
413
414 return str;
415
416 fail:
417 if (str)
418 free(str);
419 return NULL;
420 }
421
read2(struct tracecmd_input * handle,unsigned short * size)422 static int read2(struct tracecmd_input *handle, unsigned short *size)
423 {
424 struct tep_handle *pevent = handle->pevent;
425 unsigned short data;
426
427 if (do_read_check(handle, &data, 2))
428 return -1;
429
430 *size = tep_read_number(pevent, &data, 2);
431 return 0;
432 }
433
read4(struct tracecmd_input * handle,unsigned int * size)434 static int read4(struct tracecmd_input *handle, unsigned int *size)
435 {
436 struct tep_handle *pevent = handle->pevent;
437 unsigned int data;
438
439 if (do_read_check(handle, &data, 4))
440 return -1;
441
442 *size = tep_read_number(pevent, &data, 4);
443 return 0;
444 }
445
read8(struct tracecmd_input * handle,unsigned long long * size)446 static int read8(struct tracecmd_input *handle, unsigned long long *size)
447 {
448 struct tep_handle *pevent = handle->pevent;
449 unsigned long long data;
450
451 if (do_read_check(handle, &data, 8))
452 return -1;
453
454 *size = tep_read_number(pevent, &data, 8);
455 return 0;
456 }
457
in_uncompress_reset(struct tracecmd_input * handle)458 __hidden void in_uncompress_reset(struct tracecmd_input *handle)
459 {
460 if (handle->compress) {
461 handle->read_compress = false;
462 tracecmd_compress_reset(handle->compress);
463 }
464 }
465
in_uncompress_block(struct tracecmd_input * handle)466 __hidden int in_uncompress_block(struct tracecmd_input *handle)
467 {
468 int ret = 0;
469
470 if (handle->compress) {
471 ret = tracecmd_uncompress_block(handle->compress);
472 if (!ret)
473 handle->read_compress = true;
474 }
475 return ret;
476 }
477
section_get(struct tracecmd_input * handle,int id)478 static struct file_section *section_get(struct tracecmd_input *handle, int id)
479 {
480 struct file_section *sec;
481
482 for (sec = handle->sections; sec; sec = sec->next) {
483 if (sec->id == id)
484 return sec;
485 }
486
487 return NULL;
488 }
489
section_open(struct tracecmd_input * handle,int id)490 static struct file_section *section_open(struct tracecmd_input *handle, int id)
491 {
492 struct file_section *sec = section_get(handle, id);
493
494 if (!sec)
495 return NULL;
496
497 if (lseek64(handle->fd, sec->data_offset, SEEK_SET) == (off64_t)-1)
498 return NULL;
499
500 if ((sec->flags & TRACECMD_SEC_FL_COMPRESS) && in_uncompress_block(handle))
501 return NULL;
502
503 return sec;
504 }
505
section_close(struct tracecmd_input * handle,struct file_section * sec)506 static void section_close(struct tracecmd_input *handle, struct file_section *sec)
507 {
508 if (sec->flags & TRACECMD_SEC_FL_COMPRESS)
509 in_uncompress_reset(handle);
510 }
511
section_add_or_update(struct tracecmd_input * handle,int id,int flags,unsigned long long section_offset,unsigned long long data_offset)512 static int section_add_or_update(struct tracecmd_input *handle, int id, int flags,
513 unsigned long long section_offset,
514 unsigned long long data_offset)
515 {
516 struct file_section *sec = section_get(handle, id);
517
518 if (!sec) {
519 sec = calloc(1, sizeof(struct file_section));
520 if (!sec)
521 return -1;
522 sec->next = handle->sections;
523 handle->sections = sec;
524 sec->id = id;
525 }
526
527 if (section_offset)
528 sec->section_offset = section_offset;
529 if (data_offset)
530 sec->data_offset = data_offset;
531 if (flags >= 0)
532 sec->flags = flags;
533
534 return 0;
535 }
536
read_header_files(struct tracecmd_input * handle)537 static int read_header_files(struct tracecmd_input *handle)
538 {
539 struct tep_handle *pevent = handle->pevent;
540 unsigned long long size;
541 char *header;
542 char buf[BUFSIZ];
543
544 if (CHECK_READ_STATE(handle, TRACECMD_FILE_HEADERS))
545 return 0;
546
547 if (!HAS_SECTIONS(handle))
548 section_add_or_update(handle, TRACECMD_OPTION_HEADER_INFO, 0, 0,
549 lseek64(handle->fd, 0, SEEK_CUR));
550
551 if (do_read_check(handle, buf, 12))
552 return -1;
553
554 if (memcmp(buf, "header_page", 12) != 0)
555 return -1;
556
557 if (read8(handle, &size) < 0)
558 return -1;
559
560 header = malloc(size);
561 if (!header)
562 return -1;
563
564 if (do_read_check(handle, header, size))
565 goto failed_read;
566
567 tep_parse_header_page(pevent, header, size, handle->long_size);
568 free(header);
569
570 /*
571 * The size field in the page is of type long,
572 * use that instead, since it represents the kernel.
573 */
574 handle->long_size = tep_get_header_page_size(pevent);
575
576 if (do_read_check(handle, buf, 13))
577 return -1;
578
579 if (memcmp(buf, "header_event", 13) != 0)
580 return -1;
581
582 if (read8(handle, &size) < 0)
583 return -1;
584
585 header = malloc(size);
586 if (!header)
587 return -1;
588
589 if (do_read_check(handle, header, size))
590 goto failed_read;
591
592 free(header);
593
594 handle->file_state = TRACECMD_FILE_HEADERS;
595
596 return 0;
597
598 failed_read:
599 free(header);
600 return -1;
601 }
602
regex_event_buf(const char * file,int size,regex_t * epreg)603 static int regex_event_buf(const char *file, int size, regex_t *epreg)
604 {
605 char *buf;
606 char *line;
607 int ret;
608
609 buf = malloc(size + 1);
610 if (!buf) {
611 tracecmd_warning("Insufficient memory");
612 return 0;
613 }
614
615 strncpy(buf, file, size);
616 buf[size] = 0;
617
618 /* get the name from the first line */
619 line = strtok(buf, "\n");
620 if (!line) {
621 tracecmd_warning("No newline found in '%s'", buf);
622 return 0;
623 }
624 /* skip name if it is there */
625 if (strncmp(line, "name: ", 6) == 0)
626 line += 6;
627
628 ret = regexec(epreg, line, 0, NULL, 0) == 0;
629
630 free(buf);
631
632 return ret;
633 }
634
read_ftrace_file(struct tracecmd_input * handle,unsigned long long size,int print,regex_t * epreg)635 static int read_ftrace_file(struct tracecmd_input *handle,
636 unsigned long long size,
637 int print, regex_t *epreg)
638 {
639 struct tep_handle *pevent = handle->pevent;
640 char *buf;
641
642 buf = malloc(size);
643 if (!buf)
644 return -1;
645 if (do_read_check(handle, buf, size)) {
646 free(buf);
647 return -1;
648 }
649
650 if (epreg) {
651 if (print || regex_event_buf(buf, size, epreg))
652 printf("%.*s\n", (int)size, buf);
653 } else {
654 if (tep_parse_event(pevent, buf, size, "ftrace"))
655 handle->parsing_failures++;
656 }
657 free(buf);
658
659 return 0;
660 }
661
read_event_file(struct tracecmd_input * handle,char * system,unsigned long long size,int print,int * sys_printed,regex_t * epreg)662 static int read_event_file(struct tracecmd_input *handle,
663 char *system, unsigned long long size,
664 int print, int *sys_printed,
665 regex_t *epreg)
666 {
667 struct tep_handle *pevent = handle->pevent;
668 char *buf;
669
670 buf = malloc(size);
671 if (!buf)
672 return -1;
673
674 if (do_read_check(handle, buf, size)) {
675 free(buf);
676 return -1;
677 }
678
679 if (epreg) {
680 if (print || regex_event_buf(buf, size, epreg)) {
681 if (!*sys_printed) {
682 printf("\nsystem: %s\n", system);
683 *sys_printed = 1;
684 }
685 printf("%.*s\n", (int)size, buf);
686 }
687 } else {
688 if (tep_parse_event(pevent, buf, size, system))
689 handle->parsing_failures++;
690 }
691 free(buf);
692
693 return 0;
694 }
695
make_preg_files(const char * regex,regex_t * system,regex_t * event,int * unique)696 static int make_preg_files(const char *regex, regex_t *system,
697 regex_t *event, int *unique)
698 {
699 char *buf;
700 char *sstr;
701 char *estr;
702 int ret;
703
704 /* unique is set if a colon is found */
705 *unique = 0;
706
707 /* split "system:event" into "system" and "event" */
708
709 buf = strdup(regex);
710 if (!buf)
711 return -ENOMEM;
712
713 sstr = strtok(buf, ":");
714 estr = strtok(NULL, ":");
715
716 /* If no colon is found, set event == system */
717 if (!estr)
718 estr = sstr;
719 else
720 *unique = 1;
721
722 ret = regcomp(system, sstr, REG_ICASE|REG_NOSUB);
723 if (ret) {
724 tracecmd_warning("Bad regular expression '%s'", sstr);
725 goto out;
726 }
727
728 ret = regcomp(event, estr, REG_ICASE|REG_NOSUB);
729 if (ret) {
730 tracecmd_warning("Bad regular expression '%s'", estr);
731 goto out;
732 }
733
734 out:
735 free(buf);
736 return ret;
737 }
738
read_ftrace_files(struct tracecmd_input * handle,const char * regex)739 static int read_ftrace_files(struct tracecmd_input *handle, const char *regex)
740 {
741 unsigned long long size;
742 regex_t spreg;
743 regex_t epreg;
744 regex_t *sreg = NULL;
745 regex_t *ereg = NULL;
746 unsigned int count, i;
747 int print_all = 0;
748 int unique;
749 int ret;
750
751 if (CHECK_READ_STATE(handle, TRACECMD_FILE_FTRACE_EVENTS))
752 return 0;
753
754 if (!HAS_SECTIONS(handle))
755 section_add_or_update(handle, TRACECMD_OPTION_FTRACE_EVENTS, 0, 0,
756 lseek64(handle->fd, 0, SEEK_CUR));
757
758 if (regex) {
759 sreg = &spreg;
760 ereg = &epreg;
761 ret = make_preg_files(regex, sreg, ereg, &unique);
762 if (ret)
763 return -1;
764
765 if (regexec(sreg, "ftrace", 0, NULL, 0) == 0) {
766 /*
767 * If the system matches a regex that did
768 * not contain a colon, then print all events.
769 */
770 if (!unique)
771 print_all = 1;
772 } else if (unique) {
773 /*
774 * The user specified a unique event that did
775 * not match the ftrace system. Don't print any
776 * events here.
777 */
778 regfree(sreg);
779 regfree(ereg);
780 sreg = NULL;
781 ereg = NULL;
782 }
783 }
784
785 ret = read4(handle, &count);
786 if (ret < 0)
787 goto out;
788
789 for (i = 0; i < count; i++) {
790 ret = read8(handle, &size);
791 if (ret < 0)
792 goto out;
793 ret = read_ftrace_file(handle, size, print_all, ereg);
794 if (ret < 0)
795 goto out;
796 }
797
798 handle->file_state = TRACECMD_FILE_FTRACE_EVENTS;
799 ret = 0;
800 out:
801 if (sreg) {
802 regfree(sreg);
803 regfree(ereg);
804 }
805
806 return ret;
807 }
808
read_event_files(struct tracecmd_input * handle,const char * regex)809 static int read_event_files(struct tracecmd_input *handle, const char *regex)
810 {
811 unsigned long long size;
812 char *system = NULL;
813 regex_t spreg;
814 regex_t epreg;
815 regex_t *sreg = NULL;
816 regex_t *ereg = NULL;
817 regex_t *reg;
818 unsigned int systems;
819 unsigned int count;
820 unsigned int i, x;
821 int print_all;
822 int sys_printed;
823 int unique;
824 int ret;
825
826 if (CHECK_READ_STATE(handle, TRACECMD_FILE_ALL_EVENTS))
827 return 0;
828
829 if (!HAS_SECTIONS(handle))
830 section_add_or_update(handle, TRACECMD_OPTION_EVENT_FORMATS, 0, 0,
831 lseek64(handle->fd, 0, SEEK_CUR));
832
833 if (regex) {
834 sreg = &spreg;
835 ereg = &epreg;
836 ret = make_preg_files(regex, sreg, ereg, &unique);
837 if (ret)
838 return -1;
839 }
840
841 ret = read4(handle, &systems);
842 if (ret < 0)
843 goto out;
844
845 for (i = 0; i < systems; i++) {
846 system = read_string(handle);
847 if (!system) {
848 ret = -1;
849 goto out;
850 }
851
852 sys_printed = 0;
853 print_all = 0;
854 reg = ereg;
855
856 if (sreg) {
857 if (regexec(sreg, system, 0, NULL, 0) == 0) {
858 /*
859 * If the user passed in a regex that
860 * did not contain a colon, then we can
861 * print all the events of this system.
862 */
863 if (!unique)
864 print_all = 1;
865 } else if (unique) {
866 /*
867 * The user passed in a unique event that
868 * specified a specific system and event.
869 * Since this system doesn't match this
870 * event, then we don't print any events
871 * for this system.
872 */
873 reg = NULL;
874 }
875 }
876
877 ret = read4(handle, &count);
878 if (ret < 0)
879 goto out;
880
881 for (x=0; x < count; x++) {
882 ret = read8(handle, &size);
883 if (ret < 0)
884 goto out;
885
886 ret = read_event_file(handle, system, size,
887 print_all, &sys_printed,
888 reg);
889 if (ret < 0)
890 goto out;
891 }
892 free(system);
893 }
894 system = NULL;
895
896 handle->file_state = TRACECMD_FILE_ALL_EVENTS;
897 ret = 0;
898 out:
899 if (sreg) {
900 regfree(sreg);
901 regfree(ereg);
902 }
903
904 free(system);
905 return ret;
906 }
907
read_proc_kallsyms(struct tracecmd_input * handle)908 static int read_proc_kallsyms(struct tracecmd_input *handle)
909 {
910 struct tep_handle *tep = handle->pevent;
911 unsigned int size;
912 char *buf;
913
914 if (CHECK_READ_STATE(handle, TRACECMD_FILE_KALLSYMS))
915 return 0;
916 if (!HAS_SECTIONS(handle))
917 section_add_or_update(handle, TRACECMD_OPTION_KALLSYMS, 0, 0,
918 lseek64(handle->fd, 0, SEEK_CUR));
919
920 if (read4(handle, &size) < 0)
921 return -1;
922 if (!size) {
923 handle->file_state = TRACECMD_FILE_KALLSYMS;
924 return 0; /* OK? */
925 }
926
927 buf = malloc(size+1);
928 if (!buf)
929 return -1;
930 if (do_read_check(handle, buf, size)){
931 free(buf);
932 return -1;
933 }
934 buf[size] = 0;
935
936 tep_parse_kallsyms(tep, buf);
937
938 free(buf);
939
940 handle->file_state = TRACECMD_FILE_KALLSYMS;
941
942 return 0;
943 }
944
read_ftrace_printk(struct tracecmd_input * handle)945 static int read_ftrace_printk(struct tracecmd_input *handle)
946 {
947 unsigned int size;
948 char *buf;
949
950 if (CHECK_READ_STATE(handle, TRACECMD_FILE_PRINTK))
951 return 0;
952
953 if (!HAS_SECTIONS(handle))
954 section_add_or_update(handle, TRACECMD_OPTION_PRINTK, 0, 0,
955 lseek64(handle->fd, 0, SEEK_CUR));
956
957 if (read4(handle, &size) < 0)
958 return -1;
959 if (!size) {
960 handle->file_state = TRACECMD_FILE_PRINTK;
961 return 0; /* OK? */
962 }
963
964 buf = malloc(size + 1);
965 if (!buf)
966 return -1;
967 if (do_read_check(handle, buf, size)) {
968 free(buf);
969 return -1;
970 }
971
972 buf[size] = 0;
973
974 tep_parse_printk_formats(handle->pevent, buf);
975
976 free(buf);
977
978 handle->file_state = TRACECMD_FILE_PRINTK;
979
980 return 0;
981 }
982
983 static int read_and_parse_cmdlines(struct tracecmd_input *handle);
984
985 /**
986 * tracecmd_get_parsing_failures - get the count of parsing failures
987 * @handle: input handle for the trace.dat file
988 *
989 * This returns the count of failures while parsing the event files
990 */
tracecmd_get_parsing_failures(struct tracecmd_input * handle)991 int tracecmd_get_parsing_failures(struct tracecmd_input *handle)
992 {
993 if (handle)
994 return handle->parsing_failures;
995 return 0;
996 }
997
read_cpus(struct tracecmd_input * handle)998 static int read_cpus(struct tracecmd_input *handle)
999 {
1000 unsigned int cpus;
1001
1002 if (CHECK_READ_STATE(handle, TRACECMD_FILE_CPU_COUNT))
1003 return 0;
1004
1005 if (read4(handle, &cpus) < 0)
1006 return -1;
1007
1008 handle->cpus = cpus;
1009 handle->max_cpu = cpus;
1010 tep_set_cpus(handle->pevent, handle->cpus);
1011 handle->file_state = TRACECMD_FILE_CPU_COUNT;
1012
1013 return 0;
1014 }
1015
read_headers_v6(struct tracecmd_input * handle,enum tracecmd_file_states state,const char * regex)1016 static int read_headers_v6(struct tracecmd_input *handle, enum tracecmd_file_states state,
1017 const char *regex)
1018 {
1019 int ret;
1020
1021 /* Set to read all if state is zero */
1022 if (!state)
1023 state = TRACECMD_FILE_OPTIONS;
1024
1025 if (state <= handle->file_state)
1026 return 0;
1027
1028 handle->parsing_failures = 0;
1029
1030 ret = read_header_files(handle);
1031 if (ret < 0)
1032 return -1;
1033
1034 if (state <= handle->file_state)
1035 return 0;
1036
1037 ret = read_ftrace_files(handle, NULL);
1038 if (ret < 0)
1039 return -1;
1040
1041 if (state <= handle->file_state)
1042 return 0;
1043
1044 ret = read_event_files(handle, regex);
1045 if (ret < 0)
1046 return -1;
1047
1048 if (state <= handle->file_state)
1049 return 0;
1050
1051 ret = read_proc_kallsyms(handle);
1052 if (ret < 0)
1053 return -1;
1054
1055 if (state <= handle->file_state)
1056 return 0;
1057
1058 ret = read_ftrace_printk(handle);
1059 if (ret < 0)
1060 return -1;
1061
1062 if (state <= handle->file_state)
1063 return 0;
1064
1065 if (read_and_parse_cmdlines(handle) < 0)
1066 return -1;
1067
1068 if (state <= handle->file_state)
1069 return 0;
1070
1071 if (read_cpus(handle) < 0)
1072 return -1;
1073
1074 if (state <= handle->file_state)
1075 return 0;
1076
1077 if (read_options_type(handle) < 0)
1078 return -1;
1079
1080 return 0;
1081 }
1082
1083 static int handle_options(struct tracecmd_input *handle);
1084
get_metadata_string(struct tracecmd_input * handle,int offset)1085 static const char *get_metadata_string(struct tracecmd_input *handle, int offset)
1086 {
1087 if (!handle || !handle->strings || offset < 0 || handle->strings_size >= offset)
1088 return NULL;
1089
1090 return handle->strings + offset;
1091 }
1092
read_section_header(struct tracecmd_input * handle,unsigned short * id,unsigned short * flags,unsigned long long * size,const char ** description)1093 static int read_section_header(struct tracecmd_input *handle, unsigned short *id,
1094 unsigned short *flags, unsigned long long *size, const char **description)
1095 {
1096 unsigned short fl;
1097 unsigned short sec_id;
1098 unsigned long long sz;
1099 int desc;
1100
1101 if (read2(handle, &sec_id))
1102 return -1;
1103 if (read2(handle, &fl))
1104 return -1;
1105 if (read4(handle, (unsigned int *)&desc))
1106 return -1;
1107 if (read8(handle, &sz))
1108 return -1;
1109
1110 if (id)
1111 *id = sec_id;
1112 if (flags)
1113 *flags = fl;
1114 if (size)
1115 *size = sz;
1116 if (description)
1117 *description = get_metadata_string(handle, desc);
1118
1119 return 0;
1120 }
1121
handle_section(struct tracecmd_input * handle,struct file_section * section,const char * regex)1122 static int handle_section(struct tracecmd_input *handle, struct file_section *section,
1123 const char *regex)
1124 {
1125 unsigned short id, flags;
1126 unsigned long long size;
1127 int ret;
1128
1129 if (lseek64(handle->fd, section->section_offset, SEEK_SET) == (off_t)-1)
1130 return -1;
1131 if (read_section_header(handle, &id, &flags, &size, NULL))
1132 return -1;
1133 section->flags = flags;
1134 if (id != section->id)
1135 return -1;
1136
1137 section->data_offset = lseek64(handle->fd, 0, SEEK_CUR);
1138 if ((section->flags & TRACECMD_SEC_FL_COMPRESS) && in_uncompress_block(handle))
1139 return -1;
1140
1141 switch (section->id) {
1142 case TRACECMD_OPTION_HEADER_INFO:
1143 ret = read_header_files(handle);
1144 break;
1145 case TRACECMD_OPTION_FTRACE_EVENTS:
1146 ret = read_ftrace_files(handle, NULL);
1147 break;
1148 case TRACECMD_OPTION_EVENT_FORMATS:
1149 ret = read_event_files(handle, regex);
1150 break;
1151 case TRACECMD_OPTION_KALLSYMS:
1152 ret = read_proc_kallsyms(handle);
1153 break;
1154 case TRACECMD_OPTION_PRINTK:
1155 ret = read_ftrace_printk(handle);
1156 break;
1157 case TRACECMD_OPTION_CMDLINES:
1158 ret = read_and_parse_cmdlines(handle);
1159 break;
1160 default:
1161 ret = 0;
1162 break;
1163 }
1164
1165 if (section->flags & TRACECMD_SEC_FL_COMPRESS)
1166 in_uncompress_reset(handle);
1167
1168 return ret;
1169 }
1170
read_headers(struct tracecmd_input * handle,const char * regex)1171 static int read_headers(struct tracecmd_input *handle, const char *regex)
1172 {
1173 struct file_section *section;
1174
1175 if (handle->options_init)
1176 return 0;
1177
1178 if (!handle->options_start)
1179 return -1;
1180
1181 if (lseek64(handle->fd, handle->options_start, SEEK_SET) == (off64_t)-1) {
1182 tracecmd_warning("Filed to goto options offset %lld", handle->options_start);
1183 return -1;
1184 }
1185
1186 if (handle_options(handle))
1187 return -1;
1188
1189 section = handle->sections;
1190 while (section) {
1191 if (handle_section(handle, section, NULL))
1192 return -1;
1193 section = section->next;
1194 }
1195
1196 handle->options_init = true;
1197 return 0;
1198 }
1199
1200 /**
1201 * tracecmd_read_headers - read the header information from trace.dat
1202 * @handle: input handle for the trace.dat file
1203 * @state: The state to read up to or zero to read up to options.
1204 *
1205 * This reads the trace.dat file for various information. Like the
1206 * format of the ring buffer, event formats, ftrace formats, kallsyms
1207 * and printk. This may be called multiple times with different @state
1208 * values, to read partial data at a time. It will always continue
1209 * where it left off.
1210 */
tracecmd_read_headers(struct tracecmd_input * handle,enum tracecmd_file_states state)1211 int tracecmd_read_headers(struct tracecmd_input *handle,
1212 enum tracecmd_file_states state)
1213 {
1214 if (!HAS_SECTIONS(handle))
1215 return read_headers_v6(handle, state, NULL);
1216 return read_headers(handle, NULL);
1217 }
1218
calc_page_offset(struct tracecmd_input * handle,unsigned long long offset)1219 static unsigned long long calc_page_offset(struct tracecmd_input *handle,
1220 unsigned long long offset)
1221 {
1222 return offset & ~(handle->page_size - 1);
1223 }
1224
read_page(struct tracecmd_input * handle,off64_t offset,int cpu,void * map)1225 static int read_page(struct tracecmd_input *handle, off64_t offset,
1226 int cpu, void *map)
1227 {
1228 off64_t save_seek;
1229 off64_t ret;
1230
1231 if (handle->use_pipe) {
1232 ret = read(handle->cpu_data[cpu].pipe_fd, map, handle->page_size);
1233 /* Set EAGAIN if the pipe is empty */
1234 if (ret < 0) {
1235 errno = EAGAIN;
1236 return -1;
1237
1238 } else if (ret == 0) {
1239 /* Set EINVAL when the pipe has closed */
1240 errno = EINVAL;
1241 return -1;
1242 }
1243 return 0;
1244 }
1245
1246 /* other parts of the code may expect the pointer to not move */
1247 save_seek = lseek64(handle->fd, 0, SEEK_CUR);
1248
1249 ret = lseek64(handle->fd, offset, SEEK_SET);
1250 if (ret < 0)
1251 return -1;
1252 ret = read(handle->fd, map, handle->page_size);
1253 if (ret < 0)
1254 return -1;
1255
1256 /* reset the file pointer back */
1257 lseek64(handle->fd, save_seek, SEEK_SET);
1258
1259 return 0;
1260 }
1261
1262 /* page_map_size must be a power of two */
normalize_size(unsigned long long size)1263 static unsigned long long normalize_size(unsigned long long size)
1264 {
1265 /* From Hacker's Delight: or bits after first set bit to all 1s */
1266 size |= (size >> 1);
1267 size |= (size >> 2);
1268 size |= (size >> 4);
1269 size |= (size >> 8);
1270 size |= (size >> 16);
1271 size |= (size >> 32);
1272
1273 /* Clear all bits except first one for previous power of two */
1274 return size - (size >> 1);
1275 }
1276
free_page_map(struct page_map * page_map)1277 static void free_page_map(struct page_map *page_map)
1278 {
1279 page_map->ref_count--;
1280 if (page_map->ref_count)
1281 return;
1282
1283 munmap(page_map->map, page_map->size);
1284 list_del(&page_map->list);
1285 free(page_map);
1286 }
1287
1288 #define CHUNK_CHECK_OFFSET(C, O) ((O) >= (C)->offset && (O) < ((C)->offset + (C)->size))
1289
chunk_cmp(const void * A,const void * B)1290 static int chunk_cmp(const void *A, const void *B)
1291 {
1292 const struct tracecmd_compress_chunk *a = A;
1293 const struct tracecmd_compress_chunk *b = B;
1294
1295 if (CHUNK_CHECK_OFFSET(b, a->offset))
1296 return 0;
1297
1298 if (b->offset < a->offset)
1299 return -1;
1300
1301 return 1;
1302 }
1303
get_zchunk(struct cpu_data * cpu,off64_t offset)1304 static struct tracecmd_compress_chunk *get_zchunk(struct cpu_data *cpu, off64_t offset)
1305 {
1306 struct cpu_zdata *cpuz = &cpu->compress;
1307 struct tracecmd_compress_chunk *chunk;
1308 struct tracecmd_compress_chunk key;
1309
1310 if (!cpuz->chunks)
1311 return NULL;
1312
1313 if (offset > (cpuz->chunks[cpuz->count - 1].offset + cpuz->chunks[cpuz->count - 1].size))
1314 return NULL;
1315
1316 /* check if the requested offset is in the last requested chunk or in the next chunk */
1317 if (CHUNK_CHECK_OFFSET(cpuz->chunks + cpuz->last_chunk, offset))
1318 return cpuz->chunks + cpuz->last_chunk;
1319
1320 cpuz->last_chunk++;
1321 if (cpuz->last_chunk < cpuz->count &&
1322 CHUNK_CHECK_OFFSET(cpuz->chunks + cpuz->last_chunk, offset))
1323 return cpuz->chunks + cpuz->last_chunk;
1324
1325 key.offset = offset;
1326 chunk = bsearch(&key, cpuz->chunks, cpuz->count, sizeof(*chunk), chunk_cmp);
1327
1328 if (!chunk) /* should never happen */
1329 return NULL;
1330
1331 cpuz->last_chunk = chunk - cpuz->chunks;
1332 return chunk;
1333 }
1334
free_zpage(struct cpu_data * cpu_data,void * map)1335 static void free_zpage(struct cpu_data *cpu_data, void *map)
1336 {
1337 struct zchunk_cache *cache;
1338
1339 list_for_each_entry(cache, &cpu_data->compress.cache, list) {
1340 if (map <= cache->map && map > (cache->map + cache->chunk->size))
1341 goto found;
1342 }
1343 return;
1344
1345 found:
1346 cache->ref--;
1347 if (cache->ref)
1348 return;
1349 list_del(&cache->list);
1350 free(cache->map);
1351 free(cache);
1352 }
1353
read_zpage(struct tracecmd_input * handle,int cpu,off64_t offset)1354 static void *read_zpage(struct tracecmd_input *handle, int cpu, off64_t offset)
1355 {
1356 struct cpu_data *cpu_data = &handle->cpu_data[cpu];
1357 struct tracecmd_compress_chunk *chunk;
1358 struct zchunk_cache *cache;
1359 void *map = NULL;
1360 int pindex;
1361 int size;
1362
1363 offset -= cpu_data->file_offset;
1364
1365 /* Look in the cache of already loaded chunks */
1366 list_for_each_entry(cache, &cpu_data->compress.cache, list) {
1367 if (CHUNK_CHECK_OFFSET(cache->chunk, offset)) {
1368 cache->ref++;
1369 goto out;
1370 }
1371 }
1372
1373 chunk = get_zchunk(cpu_data, offset);
1374 if (!chunk)
1375 return NULL;
1376
1377 size = handle->page_size > chunk->size ? handle->page_size : chunk->size;
1378 map = malloc(size);
1379 if (!map)
1380 return NULL;
1381
1382 if (tracecmd_uncompress_chunk(handle->compress, chunk, map) < 0)
1383 goto error;
1384
1385 cache = calloc(1, sizeof(struct zchunk_cache));
1386 if (!cache)
1387 goto error;
1388
1389 cache->ref = 1;
1390 cache->chunk = chunk;
1391 cache->map = map;
1392 list_add(&cache->list, &cpu_data->compress.cache);
1393
1394 /* a chunk can hold multiple pages, get the requested one */
1395 out:
1396 pindex = (offset - cache->chunk->offset) / handle->page_size;
1397 return cache->map + (pindex * handle->page_size);
1398 error:
1399 free(map);
1400 return NULL;
1401 }
1402
allocate_page_map(struct tracecmd_input * handle,struct page * page,int cpu,off64_t offset)1403 static void *allocate_page_map(struct tracecmd_input *handle,
1404 struct page *page, int cpu, off64_t offset)
1405 {
1406 struct cpu_data *cpu_data = &handle->cpu_data[cpu];
1407 struct page_map *page_map;
1408 off64_t map_size;
1409 off64_t map_offset;
1410 void *map;
1411 int ret;
1412 int fd;
1413
1414 if (handle->cpu_compressed && handle->read_zpage)
1415 return read_zpage(handle, cpu, offset);
1416
1417 if (handle->read_page) {
1418 map = malloc(handle->page_size);
1419 if (!map)
1420 return NULL;
1421 ret = read_page(handle, offset, cpu, map);
1422 if (ret < 0) {
1423 free(map);
1424 return NULL;
1425 }
1426 return map;
1427 }
1428
1429 map_size = handle->page_map_size;
1430 map_offset = offset & ~(map_size - 1);
1431
1432 if (map_offset < cpu_data->file_offset) {
1433 map_size -= cpu_data->file_offset - map_offset;
1434 map_offset = cpu_data->file_offset;
1435 }
1436
1437 page_map = cpu_data->page_map;
1438
1439 if (page_map && page_map->offset == map_offset)
1440 goto out;
1441
1442 list_for_each_entry(page_map, &cpu_data->page_maps, list) {
1443 if (page_map->offset == map_offset)
1444 goto out;
1445 }
1446
1447 page_map = calloc(1, sizeof(*page_map));
1448 if (!page_map)
1449 return NULL;
1450
1451 if (map_offset + map_size > cpu_data->file_offset + cpu_data->file_size)
1452 map_size -= map_offset + map_size -
1453 (cpu_data->file_offset + cpu_data->file_size);
1454
1455 if (cpu_data->compress.fd >= 0) {
1456 map_offset -= cpu_data->file_offset;
1457 fd = cpu_data->compress.fd;
1458 } else
1459 fd = handle->fd;
1460 again:
1461 page_map->size = map_size;
1462 page_map->offset = map_offset;
1463
1464 page_map->map = mmap(NULL, map_size, PROT_READ, MAP_PRIVATE, fd, map_offset);
1465
1466 if (page_map->map == MAP_FAILED) {
1467 /* Try a smaller map */
1468 map_size >>= 1;
1469 if (map_size < handle->page_size) {
1470 free(page_map);
1471 return NULL;
1472 }
1473 handle->page_map_size = map_size;
1474 map_offset = offset & ~(map_size - 1);
1475 /*
1476 * Note, it is now possible to get duplicate memory
1477 * maps. But that's fine, the previous maps with
1478 * larger sizes will eventually be unmapped.
1479 */
1480 goto again;
1481 }
1482
1483 list_add(&page_map->list, &cpu_data->page_maps);
1484 out:
1485 if (cpu_data->page_map != page_map) {
1486 struct page_map *old_map = cpu_data->page_map;
1487 cpu_data->page_map = page_map;
1488 page_map->ref_count++;
1489 if (old_map)
1490 free_page_map(old_map);
1491 }
1492 page->page_map = page_map;
1493 page_map->ref_count++;
1494 return page_map->map + offset - page_map->offset;
1495 }
1496
allocate_page(struct tracecmd_input * handle,int cpu,off64_t offset)1497 static struct page *allocate_page(struct tracecmd_input *handle,
1498 int cpu, off64_t offset)
1499 {
1500 struct cpu_data *cpu_data = &handle->cpu_data[cpu];
1501 struct page **pages;
1502 struct page *page;
1503 int index;
1504
1505 index = (offset - cpu_data->file_offset) / handle->page_size;
1506 if (index >= cpu_data->nr_pages) {
1507 pages = realloc(cpu_data->pages, (index + 1) * sizeof(*cpu_data->pages));
1508 if (!pages)
1509 return NULL;
1510 memset(pages + cpu_data->nr_pages, 0,
1511 (index + 1 - cpu_data->nr_pages) * sizeof(*cpu_data->pages));
1512 cpu_data->pages = pages;
1513 cpu_data->nr_pages = index + 1;
1514 }
1515 if (cpu_data->pages[index]) {
1516 cpu_data->pages[index]->ref_count++;
1517 return cpu_data->pages[index];
1518 }
1519
1520 page = malloc(sizeof(*page));
1521 if (!page)
1522 return NULL;
1523
1524 memset(page, 0, sizeof(*page));
1525 page->offset = offset;
1526 page->handle = handle;
1527 page->cpu = cpu;
1528
1529 page->map = allocate_page_map(handle, page, cpu, offset);
1530
1531 if (!page->map) {
1532 free(page);
1533 return NULL;
1534 }
1535
1536 cpu_data->pages[index] = page;
1537 cpu_data->page_cnt++;
1538 page->ref_count = 1;
1539
1540 return page;
1541 }
1542
__free_page(struct tracecmd_input * handle,struct page * page)1543 static void __free_page(struct tracecmd_input *handle, struct page *page)
1544 {
1545 struct cpu_data *cpu_data = &handle->cpu_data[page->cpu];
1546 struct page **pages;
1547 int index;
1548
1549 if (!page->ref_count) {
1550 tracecmd_critical("Page ref count is zero!");
1551 return;
1552 }
1553
1554 page->ref_count--;
1555 if (page->ref_count)
1556 return;
1557
1558 if (handle->read_page)
1559 free(page->map);
1560 else if (handle->read_zpage)
1561 free_zpage(cpu_data, page->map);
1562 else
1563 free_page_map(page->page_map);
1564
1565 index = (page->offset - cpu_data->file_offset) / handle->page_size;
1566 cpu_data->pages[index] = NULL;
1567 cpu_data->page_cnt--;
1568
1569 free(page);
1570
1571 if (handle->use_pipe) {
1572 for (index = cpu_data->nr_pages - 1; index > 0; index--)
1573 if (cpu_data->pages[index])
1574 break;
1575 if (index < (cpu_data->nr_pages - 1)) {
1576 pages = realloc(cpu_data->pages, (index + 1) * sizeof(*cpu_data->pages));
1577 if (!pages)
1578 return;
1579 cpu_data->pages = pages;
1580 cpu_data->nr_pages = index + 1;
1581 }
1582 }
1583 }
1584
free_page(struct tracecmd_input * handle,int cpu)1585 static void free_page(struct tracecmd_input *handle, int cpu)
1586 {
1587 if (!handle->cpu_data || cpu >= handle->cpus ||
1588 !handle->cpu_data[cpu].page)
1589 return;
1590
1591 __free_page(handle, handle->cpu_data[cpu].page);
1592
1593 handle->cpu_data[cpu].page = NULL;
1594 }
1595
__free_record(struct tep_record * record)1596 static void __free_record(struct tep_record *record)
1597 {
1598 if (record->priv) {
1599 struct page *page = record->priv;
1600 remove_record(page, record);
1601 __free_page(page->handle, page);
1602 }
1603
1604 free(record);
1605 }
1606
tracecmd_free_record(struct tep_record * record)1607 void tracecmd_free_record(struct tep_record *record)
1608 {
1609 if (!record)
1610 return;
1611
1612 if (!record->ref_count) {
1613 tracecmd_critical("record ref count is zero!");
1614 return;
1615 }
1616
1617 record->ref_count--;
1618
1619 if (record->ref_count)
1620 return;
1621
1622 if (record->locked) {
1623 tracecmd_critical("freeing record when it is locked!");
1624 return;
1625 }
1626
1627 record->data = NULL;
1628
1629 __free_record(record);
1630 }
1631
tracecmd_record_ref(struct tep_record * record)1632 void tracecmd_record_ref(struct tep_record *record)
1633 {
1634 record->ref_count++;
1635 #if DEBUG_RECORD
1636 /* Update locating of last reference */
1637 record->alloc_addr = (unsigned long)__builtin_return_address(0);
1638 #endif
1639 }
1640
free_next(struct tracecmd_input * handle,int cpu)1641 static void free_next(struct tracecmd_input *handle, int cpu)
1642 {
1643 struct tep_record *record;
1644
1645 if (!handle->cpu_data || cpu >= handle->cpus)
1646 return;
1647
1648 record = handle->cpu_data[cpu].next;
1649 if (!record)
1650 return;
1651
1652 handle->cpu_data[cpu].next = NULL;
1653
1654 record->locked = 0;
1655 tracecmd_free_record(record);
1656 }
1657
1658 /* This functions was taken from the Linux kernel */
mul_u64_u32_shr(unsigned long long a,unsigned long long mul,unsigned int shift)1659 static unsigned long long mul_u64_u32_shr(unsigned long long a,
1660 unsigned long long mul, unsigned int shift)
1661 {
1662 unsigned int ah, al;
1663 unsigned long long ret;
1664
1665 al = a;
1666 ah = a >> 32;
1667
1668 ret = (al * mul) >> shift;
1669 if (ah)
1670 ret += (ah * mul) << (32 - shift);
1671
1672 return ret;
1673 }
1674
1675 static inline unsigned long long
timestamp_correction_calc(unsigned long long ts,unsigned int flags,struct ts_offset_sample * min,struct ts_offset_sample * max)1676 timestamp_correction_calc(unsigned long long ts, unsigned int flags,
1677 struct ts_offset_sample *min,
1678 struct ts_offset_sample *max)
1679 {
1680 long long tscor;
1681
1682 if (flags & TRACECMD_TSYNC_FLAG_INTERPOLATE) {
1683 long long delta = max->time - min->time;
1684 long long offset = ((long long)ts - min->time) *
1685 (max->offset - min->offset);
1686
1687 tscor = min->offset + (offset + delta / 2) / delta;
1688 } else {
1689 tscor = min->offset;
1690 }
1691
1692 ts = (ts * min->scaling) >> min->fraction;
1693 if (tscor < 0)
1694 return ts - llabs(tscor);
1695
1696 return ts + tscor;
1697 }
1698
timestamp_host_sync(unsigned long long ts,int cpu,struct tracecmd_input * handle)1699 static unsigned long long timestamp_host_sync(unsigned long long ts, int cpu,
1700 struct tracecmd_input *handle)
1701 {
1702 struct timesync_offsets *tsync;
1703 int min, mid, max;
1704
1705 if (cpu >= handle->host.cpu_count)
1706 return ts;
1707 tsync = &handle->host.ts_offsets[cpu];
1708
1709 /* We have one sample, nothing to calc here */
1710 if (tsync->ts_samples_count == 1)
1711 return ts + tsync->ts_samples[0].offset;
1712
1713 /* We have two samples, nothing to search here */
1714 if (tsync->ts_samples_count == 2)
1715 return timestamp_correction_calc(ts, handle->host.flags,
1716 &tsync->ts_samples[0],
1717 &tsync->ts_samples[1]);
1718
1719 /* We have more than two samples */
1720 if (ts <= tsync->ts_samples[0].time)
1721 return timestamp_correction_calc(ts, handle->host.flags,
1722 &tsync->ts_samples[0],
1723 &tsync->ts_samples[1]);
1724 else if (ts >= tsync->ts_samples[tsync->ts_samples_count-1].time)
1725 return timestamp_correction_calc(ts, handle->host.flags,
1726 &tsync->ts_samples[tsync->ts_samples_count-2],
1727 &tsync->ts_samples[tsync->ts_samples_count-1]);
1728 min = 0;
1729 max = tsync->ts_samples_count-1;
1730 mid = (min + max)/2;
1731 while (min <= max) {
1732 if (ts < tsync->ts_samples[mid].time)
1733 max = mid - 1;
1734 else if (ts > tsync->ts_samples[mid].time)
1735 min = mid + 1;
1736 else
1737 break;
1738 mid = (min + max)/2;
1739 }
1740
1741 return timestamp_correction_calc(ts, handle->host.flags,
1742 &tsync->ts_samples[mid],
1743 &tsync->ts_samples[mid+1]);
1744 }
1745
timestamp_calc(unsigned long long ts,int cpu,struct tracecmd_input * handle)1746 static unsigned long long timestamp_calc(unsigned long long ts, int cpu,
1747 struct tracecmd_input *handle)
1748 {
1749 /* do not modify raw timestamps */
1750 if (handle->flags & TRACECMD_FL_RAW_TS)
1751 return ts;
1752
1753 /* Guest trace file, sync with host timestamps */
1754 if (handle->host.sync_enable)
1755 ts = timestamp_host_sync(ts, cpu, handle);
1756
1757 if (handle->ts2secs) {
1758 /* user specified clock frequency */
1759 ts *= handle->ts2secs;
1760 } else if (handle->tsc_calc.mult) {
1761 /* auto calculated TSC clock frequency */
1762 ts = mul_u64_u32_shr(ts, handle->tsc_calc.mult, handle->tsc_calc.shift);
1763 }
1764
1765 /* User specified time offset with --ts-offset or --date options */
1766 ts += handle->ts_offset;
1767
1768 return ts;
1769 }
1770
1771 /*
1772 * Page is mapped, now read in the page header info.
1773 */
update_page_info(struct tracecmd_input * handle,int cpu)1774 static int update_page_info(struct tracecmd_input *handle, int cpu)
1775 {
1776 struct tep_handle *pevent = handle->pevent;
1777 void *ptr = handle->cpu_data[cpu].page->map;
1778 struct kbuffer *kbuf = handle->cpu_data[cpu].kbuf;
1779
1780 /* FIXME: handle header page */
1781 if (tep_get_header_timestamp_size(pevent) != 8) {
1782 tracecmd_warning("expected a long long type for timestamp");
1783 return -1;
1784 }
1785
1786 kbuffer_load_subbuffer(kbuf, ptr);
1787 if (kbuffer_subbuffer_size(kbuf) > handle->page_size) {
1788 tracecmd_warning("bad page read, with size of %d", kbuffer_subbuffer_size(kbuf));
1789 return -1;
1790 }
1791 handle->cpu_data[cpu].timestamp = timestamp_calc(kbuffer_timestamp(kbuf),
1792 cpu, handle);
1793
1794 return 0;
1795 }
1796
1797 /*
1798 * get_page maps a page for a given cpu.
1799 *
1800 * Returns 1 if the page was already mapped,
1801 * 0 if it mapped successfully
1802 * -1 on error
1803 */
get_page(struct tracecmd_input * handle,int cpu,off64_t offset)1804 static int get_page(struct tracecmd_input *handle, int cpu,
1805 off64_t offset)
1806 {
1807 /* Don't map if the page is already where we want */
1808 if (handle->cpu_data[cpu].offset == offset &&
1809 handle->cpu_data[cpu].page)
1810 return 1;
1811
1812 /* Do not map no data for CPU */
1813 if (!handle->cpu_data[cpu].size)
1814 return -1;
1815
1816 if (offset & (handle->page_size - 1)) {
1817 errno = -EINVAL;
1818 tracecmd_critical("bad page offset %llx", offset);
1819 return -1;
1820 }
1821
1822 if (offset < handle->cpu_data[cpu].file_offset ||
1823 offset > handle->cpu_data[cpu].file_offset +
1824 handle->cpu_data[cpu].file_size) {
1825 errno = -EINVAL;
1826 tracecmd_critical("bad page offset %llx", offset);
1827 return -1;
1828 }
1829
1830 handle->cpu_data[cpu].offset = offset;
1831 handle->cpu_data[cpu].size = (handle->cpu_data[cpu].file_offset +
1832 handle->cpu_data[cpu].file_size) -
1833 offset;
1834
1835 free_page(handle, cpu);
1836
1837 handle->cpu_data[cpu].page = allocate_page(handle, cpu, offset);
1838 if (!handle->cpu_data[cpu].page)
1839 return -1;
1840
1841 if (update_page_info(handle, cpu))
1842 return -1;
1843
1844 return 0;
1845 }
1846
get_next_page(struct tracecmd_input * handle,int cpu)1847 static int get_next_page(struct tracecmd_input *handle, int cpu)
1848 {
1849 off64_t offset;
1850
1851 if (!handle->cpu_data[cpu].page && !handle->use_pipe)
1852 return 0;
1853
1854 free_page(handle, cpu);
1855
1856 if (handle->cpu_data[cpu].size <= handle->page_size) {
1857 handle->cpu_data[cpu].offset = 0;
1858 return 0;
1859 }
1860
1861 offset = handle->cpu_data[cpu].offset + handle->page_size;
1862
1863 return get_page(handle, cpu, offset);
1864 }
1865
1866 static struct tep_record *
peek_event(struct tracecmd_input * handle,unsigned long long offset,int cpu)1867 peek_event(struct tracecmd_input *handle, unsigned long long offset,
1868 int cpu)
1869 {
1870 struct tep_record *record = NULL;
1871
1872 /*
1873 * Since the timestamp is calculated from the beginning
1874 * of the page and through each event, we reset the
1875 * page to the beginning. This is just used by
1876 * tracecmd_read_at.
1877 */
1878 update_page_info(handle, cpu);
1879
1880 do {
1881 free_next(handle, cpu);
1882 record = tracecmd_peek_data(handle, cpu);
1883 if (record && (record->offset + record->record_size) > offset)
1884 break;
1885 } while (record);
1886
1887 return record;
1888 }
1889
1890 static struct tep_record *
read_event(struct tracecmd_input * handle,unsigned long long offset,int cpu)1891 read_event(struct tracecmd_input *handle, unsigned long long offset,
1892 int cpu)
1893 {
1894 struct tep_record *record;
1895
1896 record = peek_event(handle, offset, cpu);
1897 if (record)
1898 record = tracecmd_read_data(handle, cpu);
1899 return record;
1900 }
1901
1902 static struct tep_record *
find_and_peek_event(struct tracecmd_input * handle,unsigned long long offset,int * pcpu)1903 find_and_peek_event(struct tracecmd_input *handle, unsigned long long offset,
1904 int *pcpu)
1905 {
1906 unsigned long long page_offset;
1907 int cpu;
1908
1909 /* find the cpu that this offset exists in */
1910 for (cpu = 0; cpu < handle->cpus; cpu++) {
1911 if (offset >= handle->cpu_data[cpu].file_offset &&
1912 offset < handle->cpu_data[cpu].file_offset +
1913 handle->cpu_data[cpu].file_size)
1914 break;
1915 }
1916
1917 /* Not found? */
1918 if (cpu == handle->cpus)
1919 return NULL;
1920
1921 /* Move this cpu index to point to this offest */
1922 page_offset = calc_page_offset(handle, offset);
1923
1924 if (get_page(handle, cpu, page_offset) < 0)
1925 return NULL;
1926
1927 if (pcpu)
1928 *pcpu = cpu;
1929
1930 return peek_event(handle, offset, cpu);
1931 }
1932
1933
1934 static struct tep_record *
find_and_read_event(struct tracecmd_input * handle,unsigned long long offset,int * pcpu)1935 find_and_read_event(struct tracecmd_input *handle, unsigned long long offset,
1936 int *pcpu)
1937 {
1938 struct tep_record *record;
1939 int cpu;
1940
1941 record = find_and_peek_event(handle, offset, &cpu);
1942 if (record) {
1943 record = tracecmd_read_data(handle, cpu);
1944 if (pcpu)
1945 *pcpu = cpu;
1946 }
1947 return record;
1948 }
1949
1950 /**
1951 * tracecmd_read_at - read a record from a specific offset
1952 * @handle: input handle for the trace.dat file
1953 * @offset: the offset into the file to find the record
1954 * @pcpu: pointer to a variable to store the CPU id the record was found in
1955 *
1956 * This function is useful when looking for a previous record.
1957 * You can store the offset of the record "record->offset" and use that
1958 * offset to retreive the record again without needing to store any
1959 * other information about the record.
1960 *
1961 * The record returned must be freed.
1962 */
1963 struct tep_record *
tracecmd_read_at(struct tracecmd_input * handle,unsigned long long offset,int * pcpu)1964 tracecmd_read_at(struct tracecmd_input *handle, unsigned long long offset,
1965 int *pcpu)
1966 {
1967 unsigned long long page_offset;
1968 int cpu;
1969
1970 page_offset = calc_page_offset(handle, offset);
1971
1972 /* check to see if we have this page already */
1973 for (cpu = 0; cpu < handle->cpus; cpu++) {
1974 if (handle->cpu_data[cpu].offset == page_offset &&
1975 handle->cpu_data[cpu].file_size)
1976 break;
1977 }
1978
1979 if (cpu < handle->cpus && handle->cpu_data[cpu].page) {
1980 if (pcpu)
1981 *pcpu = cpu;
1982 return read_event(handle, offset, cpu);
1983 } else
1984 return find_and_read_event(handle, offset, pcpu);
1985 }
1986
1987 /**
1988 * tracecmd_refresh_record - remaps the records data
1989 * @handle: input handle for the trace.dat file
1990 * @record: the record to be refreshed
1991 *
1992 * A record data points to a mmap section of memory.
1993 * by reading new records the mmap section may be unmapped.
1994 * This will refresh the record's data mapping.
1995 *
1996 * ===== OBSOLETED BY PAGE REFERENCES =====
1997 *
1998 * Returns 1 if page is still mapped (does not modify CPU iterator)
1999 * 0 on successful mapping (was not mapped before,
2000 * This will update CPU iterator to point to
2001 * the next record)
2002 * -1 on error.
2003 */
tracecmd_refresh_record(struct tracecmd_input * handle,struct tep_record * record)2004 int tracecmd_refresh_record(struct tracecmd_input *handle,
2005 struct tep_record *record)
2006 {
2007 unsigned long long page_offset;
2008 int cpu = record->cpu;
2009 struct cpu_data *cpu_data = &handle->cpu_data[cpu];
2010 int index;
2011 int ret;
2012
2013 page_offset = calc_page_offset(handle, record->offset);
2014 index = record->offset & (handle->page_size - 1);
2015
2016 ret = get_page(handle, record->cpu, page_offset);
2017 if (ret < 0)
2018 return -1;
2019
2020 /* If the page is still mapped, there's nothing to do */
2021 if (ret)
2022 return 1;
2023
2024 record->data = kbuffer_read_at_offset(cpu_data->kbuf, index, &record->ts);
2025 cpu_data->timestamp = record->ts;
2026
2027 return 0;
2028 }
2029
2030 /**
2031 * tracecmd_read_cpu_first - get the first record in a CPU
2032 * @handle: input handle for the trace.dat file
2033 * @cpu: the CPU to search
2034 *
2035 * This returns the first (by time) record entry in a given CPU.
2036 *
2037 * The record returned must be freed.
2038 */
2039 struct tep_record *
tracecmd_read_cpu_first(struct tracecmd_input * handle,int cpu)2040 tracecmd_read_cpu_first(struct tracecmd_input *handle, int cpu)
2041 {
2042 unsigned long long page_offset;
2043 int ret;
2044
2045 if (cpu >= handle->cpus)
2046 return NULL;
2047
2048 page_offset = calc_page_offset(handle, handle->cpu_data[cpu].file_offset);
2049
2050 ret = get_page(handle, cpu, page_offset);
2051 if (ret < 0)
2052 return NULL;
2053
2054 /* If the page was already mapped, we need to reset it */
2055 if (ret)
2056 update_page_info(handle, cpu);
2057
2058 free_next(handle, cpu);
2059
2060 return tracecmd_read_data(handle, cpu);
2061 }
2062
2063 /**
2064 * tracecmd_read_cpu_last - get the last record in a CPU
2065 * @handle: input handle for the trace.dat file
2066 * @cpu: the CPU to search
2067 *
2068 * This returns the last (by time) record entry in a given CPU.
2069 *
2070 * The record returned must be freed.
2071 */
2072 struct tep_record *
tracecmd_read_cpu_last(struct tracecmd_input * handle,int cpu)2073 tracecmd_read_cpu_last(struct tracecmd_input *handle, int cpu)
2074 {
2075 struct tep_record *record = NULL;
2076 off64_t offset, page_offset;
2077
2078 offset = handle->cpu_data[cpu].file_offset +
2079 handle->cpu_data[cpu].file_size;
2080
2081 if (offset & (handle->page_size - 1))
2082 offset &= ~(handle->page_size - 1);
2083 else
2084 offset -= handle->page_size;
2085
2086 page_offset = offset;
2087
2088 again:
2089 if (get_page(handle, cpu, page_offset) < 0)
2090 return NULL;
2091
2092 offset = page_offset;
2093
2094 do {
2095 tracecmd_free_record(record);
2096 record = tracecmd_read_data(handle, cpu);
2097 if (record)
2098 offset = record->offset;
2099 } while (record);
2100
2101 record = tracecmd_read_at(handle, offset, NULL);
2102
2103 /*
2104 * It is possible that a page has just a timestamp
2105 * or just padding on it.
2106 */
2107 if (!record) {
2108 if (page_offset == handle->cpu_data[cpu].file_offset)
2109 return NULL;
2110 page_offset -= handle->page_size;
2111 goto again;
2112 }
2113
2114 return record;
2115 }
2116
2117 /**
2118 * tracecmd_set_cpu_to_timestamp - set the CPU iterator to a given time
2119 * @handle: input handle for the trace.dat file
2120 * @cpu: the CPU pointer to set
2121 * @ts: the timestamp to set the CPU at.
2122 *
2123 * This sets the CPU iterator used by tracecmd_read_data and
2124 * tracecmd_peek_data to a location in the CPU storage near
2125 * a given timestamp. It will try to set the iterator to a time before
2126 * the time stamp and not actually at a given time.
2127 *
2128 * To use this to find a record in a time field, call this function
2129 * first, than iterate with tracecmd_read_data to find the records
2130 * you need.
2131 */
2132 int
tracecmd_set_cpu_to_timestamp(struct tracecmd_input * handle,int cpu,unsigned long long ts)2133 tracecmd_set_cpu_to_timestamp(struct tracecmd_input *handle, int cpu,
2134 unsigned long long ts)
2135 {
2136 struct cpu_data *cpu_data = &handle->cpu_data[cpu];
2137 off64_t start, end, next;
2138
2139 if (cpu < 0 || cpu >= handle->cpus) {
2140 errno = -EINVAL;
2141 return -1;
2142 }
2143
2144 if (!cpu_data->size)
2145 return -1;
2146
2147 if (!cpu_data->page) {
2148 if (init_cpu(handle, cpu))
2149 return -1;
2150 }
2151
2152 if (cpu_data->timestamp == ts) {
2153 /*
2154 * If a record is cached, then that record is most
2155 * likely the matching timestamp. Otherwise we need
2156 * to start from the beginning of the index;
2157 */
2158 if (!cpu_data->next ||
2159 cpu_data->next->ts != ts)
2160 update_page_info(handle, cpu);
2161 return 0;
2162 }
2163
2164 /* Set to the first record on current page */
2165 update_page_info(handle, cpu);
2166
2167 if (cpu_data->timestamp < ts) {
2168 start = cpu_data->offset;
2169 end = cpu_data->file_offset + cpu_data->file_size;
2170 if (end & (handle->page_size - 1))
2171 end &= ~(handle->page_size - 1);
2172 else
2173 end -= handle->page_size;
2174 next = end;
2175 } else {
2176 end = cpu_data->offset;
2177 start = cpu_data->file_offset;
2178 next = start;
2179 }
2180
2181 while (start < end) {
2182 if (get_page(handle, cpu, next) < 0)
2183 return -1;
2184
2185 if (cpu_data->timestamp == ts)
2186 break;
2187
2188 if (cpu_data->timestamp < ts)
2189 start = next;
2190 else
2191 end = next;
2192
2193 next = start + (end - start) / 2;
2194 next = calc_page_offset(handle, next);
2195
2196 /* Prevent an infinite loop if start and end are a page off */
2197 if (next == start)
2198 start = next += handle->page_size;
2199 }
2200
2201 /*
2202 * We need to end up on a page before the time stamp.
2203 * We go back even if the timestamp is the same. This is because
2204 * we want the event with the timestamp, not the page. The page
2205 * can start with the timestamp we are looking for, but the event
2206 * may be on the previous page.
2207 */
2208 if (cpu_data->timestamp >= ts &&
2209 cpu_data->offset > cpu_data->file_offset)
2210 get_page(handle, cpu, cpu_data->offset - handle->page_size);
2211
2212 return 0;
2213 }
2214
2215 /**
2216 * tracecmd_set_all_cpus_to_timestamp - set all CPUs iterator to a given time
2217 * @handle: input handle for the trace.dat file
2218 * @cpu: the CPU pointer to set
2219 * @ts: the timestamp to set the CPU at.
2220 *
2221 * This sets the CPU iterator used by tracecmd_read_data and
2222 * tracecmd_peek_data to a location in the CPU storage near
2223 * a given timestamp. It will try to set the iterator to a time before
2224 * the time stamp and not actually at a given time.
2225 *
2226 * To use this to find a record in a time field, call this function
2227 * first, than iterate with tracecmd_read_next_data to find the records
2228 * you need.
2229 */
2230 void
tracecmd_set_all_cpus_to_timestamp(struct tracecmd_input * handle,unsigned long long time)2231 tracecmd_set_all_cpus_to_timestamp(struct tracecmd_input *handle,
2232 unsigned long long time)
2233 {
2234 int cpu;
2235
2236 for (cpu = 0; cpu < handle->cpus; cpu++)
2237 tracecmd_set_cpu_to_timestamp(handle, cpu, time);
2238 }
2239
2240 /**
2241 * tracecmd_set_cursor - set the offset for the next tracecmd_read_data
2242 * @handle: input handle for the trace.dat file
2243 * @cpu: the CPU pointer to set
2244 * @offset: the offset to place the cursor
2245 *
2246 * Set the pointer to the next read or peek. This is useful when
2247 * needing to read sequentially and then look at another record
2248 * out of sequence without breaking the iteration. This is done with:
2249 *
2250 * record = tracecmd_peek_data()
2251 * offset = record->offset;
2252 * record = tracecmd_read_at();
2253 * - do what ever with record -
2254 * tracecmd_set_cursor(handle, cpu, offset);
2255 *
2256 * Now the next tracecmd_peek_data or tracecmd_read_data will return
2257 * the original record.
2258 */
tracecmd_set_cursor(struct tracecmd_input * handle,int cpu,unsigned long long offset)2259 int tracecmd_set_cursor(struct tracecmd_input *handle,
2260 int cpu, unsigned long long offset)
2261 {
2262 struct cpu_data *cpu_data = &handle->cpu_data[cpu];
2263 unsigned long long page_offset;
2264
2265 if (cpu < 0 || cpu >= handle->cpus)
2266 return -1;
2267
2268 if (offset < cpu_data->file_offset ||
2269 offset > cpu_data->file_offset + cpu_data->file_size)
2270 return -1; /* cpu does not have this offset. */
2271
2272 /* Move this cpu index to point to this offest */
2273 page_offset = calc_page_offset(handle, offset);
2274
2275 if (get_page(handle, cpu, page_offset) < 0)
2276 return -1;
2277
2278 peek_event(handle, offset, cpu);
2279
2280 return 0;
2281 }
2282
2283 /**
2284 * tracecmd_get_cursor - get the offset for the next tracecmd_read_data
2285 * @handle: input handle for the trace.dat file
2286 * @cpu: the CPU pointer to get the cursor from
2287 *
2288 * Returns the offset of the next record that would be read.
2289 */
2290 unsigned long long
tracecmd_get_cursor(struct tracecmd_input * handle,int cpu)2291 tracecmd_get_cursor(struct tracecmd_input *handle, int cpu)
2292 {
2293 struct cpu_data *cpu_data = &handle->cpu_data[cpu];
2294 struct kbuffer *kbuf = cpu_data->kbuf;
2295
2296 if (cpu < 0 || cpu >= handle->cpus)
2297 return 0;
2298
2299 /*
2300 * Use the next pointer if it exists and matches the
2301 * current timestamp.
2302 */
2303 if (cpu_data->next &&
2304 cpu_data->next->ts == cpu_data->timestamp)
2305 return cpu_data->next->offset;
2306
2307 /*
2308 * Either the next point does not exist, or it does
2309 * not match the timestamp. The next read will use the
2310 * current page.
2311 *
2312 * If the offset is at the end, then return that.
2313 */
2314 if (cpu_data->offset >= cpu_data->file_offset +
2315 cpu_data->file_size)
2316 return cpu_data->offset;
2317
2318 return cpu_data->offset + kbuffer_curr_offset(kbuf);
2319 }
2320
2321 /**
2322 * tracecmd_translate_data - create a record from raw data
2323 * @handle: input handle for the trace.dat file
2324 * @ptr: raw data to read
2325 * @size: the size of the data
2326 *
2327 * This function tries to create a record from some given
2328 * raw data. The data does not need to be from the trace.dat file.
2329 * It can be stored from another location.
2330 *
2331 * Note, since the timestamp is calculated from within the trace
2332 * buffer, the timestamp for the record will be zero, since it
2333 * can't calculate it.
2334 *
2335 * The record returned must be freed.
2336 */
2337 struct tep_record *
tracecmd_translate_data(struct tracecmd_input * handle,void * ptr,int size)2338 tracecmd_translate_data(struct tracecmd_input *handle,
2339 void *ptr, int size)
2340 {
2341 struct tep_handle *pevent = handle->pevent;
2342 struct tep_record *record;
2343 unsigned int length;
2344 int swap = 1;
2345
2346 /* minimum record read is 8, (warn?) (TODO: make 8 into macro) */
2347 if (size < 8)
2348 return NULL;
2349
2350 record = malloc(sizeof(*record));
2351 if (!record)
2352 return NULL;
2353 memset(record, 0, sizeof(*record));
2354
2355 record->ref_count = 1;
2356 if (tep_is_local_bigendian(pevent) == tep_is_file_bigendian(pevent))
2357 swap = 0;
2358 record->data = kbuffer_translate_data(swap, ptr, &length);
2359 record->size = length;
2360 if (record->data)
2361 record->record_size = record->size + (record->data - ptr);
2362
2363 return record;
2364 }
2365
2366
2367 /**
2368 * tracecmd_peek_data - return the record at the current location.
2369 * @handle: input handle for the trace.dat file
2370 * @cpu: the CPU to pull from
2371 *
2372 * This returns the record at the current location of the CPU
2373 * iterator. It does not increment the CPU iterator.
2374 */
2375 struct tep_record *
tracecmd_peek_data(struct tracecmd_input * handle,int cpu)2376 tracecmd_peek_data(struct tracecmd_input *handle, int cpu)
2377 {
2378 struct tep_record *record;
2379 unsigned long long ts;
2380 struct kbuffer *kbuf;
2381 struct page *page;
2382 int index;
2383 void *data;
2384
2385 if (cpu >= handle->cpus)
2386 return NULL;
2387
2388 page = handle->cpu_data[cpu].page;
2389 kbuf = handle->cpu_data[cpu].kbuf;
2390
2391 /* Hack to work around function graph read ahead */
2392 tracecmd_curr_thread_handle = handle;
2393
2394 if (handle->cpu_data[cpu].next) {
2395
2396 record = handle->cpu_data[cpu].next;
2397 if (!record->data) {
2398 tracecmd_critical("Something freed the record");
2399 return NULL;
2400 }
2401
2402 if (handle->cpu_data[cpu].timestamp == record->ts)
2403 return record;
2404
2405 /*
2406 * The timestamp changed, which means the cached
2407 * record is no longer valid. Reread a new record.
2408 */
2409 free_next(handle, cpu);
2410 }
2411
2412 read_again:
2413 if (!page) {
2414 if (handle->use_pipe) {
2415 get_next_page(handle, cpu);
2416 page = handle->cpu_data[cpu].page;
2417 }
2418 if (!page)
2419 return NULL;
2420 }
2421
2422 data = kbuffer_read_event(kbuf, &ts);
2423 if (!data) {
2424 if (get_next_page(handle, cpu))
2425 return NULL;
2426 page = handle->cpu_data[cpu].page;
2427 goto read_again;
2428 }
2429
2430 handle->cpu_data[cpu].timestamp = timestamp_calc(ts, cpu, handle);
2431
2432 index = kbuffer_curr_offset(kbuf);
2433
2434 record = malloc(sizeof(*record));
2435 if (!record)
2436 return NULL;
2437 memset(record, 0, sizeof(*record));
2438
2439 record->ts = handle->cpu_data[cpu].timestamp;
2440 record->size = kbuffer_event_size(kbuf);
2441 record->cpu = handle->cpu_data[cpu].cpu;
2442 record->data = data;
2443 record->offset = handle->cpu_data[cpu].offset + index;
2444 record->missed_events = kbuffer_missed_events(kbuf);
2445 record->ref_count = 1;
2446 record->locked = 1;
2447
2448 handle->cpu_data[cpu].next = record;
2449
2450 record->record_size = kbuffer_curr_size(kbuf);
2451 record->priv = page;
2452 add_record(page, record);
2453 page->ref_count++;
2454
2455 kbuffer_next_event(kbuf, NULL);
2456
2457 return record;
2458 }
2459
2460 /**
2461 * tracecmd_read_data - read the next record and increment
2462 * @handle: input handle for the trace.dat file
2463 * @cpu: the CPU to pull from
2464 *
2465 * This returns the record at the current location of the CPU
2466 * iterator and increments the CPU iterator.
2467 *
2468 * The record returned must be freed.
2469 */
2470 struct tep_record *
tracecmd_read_data(struct tracecmd_input * handle,int cpu)2471 tracecmd_read_data(struct tracecmd_input *handle, int cpu)
2472 {
2473 struct tep_record *record;
2474
2475 if (cpu >= handle->cpus)
2476 return NULL;
2477
2478 record = tracecmd_peek_data(handle, cpu);
2479 handle->cpu_data[cpu].next = NULL;
2480 if (record) {
2481 record->locked = 0;
2482 #if DEBUG_RECORD
2483 record->alloc_addr = (unsigned long)__builtin_return_address(0);
2484 #endif
2485 }
2486 return record;
2487 }
2488
2489 /**
2490 * tracecmd_read_next_data - read the next record
2491 * @handle: input handle to the trace.dat file
2492 * @rec_cpu: return pointer to the CPU that the record belongs to
2493 *
2494 * This returns the next record by time. This is different than
2495 * tracecmd_read_data in that it looks at all CPUs. It does a peek
2496 * at each CPU and the record with the earliest time stame is
2497 * returned. If @rec_cpu is not NULL it gets the CPU id the record was
2498 * on. The CPU cursor of the returned record is moved to the
2499 * next record.
2500 *
2501 * Multiple reads of this function will return a serialized list
2502 * of all records for all CPUs in order of time stamp.
2503 *
2504 * The record returned must be freed.
2505 */
2506 struct tep_record *
tracecmd_read_next_data(struct tracecmd_input * handle,int * rec_cpu)2507 tracecmd_read_next_data(struct tracecmd_input *handle, int *rec_cpu)
2508 {
2509 struct tep_record *record;
2510 int next_cpu;
2511
2512 record = tracecmd_peek_next_data(handle, &next_cpu);
2513 if (!record)
2514 return NULL;
2515
2516 if (rec_cpu)
2517 *rec_cpu = next_cpu;
2518
2519 return tracecmd_read_data(handle, next_cpu);
2520 }
2521
2522 /**
2523 * tracecmd_peek_next_data - return the next record
2524 * @handle: input handle to the trace.dat file
2525 * @rec_cpu: return pointer to the CPU that the record belongs to
2526 *
2527 * This returns the next record by time. This is different than
2528 * tracecmd_peek_data in that it looks at all CPUs. It does a peek
2529 * at each CPU and the record with the earliest time stame is
2530 * returned. If @rec_cpu is not NULL it gets the CPU id the record was
2531 * on. It does not increment the CPU iterator.
2532 */
2533 struct tep_record *
tracecmd_peek_next_data(struct tracecmd_input * handle,int * rec_cpu)2534 tracecmd_peek_next_data(struct tracecmd_input *handle, int *rec_cpu)
2535 {
2536 unsigned long long ts;
2537 struct tep_record *record, *next_record = NULL;
2538 int next_cpu;
2539 int cpu;
2540
2541 if (rec_cpu)
2542 *rec_cpu = -1;
2543
2544 next_cpu = -1;
2545 ts = 0;
2546
2547 for (cpu = 0; cpu < handle->cpus; cpu++) {
2548 record = tracecmd_peek_data(handle, cpu);
2549 if (record && (!next_record || record->ts < ts)) {
2550 ts = record->ts;
2551 next_cpu = cpu;
2552 next_record = record;
2553 }
2554 }
2555
2556 if (next_record) {
2557 if (rec_cpu)
2558 *rec_cpu = next_cpu;
2559 return next_record;
2560 }
2561
2562 return NULL;
2563 }
2564
2565 /**
2566 * tracecmd_read_prev - read the record before the given record
2567 * @handle: input handle to the trace.dat file
2568 * @record: the record to use to find the previous record.
2569 *
2570 * This returns the record before the @record on its CPU. If
2571 * @record is the first record, NULL is returned. The cursor is set
2572 * as if the previous record was read by tracecmd_read_data().
2573 *
2574 * @record can not be NULL, otherwise NULL is returned; the
2575 * record ownership goes to this function.
2576 *
2577 * Note, this is not that fast of an algorithm, since it needs
2578 * to build the timestamp for the record.
2579 *
2580 * The record returned must be freed with tracecmd_free_record().
2581 */
2582 struct tep_record *
tracecmd_read_prev(struct tracecmd_input * handle,struct tep_record * record)2583 tracecmd_read_prev(struct tracecmd_input *handle, struct tep_record *record)
2584 {
2585 unsigned long long offset, page_offset;;
2586 struct cpu_data *cpu_data;
2587 int index;
2588 int cpu;
2589
2590 if (!record)
2591 return NULL;
2592
2593 cpu = record->cpu;
2594 offset = record->offset;
2595 cpu_data = &handle->cpu_data[cpu];
2596
2597 page_offset = calc_page_offset(handle, offset);
2598 index = offset - page_offset;
2599
2600 /* Note, the record passed in could have been a peek */
2601 free_next(handle, cpu);
2602
2603 /* Reset the cursor */
2604 /* Should not happen */
2605 if (get_page(handle, cpu, page_offset) < 0)
2606 return NULL;
2607
2608 update_page_info(handle, cpu);
2609
2610 /* Find the record before this record */
2611 index = 0;
2612 for (;;) {
2613 record = tracecmd_read_data(handle, cpu);
2614 /* Should not happen! */
2615 if (!record)
2616 return NULL;
2617 if (record->offset == offset)
2618 break;
2619 index = record->offset - page_offset;
2620 tracecmd_free_record(record);
2621 }
2622 tracecmd_free_record(record);
2623
2624 if (index)
2625 /* we found our record */
2626 return tracecmd_read_at(handle, page_offset + index, NULL);
2627
2628 /* reset the index to start at the beginning of the page */
2629 update_page_info(handle, cpu);
2630
2631 /* The previous record is on the previous page */
2632 for (;;) {
2633 /* check if this is the first page */
2634 if (page_offset == cpu_data->file_offset)
2635 return NULL;
2636 page_offset -= handle->page_size;
2637
2638 /* Updating page to a new page will reset index to 0 */
2639 get_page(handle, cpu, page_offset);
2640
2641 record = NULL;
2642 index = 0;
2643 do {
2644 if (record) {
2645 index = record->offset - page_offset;
2646 tracecmd_free_record(record);
2647 }
2648 record = tracecmd_read_data(handle, cpu);
2649 /* Should not happen */
2650 if (!record)
2651 return NULL;
2652 } while (record->offset != offset);
2653 tracecmd_free_record(record);
2654
2655 if (index)
2656 /* we found our record */
2657 return tracecmd_read_at(handle, page_offset + index, NULL);
2658 }
2659
2660 /* Not reached */
2661 }
2662
init_cpu_zfile(struct tracecmd_input * handle,int cpu)2663 static int init_cpu_zfile(struct tracecmd_input *handle, int cpu)
2664 {
2665 struct cpu_data *cpu_data;
2666 unsigned long long size;
2667 off64_t offset;
2668
2669 cpu_data = &handle->cpu_data[cpu];
2670 offset = lseek64(handle->fd, 0, SEEK_CUR);
2671 if (lseek64(handle->fd, cpu_data->file_offset, SEEK_SET) == (off_t)-1)
2672 return -1;
2673
2674 strcpy(cpu_data->compress.file, COMPR_TEMP_FILE);
2675 cpu_data->compress.fd = mkstemp(cpu_data->compress.file);
2676 if (cpu_data->compress.fd < 0)
2677 return -1;
2678
2679 if (tracecmd_uncompress_copy_to(handle->compress, cpu_data->compress.fd, NULL, &size))
2680 return -1;
2681
2682 if (lseek64(handle->fd, offset, SEEK_SET) == (off_t)-1)
2683 return -1;
2684
2685 cpu_data->file_offset = handle->next_offset;
2686 handle->next_offset = (handle->next_offset + size + handle->page_size - 1) &
2687 ~(handle->page_size - 1);
2688 cpu_data->offset = cpu_data->file_offset;
2689
2690 cpu_data->file_size = size;
2691 cpu_data->size = size;
2692 return 0;
2693 }
2694
init_cpu_zpage(struct tracecmd_input * handle,int cpu)2695 static int init_cpu_zpage(struct tracecmd_input *handle, int cpu)
2696 {
2697 struct cpu_data *cpu_data = &handle->cpu_data[cpu];
2698 int count;
2699 int i;
2700
2701 if (lseek64(handle->fd, cpu_data->file_offset, SEEK_SET) == (off_t)-1)
2702 return -1;
2703
2704 count = tracecmd_load_chunks_info(handle->compress, &cpu_data->compress.chunks);
2705 if (count < 0)
2706 return -1;
2707
2708 cpu_data->compress.count = count;
2709 cpu_data->compress.last_chunk = 0;
2710
2711 cpu_data->file_offset = handle->next_offset;
2712
2713 for (i = 0; i < count; i++)
2714 cpu_data->file_size += cpu_data->compress.chunks[i].size;
2715
2716 cpu_data->offset = cpu_data->file_offset;
2717 cpu_data->size = cpu_data->file_size;
2718 handle->next_offset = (handle->next_offset + cpu_data->size + handle->page_size - 1) &
2719 ~(handle->page_size - 1);
2720 return 0;
2721 }
2722
init_cpu(struct tracecmd_input * handle,int cpu)2723 static int init_cpu(struct tracecmd_input *handle, int cpu)
2724 {
2725 struct cpu_data *cpu_data = &handle->cpu_data[cpu];
2726 int ret;
2727 int i;
2728
2729 if (handle->cpu_compressed && cpu_data->file_size > 0) {
2730 if (handle->read_zpage)
2731 ret = init_cpu_zpage(handle, cpu);
2732 else
2733 ret = init_cpu_zfile(handle, cpu);
2734 if (ret)
2735 return ret;
2736 } else {
2737 cpu_data->offset = cpu_data->file_offset;
2738 cpu_data->size = cpu_data->file_size;
2739 }
2740 cpu_data->timestamp = 0;
2741
2742 list_head_init(&cpu_data->page_maps);
2743 list_head_init(&cpu_data->compress.cache);
2744
2745 if (!cpu_data->size) {
2746 tracecmd_info("CPU %d is empty", cpu);
2747 return 0;
2748 }
2749
2750 cpu_data->nr_pages = (cpu_data->size + handle->page_size - 1) / handle->page_size;
2751 if (!cpu_data->nr_pages)
2752 cpu_data->nr_pages = 1;
2753 cpu_data->pages = calloc(cpu_data->nr_pages, sizeof(*cpu_data->pages));
2754 if (!cpu_data->pages)
2755 return -1;
2756
2757 if (handle->use_pipe) {
2758 /* Just make a page, it will be nuked later */
2759 cpu_data->page = malloc(sizeof(*cpu_data->page));
2760 if (!cpu_data->page)
2761 goto fail;
2762
2763 memset(cpu_data->page, 0, sizeof(*cpu_data->page));
2764 cpu_data->pages[0] = cpu_data->page;
2765 cpu_data->page_cnt = 1;
2766 cpu_data->page->ref_count = 1;
2767 return 0;
2768 }
2769
2770 cpu_data->page = allocate_page(handle, cpu, cpu_data->offset);
2771 if (!cpu_data->page && !handle->read_page) {
2772 perror("mmap");
2773 fprintf(stderr, "Can not mmap file, will read instead\n");
2774
2775 if (cpu) {
2776 /*
2777 * If the other CPUs had size and was able to mmap
2778 * then bail.
2779 */
2780 for (i = 0; i < cpu; i++) {
2781 if (handle->cpu_data[i].size)
2782 goto fail;
2783 }
2784 }
2785
2786 /* try again without mmapping, just read it directly */
2787 handle->read_page = true;
2788 cpu_data->page = allocate_page(handle, cpu, cpu_data->offset);
2789 if (!cpu_data->page)
2790 /* Still no luck, bail! */
2791 goto fail;
2792 }
2793
2794 if (update_page_info(handle, cpu))
2795 goto fail;
2796 cpu_data->first_ts = cpu_data->timestamp;
2797
2798 return 0;
2799 fail:
2800 free(cpu_data->pages);
2801 cpu_data->pages = NULL;
2802 free(cpu_data->page);
2803 cpu_data->page = NULL;
2804 return -1;
2805 }
2806
tracecmd_set_ts_offset(struct tracecmd_input * handle,long long offset)2807 void tracecmd_set_ts_offset(struct tracecmd_input *handle,
2808 long long offset)
2809 {
2810 handle->ts_offset = offset;
2811 }
2812
2813 /**
2814 * tracecmd_add_ts_offset - Add value to the offset which will be applied to the timestamps of all
2815 * events from given trace file
2816 * @handle: input handle to the trace.dat file
2817 * @offset: value, that will be added to the offset
2818 */
tracecmd_add_ts_offset(struct tracecmd_input * handle,long long offset)2819 void tracecmd_add_ts_offset(struct tracecmd_input *handle,
2820 long long offset)
2821 {
2822 handle->ts_offset += offset;
2823 }
2824
tracecmd_set_ts2secs(struct tracecmd_input * handle,unsigned long long hz)2825 void tracecmd_set_ts2secs(struct tracecmd_input *handle,
2826 unsigned long long hz)
2827 {
2828 double ts2secs;
2829
2830 ts2secs = (double)NSEC_PER_SEC / (double)hz;
2831 handle->ts2secs = ts2secs;
2832 handle->use_trace_clock = false;
2833 }
2834
tsync_offset_cmp(const void * a,const void * b)2835 static int tsync_offset_cmp(const void *a, const void *b)
2836 {
2837 struct ts_offset_sample *ts_a = (struct ts_offset_sample *)a;
2838 struct ts_offset_sample *ts_b = (struct ts_offset_sample *)b;
2839
2840 if (ts_a->time > ts_b->time)
2841 return 1;
2842 if (ts_a->time < ts_b->time)
2843 return -1;
2844 return 0;
2845 }
2846
2847 #define safe_read(R, C) \
2848 do { \
2849 if ((C) > size) \
2850 return -EFAULT; \
2851 (R) = tep_read_number(tep, buf, (C)); \
2852 buf += (C); \
2853 size -= (C); \
2854 } while (0)
2855
2856 #define safe_read_loop(type) \
2857 do { \
2858 int ii; \
2859 for (ii = 0; ii < ts_offsets->ts_samples_count; ii++) \
2860 safe_read(ts_offsets->ts_samples[ii].type, 8); \
2861 } while (0)
2862
tsync_cpu_offsets_load(struct tracecmd_input * handle,char * buf,int size)2863 static int tsync_cpu_offsets_load(struct tracecmd_input *handle, char *buf, int size)
2864 {
2865 struct tep_handle *tep = handle->pevent;
2866 struct timesync_offsets *ts_offsets;
2867 int i, j, k;
2868
2869 safe_read(handle->host.cpu_count, 4);
2870 handle->host.ts_offsets = calloc(handle->host.cpu_count,
2871 sizeof(struct timesync_offsets));
2872 if (!handle->host.ts_offsets)
2873 return -ENOMEM;
2874 for (i = 0; i < handle->host.cpu_count; i++) {
2875 ts_offsets = &handle->host.ts_offsets[i];
2876 safe_read(ts_offsets->ts_samples_count, 4);
2877 ts_offsets->ts_samples = calloc(ts_offsets->ts_samples_count,
2878 sizeof(struct ts_offset_sample));
2879 if (!ts_offsets->ts_samples)
2880 return -ENOMEM;
2881 safe_read_loop(time);
2882 safe_read_loop(offset);
2883 safe_read_loop(scaling);
2884 }
2885
2886 if (size > 0) {
2887 for (i = 0; i < handle->host.cpu_count; i++) {
2888 ts_offsets = &handle->host.ts_offsets[i];
2889 safe_read_loop(fraction);
2890 }
2891 }
2892
2893 for (i = 0; i < handle->host.cpu_count; i++) {
2894 ts_offsets = &handle->host.ts_offsets[i];
2895 qsort(ts_offsets->ts_samples, ts_offsets->ts_samples_count,
2896 sizeof(struct ts_offset_sample), tsync_offset_cmp);
2897 /* Filter possible samples with equal time */
2898 for (k = 0, j = 0; k < ts_offsets->ts_samples_count; k++) {
2899 if (k == 0 || ts_offsets->ts_samples[k].time != ts_offsets->ts_samples[k-1].time)
2900 ts_offsets->ts_samples[j++] = ts_offsets->ts_samples[k];
2901 }
2902 ts_offsets->ts_samples_count = j;
2903 }
2904
2905 return 0;
2906 }
2907
trace_tsync_offset_free(struct host_trace_info * host)2908 static void trace_tsync_offset_free(struct host_trace_info *host)
2909 {
2910 int i;
2911
2912 if (host->ts_offsets) {
2913 for (i = 0; i < host->cpu_count; i++)
2914 free(host->ts_offsets[i].ts_samples);
2915 free(host->ts_offsets);
2916 host->ts_offsets = NULL;
2917 }
2918 }
2919
trace_pid_map_cmp(const void * a,const void * b)2920 static int trace_pid_map_cmp(const void *a, const void *b)
2921 {
2922 struct tracecmd_proc_addr_map *m_a = (struct tracecmd_proc_addr_map *)a;
2923 struct tracecmd_proc_addr_map *m_b = (struct tracecmd_proc_addr_map *)b;
2924
2925 if (m_a->start > m_b->start)
2926 if (m_a->start < m_b->start)
2927 return -1;
2928 return 0;
2929 }
2930
procmap_free(struct pid_addr_maps * maps)2931 static void procmap_free(struct pid_addr_maps *maps)
2932 {
2933 int i;
2934
2935 if (!maps)
2936 return;
2937 if (maps->lib_maps) {
2938 for (i = 0; i < maps->nr_lib_maps; i++)
2939 free(maps->lib_maps[i].lib_name);
2940 free(maps->lib_maps);
2941 }
2942 free(maps->proc_name);
2943 free(maps);
2944 }
2945
trace_guests_free(struct tracecmd_input * handle)2946 static void trace_guests_free(struct tracecmd_input *handle)
2947 {
2948 struct guest_trace_info *guest;
2949
2950 while (handle->guest) {
2951 guest = handle->guest;
2952 handle->guest = handle->guest->next;
2953 free(guest->name);
2954 free(guest->cpu_pid);
2955 free(guest);
2956 }
2957 }
2958
trace_guest_load(struct tracecmd_input * handle,char * buf,int size)2959 static int trace_guest_load(struct tracecmd_input *handle, char *buf, int size)
2960 {
2961 struct guest_trace_info *guest = NULL;
2962 int cpu;
2963 int i;
2964
2965 guest = calloc(1, sizeof(struct guest_trace_info));
2966 if (!guest)
2967 goto error;
2968
2969 /*
2970 * Guest name, null terminated string
2971 * long long (8 bytes) trace-id
2972 * int (4 bytes) number of guest CPUs
2973 * array of size number of guest CPUs:
2974 * int (4 bytes) Guest CPU id
2975 * int (4 bytes) Host PID, running the guest CPU
2976 */
2977
2978 guest->name = strndup(buf, size);
2979 if (!guest->name)
2980 goto error;
2981 buf += strlen(guest->name) + 1;
2982 size -= strlen(guest->name) + 1;
2983
2984 if (size < sizeof(long long))
2985 goto error;
2986 guest->trace_id = tep_read_number(handle->pevent, buf, sizeof(long long));
2987 buf += sizeof(long long);
2988 size -= sizeof(long long);
2989
2990 if (size < sizeof(int))
2991 goto error;
2992 guest->vcpu_count = tep_read_number(handle->pevent, buf, sizeof(int));
2993 buf += sizeof(int);
2994 size -= sizeof(int);
2995
2996 guest->cpu_pid = calloc(guest->vcpu_count, sizeof(int));
2997 if (!guest->cpu_pid)
2998 goto error;
2999
3000 for (i = 0; i < guest->vcpu_count; i++) {
3001 if (size < 2 * sizeof(int))
3002 goto error;
3003 cpu = tep_read_number(handle->pevent, buf, sizeof(int));
3004 buf += sizeof(int);
3005 if (cpu >= guest->vcpu_count)
3006 goto error;
3007 guest->cpu_pid[cpu] = tep_read_number(handle->pevent,
3008 buf, sizeof(int));
3009 buf += sizeof(int);
3010 size -= 2 * sizeof(int);
3011 }
3012
3013 guest->next = handle->guest;
3014 handle->guest = guest;
3015 return 0;
3016
3017 error:
3018 if (guest) {
3019 free(guest->cpu_pid);
3020 free(guest->name);
3021 free(guest);
3022 }
3023 return -1;
3024 }
3025
3026 /* Needs to be a constant, and 4K should be good enough */
3027 #define STR_PROCMAP_LINE_MAX 4096
trace_pid_map_load(struct tracecmd_input * handle,char * buf)3028 static int trace_pid_map_load(struct tracecmd_input *handle, char *buf)
3029 {
3030 struct pid_addr_maps *maps = NULL;
3031 char mapname[STR_PROCMAP_LINE_MAX+1];
3032 char *line;
3033 int res;
3034 int ret;
3035 int i;
3036
3037 maps = calloc(1, sizeof(*maps));
3038 if (!maps)
3039 return -ENOMEM;
3040
3041 ret = -EINVAL;
3042 line = strchr(buf, '\n');
3043 if (!line)
3044 goto out_fail;
3045
3046 *line = '\0';
3047 if (strlen(buf) > STR_PROCMAP_LINE_MAX)
3048 goto out_fail;
3049
3050 res = sscanf(buf, "%x %x %"STRINGIFY(STR_PROCMAP_LINE_MAX)"s", &maps->pid, &maps->nr_lib_maps, mapname);
3051 if (res != 3)
3052 goto out_fail;
3053
3054 ret = -ENOMEM;
3055 maps->proc_name = strdup(mapname);
3056 if (!maps->proc_name)
3057 goto out_fail;
3058
3059 maps->lib_maps = calloc(maps->nr_lib_maps, sizeof(struct tracecmd_proc_addr_map));
3060 if (!maps->lib_maps)
3061 goto out_fail;
3062
3063 buf = line + 1;
3064 line = strchr(buf, '\n');
3065 for (i = 0; i < maps->nr_lib_maps; i++) {
3066 if (!line)
3067 break;
3068 *line = '\0';
3069 if (strlen(buf) > STR_PROCMAP_LINE_MAX)
3070 break;
3071 res = sscanf(buf, "%llx %llx %s", &maps->lib_maps[i].start,
3072 &maps->lib_maps[i].end, mapname);
3073 if (res != 3)
3074 break;
3075 maps->lib_maps[i].lib_name = strdup(mapname);
3076 if (!maps->lib_maps[i].lib_name)
3077 goto out_fail;
3078 buf = line + 1;
3079 line = strchr(buf, '\n');
3080 }
3081
3082 ret = -EINVAL;
3083 if (i != maps->nr_lib_maps)
3084 goto out_fail;
3085
3086 qsort(maps->lib_maps, maps->nr_lib_maps,
3087 sizeof(*maps->lib_maps), trace_pid_map_cmp);
3088
3089 maps->next = handle->pid_maps;
3090 handle->pid_maps = maps;
3091
3092 return 0;
3093
3094 out_fail:
3095 procmap_free(maps);
3096 return ret;
3097 }
3098
trace_pid_map_free(struct pid_addr_maps * maps)3099 static void trace_pid_map_free(struct pid_addr_maps *maps)
3100 {
3101 struct pid_addr_maps *del;
3102
3103 while (maps) {
3104 del = maps;
3105 maps = maps->next;
3106 procmap_free(del);
3107 }
3108 }
3109
trace_pid_map_search(const void * a,const void * b)3110 static int trace_pid_map_search(const void *a, const void *b)
3111 {
3112 struct tracecmd_proc_addr_map *key = (struct tracecmd_proc_addr_map *)a;
3113 struct tracecmd_proc_addr_map *map = (struct tracecmd_proc_addr_map *)b;
3114
3115 if (key->start >= map->end)
3116 return 1;
3117 if (key->start < map->start)
3118 return -1;
3119 return 0;
3120 }
3121
3122 /**
3123 * tracecmd_search_task_map - Search task memory address map
3124 * @handle: input handle to the trace.dat file
3125 * @pid: pid of the task
3126 * @addr: address from the task memory space.
3127 *
3128 * Map of the task memory can be saved in the trace.dat file, using the option
3129 * "--proc-map". If there is such information, this API can be used to look up
3130 * into this memory map to find what library is loaded at the given @addr.
3131 *
3132 * A pointer to struct tracecmd_proc_addr_map is returned, containing the name
3133 * of the library at given task @addr and the library start and end addresses.
3134 */
3135 struct tracecmd_proc_addr_map *
tracecmd_search_task_map(struct tracecmd_input * handle,int pid,unsigned long long addr)3136 tracecmd_search_task_map(struct tracecmd_input *handle,
3137 int pid, unsigned long long addr)
3138 {
3139 struct tracecmd_proc_addr_map *lib;
3140 struct tracecmd_proc_addr_map key;
3141 struct pid_addr_maps *maps;
3142
3143 if (!handle || !handle->pid_maps)
3144 return NULL;
3145
3146 maps = handle->pid_maps;
3147 while (maps) {
3148 if (maps->pid == pid)
3149 break;
3150 maps = maps->next;
3151 }
3152 if (!maps || !maps->nr_lib_maps || !maps->lib_maps)
3153 return NULL;
3154 key.start = addr;
3155 lib = bsearch(&key, maps->lib_maps, maps->nr_lib_maps,
3156 sizeof(*maps->lib_maps), trace_pid_map_search);
3157
3158 return lib;
3159 }
3160
get_meta_strings_size(struct tracecmd_input * handle)3161 __hidden unsigned int get_meta_strings_size(struct tracecmd_input *handle)
3162 {
3163 return handle->strings_size;
3164 }
3165
get_last_option_offset(struct tracecmd_input * handle)3166 __hidden unsigned long long get_last_option_offset(struct tracecmd_input *handle)
3167 {
3168 return handle->options_last_offset;
3169 }
3170
handle_option_done(struct tracecmd_input * handle,char * buf,int size)3171 static int handle_option_done(struct tracecmd_input *handle, char *buf, int size)
3172 {
3173 unsigned long long offset;
3174
3175 if (size < 8)
3176 return -1;
3177
3178 offset = lseek64(handle->fd, 0, SEEK_CUR);
3179 if (offset >= size)
3180 handle->options_last_offset = offset - size;
3181
3182 offset = tep_read_number(handle->pevent, buf, 8);
3183 if (!offset)
3184 return 0;
3185
3186 if (lseek64(handle->fd, offset, SEEK_SET) == (off_t)-1)
3187 return -1;
3188
3189 return handle_options(handle);
3190 }
3191
save_read_number(struct tep_handle * tep,char * data,int * data_size,int * read_pos,int bytes,unsigned long long * num)3192 static inline int save_read_number(struct tep_handle *tep, char *data, int *data_size,
3193 int *read_pos, int bytes, unsigned long long *num)
3194 {
3195 if (bytes > *data_size)
3196 return -1;
3197
3198 *num = tep_read_number(tep, (data + *read_pos), bytes);
3199 *read_pos += bytes;
3200 *data_size -= bytes;
3201 return 0;
3202 }
3203
save_read_string(char * data,int * data_size,int * read_pos)3204 static inline char *save_read_string(char *data, int *data_size, int *read_pos)
3205 {
3206 char *str;
3207
3208 if (*data_size < 1)
3209 return NULL;
3210
3211 str = strdup(data + *read_pos);
3212 if (!str)
3213 return NULL;
3214 *data_size -= (strlen(str) + 1);
3215 if (*data_size < 0) {
3216 free(str);
3217 return NULL;
3218 }
3219 *read_pos += (strlen(str) + 1);
3220
3221 return str;
3222 }
3223
handle_buffer_option(struct tracecmd_input * handle,unsigned short id,char * data,int size)3224 static int handle_buffer_option(struct tracecmd_input *handle,
3225 unsigned short id, char *data, int size)
3226 {
3227 struct input_buffer_instance *buff;
3228 struct cpu_file_data *cpu_data;
3229 unsigned long long tmp;
3230 long long max_cpu = -1;
3231 int rsize = 0;
3232 char *name;
3233 int i;
3234
3235 if (save_read_number(handle->pevent, data, &size, &rsize, 8, &tmp))
3236 return -1;
3237
3238 name = save_read_string(data, &size, &rsize);
3239 if (!name)
3240 return -1;
3241
3242 if (*name == '\0') {
3243 /* top buffer */
3244 buff = &handle->top_buffer;
3245 } else {
3246 buff = realloc(handle->buffers, sizeof(*handle->buffers) * (handle->nr_buffers + 1));
3247 if (!buff) {
3248 free(name);
3249 return -1;
3250 }
3251 handle->buffers = buff;
3252 handle->nr_buffers++;
3253
3254 buff = &handle->buffers[handle->nr_buffers - 1];
3255 }
3256 memset(buff, 0, sizeof(struct input_buffer_instance));
3257 buff->name = name;
3258 buff->offset = tmp;
3259
3260 if (!HAS_SECTIONS(handle))
3261 return 0;
3262
3263 /* file sections specific data */
3264 buff->clock = save_read_string(data, &size, &rsize);
3265 if (!buff->clock)
3266 return -1;
3267
3268 if (*name == '\0' && !handle->trace_clock)
3269 handle->trace_clock = strdup(buff->clock);
3270
3271 if (id == TRACECMD_OPTION_BUFFER) {
3272 if (save_read_number(handle->pevent, data, &size, &rsize, 4, &tmp))
3273 return -1;
3274 buff->page_size = tmp;
3275
3276 if (save_read_number(handle->pevent, data, &size, &rsize, 4, &tmp))
3277 return -1;
3278 buff->cpus = tmp;
3279 if (!buff->cpus)
3280 return 0;
3281 cpu_data = calloc(buff->cpus, sizeof(*cpu_data));
3282 if (!cpu_data)
3283 return -1;
3284 for (i = 0; i < buff->cpus; i++) {
3285 if (save_read_number(handle->pevent, data, &size, &rsize, 4, &tmp))
3286 goto fail;
3287 if ((long long)tmp > max_cpu)
3288 max_cpu = tmp;
3289 cpu_data[i].cpu = tmp;
3290 if (save_read_number(handle->pevent, data,
3291 &size, &rsize, 8, &cpu_data[i].offset))
3292 goto fail;
3293 if (save_read_number(handle->pevent, data,
3294 &size, &rsize, 8, &cpu_data[i].size))
3295 goto fail;
3296 }
3297 if (buff->cpus == max_cpu + 1) {
3298 /* Check to make sure cpus match the index */
3299 for (i = 0; i < buff->cpus; i++) {
3300 if (cpu_data[i].cpu != i)
3301 goto copy_buffer;
3302 }
3303 buff->cpu_data = cpu_data;
3304 } else {
3305 copy_buffer:
3306 buff->cpu_data = calloc(max_cpu + 1, sizeof(*cpu_data));
3307 if (!buff->cpu_data)
3308 goto fail;
3309 for (i = 0; i < buff->cpus; i++) {
3310 if (buff->cpu_data[cpu_data[i].cpu].size) {
3311 tracecmd_warning("More than one buffer defined for CPU %d (buffer %d)\n",
3312 cpu_data[i].cpu, i);
3313 goto fail;
3314 }
3315 buff->cpu_data[cpu_data[i].cpu] = cpu_data[i];
3316 }
3317 buff->cpus = max_cpu + 1;
3318 free(cpu_data);
3319 }
3320 } else {
3321 buff->latency = true;
3322 }
3323 return 0;
3324 fail:
3325 free(cpu_data);
3326 return -1;
3327 }
3328
handle_options(struct tracecmd_input * handle)3329 static int handle_options(struct tracecmd_input *handle)
3330 {
3331 long long offset;
3332 unsigned short option;
3333 unsigned int size;
3334 unsigned short id, flags;
3335 char *cpustats = NULL;
3336 struct hook_list *hook;
3337 bool compress = false;
3338 char *buf;
3339 int cpus;
3340 int ret;
3341
3342 if (!HAS_SECTIONS(handle)) {
3343 handle->options_start = lseek64(handle->fd, 0, SEEK_CUR);
3344 } else {
3345 if (read_section_header(handle, &id, &flags, NULL, NULL))
3346 return -1;
3347 if (id != TRACECMD_OPTION_DONE)
3348 return -1;
3349 if (flags & TRACECMD_SEC_FL_COMPRESS)
3350 compress = true;
3351 }
3352
3353 if (compress && in_uncompress_block(handle))
3354 return -1;
3355
3356 for (;;) {
3357 ret = read2(handle, &option);
3358 if (ret)
3359 goto out;
3360
3361 if (!HAS_SECTIONS(handle) && option == TRACECMD_OPTION_DONE)
3362 break;
3363
3364 /* next 4 bytes is the size of the option */
3365 ret = read4(handle, &size);
3366 if (ret)
3367 goto out;
3368 buf = malloc(size);
3369 if (!buf) {
3370 ret = -ENOMEM;
3371 goto out;
3372 }
3373 ret = do_read_check(handle, buf, size);
3374 if (ret)
3375 goto out;
3376
3377 switch (option) {
3378 case TRACECMD_OPTION_DATE:
3379 /*
3380 * A time has been mapped that is the
3381 * difference between the timestamps and
3382 * gtod. It is stored as ASCII with '0x'
3383 * appended.
3384 */
3385 if (handle->flags &
3386 (TRACECMD_FL_IGNORE_DATE | TRACECMD_FL_RAW_TS))
3387 break;
3388 offset = strtoll(buf, NULL, 0);
3389 /* Convert from micro to nano */
3390 offset *= 1000;
3391 handle->ts_offset += offset;
3392 break;
3393 case TRACECMD_OPTION_OFFSET:
3394 /*
3395 * Similar to date option, but just adds an
3396 * offset to the timestamp.
3397 */
3398 if (handle->flags & TRACECMD_FL_RAW_TS)
3399 break;
3400 offset = strtoll(buf, NULL, 0);
3401 handle->ts_offset += offset;
3402 break;
3403 case TRACECMD_OPTION_TIME_SHIFT:
3404 /*
3405 * long long int (8 bytes) trace session ID
3406 * int (4 bytes) protocol flags.
3407 * int (4 bytes) CPU count.
3408 * array of size [CPU count]:
3409 * [
3410 * int (4 bytes) count of timestamp offsets.
3411 * long long array of size [count] of times,
3412 * when the offsets were calculated.
3413 * long long array of size [count] of timestamp offsets.
3414 * long long array of size [count] of timestamp scaling ratios.*
3415 * ]
3416 * array of size [CPU count]:
3417 * [
3418 * long long array of size [count] of timestamp scaling fraction bits.*
3419 * ]*
3420 */
3421 if (size < 16 || (handle->flags & TRACECMD_FL_RAW_TS))
3422 break;
3423 handle->host.peer_trace_id = tep_read_number(handle->pevent,
3424 buf, 8);
3425 handle->host.flags = tep_read_number(handle->pevent,
3426 buf + 8, 4);
3427 ret = tsync_cpu_offsets_load(handle, buf + 12, size - 12);
3428 if (ret < 0)
3429 goto out;
3430 tracecmd_enable_tsync(handle, true);
3431 break;
3432 case TRACECMD_OPTION_CPUSTAT:
3433 buf[size-1] = '\n';
3434 cpustats = realloc(handle->cpustats,
3435 handle->cpustats_size + size + 1);
3436 if (!cpustats) {
3437 ret = -ENOMEM;
3438 goto out;
3439 }
3440 memcpy(cpustats + handle->cpustats_size, buf, size);
3441 handle->cpustats_size += size;
3442 cpustats[handle->cpustats_size] = 0;
3443 handle->cpustats = cpustats;
3444 break;
3445 case TRACECMD_OPTION_BUFFER:
3446 case TRACECMD_OPTION_BUFFER_TEXT:
3447 ret = handle_buffer_option(handle, option, buf, size);
3448 if (ret < 0)
3449 goto out;
3450 break;
3451 case TRACECMD_OPTION_TRACECLOCK:
3452 tracecmd_parse_trace_clock(handle, buf, size);
3453 if (!handle->ts2secs)
3454 handle->use_trace_clock = true;
3455 break;
3456 case TRACECMD_OPTION_UNAME:
3457 handle->uname = strdup(buf);
3458 break;
3459 case TRACECMD_OPTION_VERSION:
3460 handle->version = strdup(buf);
3461 break;
3462 case TRACECMD_OPTION_HOOK:
3463 hook = tracecmd_create_event_hook(buf);
3464 hook->next = handle->hooks;
3465 handle->hooks = hook;
3466 break;
3467 case TRACECMD_OPTION_CPUCOUNT:
3468 cpus = *(int *)buf;
3469 handle->cpus = tep_read_number(handle->pevent, &cpus, 4);
3470 if (handle->cpus > handle->max_cpu)
3471 handle->max_cpu = handle->cpus;
3472 tep_set_cpus(handle->pevent, handle->cpus);
3473 break;
3474 case TRACECMD_OPTION_PROCMAPS:
3475 if (buf[size-1] == '\0')
3476 trace_pid_map_load(handle, buf);
3477 break;
3478 case TRACECMD_OPTION_TRACEID:
3479 if (size < 8)
3480 break;
3481 handle->trace_id = tep_read_number(handle->pevent,
3482 buf, 8);
3483 break;
3484 case TRACECMD_OPTION_GUEST:
3485 trace_guest_load(handle, buf, size);
3486 break;
3487 case TRACECMD_OPTION_TSC2NSEC:
3488 if (size < 16 || (handle->flags & TRACECMD_FL_RAW_TS))
3489 break;
3490 handle->tsc_calc.mult = tep_read_number(handle->pevent,
3491 buf, 4);
3492 handle->tsc_calc.shift = tep_read_number(handle->pevent,
3493 buf + 4, 4);
3494 handle->tsc_calc.offset = tep_read_number(handle->pevent,
3495 buf + 8, 8);
3496 break;
3497 case TRACECMD_OPTION_HEADER_INFO:
3498 case TRACECMD_OPTION_FTRACE_EVENTS:
3499 case TRACECMD_OPTION_EVENT_FORMATS:
3500 case TRACECMD_OPTION_KALLSYMS:
3501 case TRACECMD_OPTION_PRINTK:
3502 case TRACECMD_OPTION_CMDLINES:
3503 if (size < 8)
3504 break;
3505 section_add_or_update(handle, option, -1,
3506 tep_read_number(handle->pevent, buf, 8), 0);
3507 break;
3508 case TRACECMD_OPTION_DONE:
3509 if (compress)
3510 in_uncompress_reset(handle);
3511 ret = handle_option_done(handle, buf, size);
3512 free(buf);
3513 return ret;
3514
3515 default:
3516 tracecmd_warning("unknown option %d", option);
3517 break;
3518 }
3519
3520 free(buf);
3521
3522 }
3523
3524 ret = 0;
3525
3526 out:
3527 if (compress)
3528 in_uncompress_reset(handle);
3529 return ret;
3530 }
3531
read_options_type(struct tracecmd_input * handle)3532 static int read_options_type(struct tracecmd_input *handle)
3533 {
3534 char buf[10];
3535
3536 if (CHECK_READ_STATE(handle, TRACECMD_FILE_CPU_LATENCY))
3537 return 0;
3538
3539 if (do_read_check(handle, buf, 10))
3540 return -1;
3541
3542 /* check if this handles options */
3543 if (strncmp(buf, "options", 7) == 0) {
3544 if (handle_options(handle) < 0)
3545 return -1;
3546 handle->file_state = TRACECMD_FILE_OPTIONS;
3547 if (do_read_check(handle, buf, 10))
3548 return -1;
3549 }
3550
3551 /*
3552 * Check if this is a latency report or flyrecord.
3553 */
3554 if (strncmp(buf, "latency", 7) == 0)
3555 handle->file_state = TRACECMD_FILE_CPU_LATENCY;
3556 else if (strncmp(buf, "flyrecord", 9) == 0)
3557 handle->file_state = TRACECMD_FILE_CPU_FLYRECORD;
3558 else
3559 return -1;
3560
3561 return 0;
3562 }
3563
tracecmd_latency_data_read(struct tracecmd_input * handle,char ** buf,size_t * size)3564 int tracecmd_latency_data_read(struct tracecmd_input *handle, char **buf, size_t *size)
3565 {
3566 struct cpu_zdata *zdata = &handle->latz;
3567 void *data;
3568 int rsize;
3569 int fd = -1;
3570 int id;
3571
3572 if (!handle || !buf || !size)
3573 return -1;
3574 if (handle->file_state != TRACECMD_FILE_CPU_LATENCY)
3575 return -1;
3576
3577 if (!handle->cpu_compressed) {
3578 fd = handle->fd;
3579 } else if (!handle->read_zpage) {
3580 if (zdata->fd < 0)
3581 return -1;
3582 fd = zdata->fd;
3583 }
3584
3585 /* Read data from a file */
3586 if (fd >= 0) {
3587 if (!(*buf)) {
3588 *size = BUFSIZ;
3589 *buf = malloc(*size);
3590 if (!(*buf))
3591 return -1;
3592 }
3593 return do_read_fd(fd, *buf, *size);
3594 }
3595
3596 /* Uncompress data in memory */
3597 if (zdata->last_chunk >= zdata->count)
3598 return 0;
3599
3600 id = zdata->last_chunk;
3601 if (!*buf || *size < zdata->chunks[id].size) {
3602 data = realloc(*buf, zdata->chunks[id].size);
3603 if (!data)
3604 return -1;
3605 *buf = data;
3606 *size = zdata->chunks[id].size;
3607 }
3608
3609 if (tracecmd_uncompress_chunk(handle->compress, &zdata->chunks[id], *buf))
3610 return -1;
3611
3612 rsize = zdata->chunks[id].size;
3613 zdata->last_chunk++;
3614 return rsize;
3615 }
3616
init_cpu_data(struct tracecmd_input * handle)3617 static int init_cpu_data(struct tracecmd_input *handle)
3618 {
3619 enum kbuffer_long_size long_size;
3620 enum kbuffer_endian endian;
3621 unsigned long long max_size = 0;
3622 unsigned long long pages;
3623 int cpu;
3624
3625 /* We expect this to be flyrecord */
3626 if (handle->file_state != TRACECMD_FILE_CPU_FLYRECORD)
3627 return -1;
3628
3629 if (force_read)
3630 handle->read_page = true;
3631
3632 if (handle->long_size == 8)
3633 long_size = KBUFFER_LSIZE_8;
3634 else
3635 long_size = KBUFFER_LSIZE_4;
3636
3637 if (tep_is_file_bigendian(handle->pevent))
3638 endian = KBUFFER_ENDIAN_BIG;
3639 else
3640 endian = KBUFFER_ENDIAN_LITTLE;
3641
3642 for (cpu = 0; cpu < handle->cpus; cpu++) {
3643 handle->cpu_data[cpu].compress.fd = -1;
3644 handle->cpu_data[cpu].kbuf = kbuffer_alloc(long_size, endian);
3645 if (!handle->cpu_data[cpu].kbuf)
3646 goto out_free;
3647 if (tep_is_old_format(handle->pevent))
3648 kbuffer_set_old_format(handle->cpu_data[cpu].kbuf);
3649
3650 if (handle->cpu_data[cpu].file_size > max_size)
3651 max_size = handle->cpu_data[cpu].file_size;
3652 }
3653
3654 /* Calculate about a meg of pages for buffering */
3655 pages = handle->page_size ? max_size / handle->page_size : 0;
3656 if (!pages)
3657 pages = 1;
3658 pages = normalize_size(pages);
3659 handle->page_map_size = handle->page_size * pages;
3660 if (handle->page_map_size < handle->page_size)
3661 handle->page_map_size = handle->page_size;
3662
3663
3664 for (cpu = 0; cpu < handle->cpus; cpu++) {
3665 if (init_cpu(handle, cpu))
3666 goto out_free;
3667 }
3668
3669 return 0;
3670
3671 out_free:
3672 for ( ; cpu >= 0; cpu--) {
3673 free_page(handle, cpu);
3674 kbuffer_free(handle->cpu_data[cpu].kbuf);
3675 handle->cpu_data[cpu].kbuf = NULL;
3676 }
3677 return -1;
3678 }
3679
init_latency_data(struct tracecmd_input * handle)3680 int init_latency_data(struct tracecmd_input *handle)
3681 {
3682 unsigned long long wsize;
3683 int ret;
3684
3685 if (!handle->cpu_compressed)
3686 return 0;
3687
3688 if (handle->read_zpage) {
3689 handle->latz.count = tracecmd_load_chunks_info(handle->compress, &handle->latz.chunks);
3690 if (handle->latz.count < 0)
3691 return -1;
3692 } else {
3693 strcpy(handle->latz.file, COMPR_TEMP_FILE);
3694 handle->latz.fd = mkstemp(handle->latz.file);
3695 if (handle->latz.fd < 0)
3696 return -1;
3697
3698 ret = tracecmd_uncompress_copy_to(handle->compress, handle->latz.fd, NULL, &wsize);
3699 if (ret)
3700 return -1;
3701
3702 lseek64(handle->latz.fd, 0, SEEK_SET);
3703 }
3704
3705 return 0;
3706 }
3707
init_buffer_cpu_data(struct tracecmd_input * handle,struct input_buffer_instance * buffer)3708 static int init_buffer_cpu_data(struct tracecmd_input *handle, struct input_buffer_instance *buffer)
3709 {
3710 unsigned long long offset;
3711 unsigned long long size;
3712 unsigned short id, flags;
3713 int cpu;
3714
3715 if (handle->cpu_data)
3716 return -1;
3717
3718 if (lseek64(handle->fd, buffer->offset, SEEK_SET) == (off_t)-1)
3719 return -1;
3720 if (read_section_header(handle, &id, &flags, NULL, NULL))
3721 return -1;
3722 if (flags & TRACECMD_SEC_FL_COMPRESS)
3723 handle->cpu_compressed = true;
3724 if (buffer->latency) {
3725 handle->file_state = TRACECMD_FILE_CPU_LATENCY;
3726 return init_latency_data(handle) == 0 ? 1 : -1;
3727 }
3728 handle->file_state = TRACECMD_FILE_CPU_FLYRECORD;
3729 handle->cpus = buffer->cpus;
3730 if (handle->max_cpu < handle->cpus)
3731 handle->max_cpu = handle->cpus;
3732
3733 handle->cpu_data = calloc(handle->cpus, sizeof(*handle->cpu_data));
3734 if (!handle->cpu_data)
3735 return -1;
3736
3737 for (cpu = 0; cpu < handle->cpus; cpu++) {
3738 handle->cpu_data[cpu].cpu = buffer->cpu_data[cpu].cpu;
3739 offset = buffer->cpu_data[cpu].offset;
3740 size = buffer->cpu_data[cpu].size;
3741 handle->cpu_data[cpu].file_offset = offset;
3742 handle->cpu_data[cpu].file_size = size;
3743 if (size && (offset + size > handle->total_file_size)) {
3744 /* this happens if the file got truncated */
3745 printf("File possibly truncated. "
3746 "Need at least %llu, but file size is %zu.\n",
3747 offset + size, handle->total_file_size);
3748 errno = EINVAL;
3749 return -1;
3750 }
3751 }
3752
3753 return init_cpu_data(handle);
3754 }
3755
read_cpu_data(struct tracecmd_input * handle)3756 static int read_cpu_data(struct tracecmd_input *handle)
3757 {
3758 unsigned long long size;
3759 int cpus;
3760 int cpu;
3761
3762 /*
3763 * Check if this is a latency report or not.
3764 */
3765 if (handle->file_state == TRACECMD_FILE_CPU_LATENCY)
3766 return 1;
3767
3768 /* We expect this to be flyrecord */
3769 if (handle->file_state != TRACECMD_FILE_CPU_FLYRECORD)
3770 return -1;
3771
3772 cpus = handle->cpus;
3773
3774 handle->cpu_data = malloc(sizeof(*handle->cpu_data) * handle->cpus);
3775 if (!handle->cpu_data)
3776 return -1;
3777 memset(handle->cpu_data, 0, sizeof(*handle->cpu_data) * handle->cpus);
3778
3779 for (cpu = 0; cpu < handle->cpus; cpu++) {
3780 unsigned long long offset;
3781
3782 handle->cpu_data[cpu].cpu = cpu;
3783 read8(handle, &offset);
3784 read8(handle, &size);
3785 handle->cpu_data[cpu].file_offset = offset;
3786 handle->cpu_data[cpu].file_size = size;
3787 if (size && (offset + size > handle->total_file_size)) {
3788 /* this happens if the file got truncated */
3789 printf("File possibly truncated. "
3790 "Need at least %llu, but file size is %zu.\n",
3791 offset + size, handle->total_file_size);
3792 errno = EINVAL;
3793 return -1;
3794 }
3795 }
3796
3797 /*
3798 * It is possible that an option changed the number of CPUs.
3799 * If that happened, then there's "empty" cpu data saved for
3800 * backward compatibility.
3801 */
3802 if (cpus < handle->cpus) {
3803 unsigned long long ignore;
3804 int once = 0;
3805
3806 read8(handle, &ignore); /* offset */
3807 read8(handle, &ignore); /* size */
3808 if (ignore != 0) {
3809 if (!once) {
3810 tracecmd_warning("ignored CPU data not zero size");
3811 once++;
3812 }
3813 }
3814 }
3815
3816 return init_cpu_data(handle);
3817 }
3818
read_data_and_size(struct tracecmd_input * handle,char ** data,unsigned long long * size)3819 static int read_data_and_size(struct tracecmd_input *handle,
3820 char **data, unsigned long long *size)
3821 {
3822 if (read8(handle, size) < 0)
3823 return -1;
3824 *data = malloc(*size + 1);
3825 if (!*data)
3826 return -1;
3827 if (do_read_check(handle, *data, *size)) {
3828 free(*data);
3829 return -1;
3830 }
3831
3832 return 0;
3833 }
3834
read_and_parse_cmdlines(struct tracecmd_input * handle)3835 static int read_and_parse_cmdlines(struct tracecmd_input *handle)
3836 {
3837 struct tep_handle *pevent = handle->pevent;
3838 unsigned long long size;
3839 char *cmdlines;
3840
3841 if (CHECK_READ_STATE(handle, TRACECMD_FILE_CMD_LINES))
3842 return 0;
3843
3844 if (!HAS_SECTIONS(handle))
3845 section_add_or_update(handle, TRACECMD_OPTION_CMDLINES, 0, 0,
3846 lseek64(handle->fd, 0, SEEK_CUR));
3847
3848
3849 if (read_data_and_size(handle, &cmdlines, &size) < 0)
3850 return -1;
3851 cmdlines[size] = 0;
3852 tep_parse_saved_cmdlines(pevent, cmdlines);
3853 free(cmdlines);
3854
3855 handle->file_state = TRACECMD_FILE_CMD_LINES;
3856
3857 return 0;
3858 }
3859
extract_trace_clock(struct tracecmd_input * handle,char * line)3860 static void extract_trace_clock(struct tracecmd_input *handle, char *line)
3861 {
3862 char *clock = NULL;
3863 char *next = NULL;
3864 char *data;
3865
3866 data = strtok_r(line, "[]", &next);
3867 sscanf(data, "%ms", &clock);
3868 /* TODO: report if it fails to allocate */
3869 handle->trace_clock = clock;
3870
3871 if (!clock)
3872 return;
3873
3874 /* Clear usecs if raw timestamps are requested */
3875 if (handle->flags & TRACECMD_FL_RAW_TS)
3876 handle->flags &= ~TRACECMD_FL_IN_USECS;
3877
3878 /* Clear usecs if not one of the specified clocks */
3879 if (strcmp(clock, "local") && strcmp(clock, "global") &&
3880 strcmp(clock, "uptime") && strcmp(clock, "perf") &&
3881 strncmp(clock, "mono", 4) && strcmp(clock, TSCNSEC_CLOCK) &&
3882 strcmp(clock, "tai"))
3883 handle->flags &= ~TRACECMD_FL_IN_USECS;
3884 }
3885
tracecmd_parse_trace_clock(struct tracecmd_input * handle,char * file,int size __maybe_unused)3886 void tracecmd_parse_trace_clock(struct tracecmd_input *handle,
3887 char *file, int size __maybe_unused)
3888 {
3889 char *line;
3890 char *next = NULL;
3891
3892 line = strtok_r(file, " ", &next);
3893 while (line) {
3894 /* current trace_clock is shown as "[local]". */
3895 if (*line == '[')
3896 return extract_trace_clock(handle, line);
3897 line = strtok_r(NULL, " ", &next);
3898 }
3899 }
3900
read_and_parse_trace_clock(struct tracecmd_input * handle,struct tep_handle * pevent)3901 static int read_and_parse_trace_clock(struct tracecmd_input *handle,
3902 struct tep_handle *pevent)
3903 {
3904 unsigned long long size;
3905 char *trace_clock;
3906
3907 if (read_data_and_size(handle, &trace_clock, &size) < 0)
3908 return -1;
3909 trace_clock[size] = 0;
3910 tracecmd_parse_trace_clock(handle, trace_clock, size);
3911 free(trace_clock);
3912 return 0;
3913 }
3914
init_data_v6(struct tracecmd_input * handle)3915 static int init_data_v6(struct tracecmd_input *handle)
3916 {
3917 struct tep_handle *pevent = handle->pevent;
3918 int ret;
3919
3920 ret = read_cpu_data(handle);
3921 if (ret < 0)
3922 return ret;
3923
3924 if (handle->use_trace_clock) {
3925 /*
3926 * There was a bug in the original setting of
3927 * the trace_clock file which let it get
3928 * corrupted. If it fails to read, force local
3929 * clock.
3930 */
3931 if (read_and_parse_trace_clock(handle, pevent) < 0) {
3932 char clock[] = "[local]";
3933 tracecmd_warning("File has trace_clock bug, using local clock");
3934 tracecmd_parse_trace_clock(handle, clock, 8);
3935 }
3936 }
3937 return ret;
3938 }
3939
init_data(struct tracecmd_input * handle)3940 static int init_data(struct tracecmd_input *handle)
3941 {
3942 return init_buffer_cpu_data(handle, &handle->top_buffer);
3943 }
3944
3945 /**
3946 * tracecmd_init_data - prepare reading the data from trace.dat
3947 * @handle: input handle for the trace.dat file
3948 *
3949 * This prepares reading the data from trace.dat. This is called
3950 * after tracecmd_read_headers() and before tracecmd_read_data().
3951 */
tracecmd_init_data(struct tracecmd_input * handle)3952 int tracecmd_init_data(struct tracecmd_input *handle)
3953 {
3954 int ret;
3955
3956 if (!HAS_SECTIONS(handle))
3957 ret = init_data_v6(handle);
3958 else
3959 ret = init_data(handle);
3960 tracecmd_blk_hack(handle);
3961
3962 return ret;
3963 }
3964
3965 /**
3966 * tracecmd_make_pipe - Have the handle read a pipe instead of a file
3967 * @handle: input handle to read from a pipe
3968 * @cpu: the cpu that the pipe represents
3969 * @fd: the read end of the pipe
3970 * @cpus: the total number of cpus for this handle
3971 *
3972 * In order to stream data from the binary trace files and produce
3973 * output or analyze the data, a tracecmd_input descriptor needs to
3974 * be created, and then converted into a form that can act on a
3975 * pipe.
3976 *
3977 * Note, there are limitations to what this descriptor can do.
3978 * Most notibly, it can not read backwards. Once a page is read
3979 * it can not be read at a later time (except if a record is attached
3980 * to it and is holding the page ref).
3981 *
3982 * It is expected that the handle has already been created and
3983 * tracecmd_read_headers() has run on it.
3984 */
tracecmd_make_pipe(struct tracecmd_input * handle,int cpu,int fd,int cpus)3985 int tracecmd_make_pipe(struct tracecmd_input *handle, int cpu, int fd, int cpus)
3986 {
3987 enum kbuffer_long_size long_size;
3988 enum kbuffer_endian endian;
3989
3990 handle->read_page = true;
3991 handle->use_pipe = true;
3992
3993 if (!handle->cpus) {
3994 handle->cpus = cpus;
3995 handle->cpu_data = malloc(sizeof(*handle->cpu_data) * handle->cpus);
3996 if (!handle->cpu_data)
3997 return -1;
3998 }
3999
4000 if (cpu >= handle->cpus)
4001 return -1;
4002
4003
4004 if (handle->long_size == 8)
4005 long_size = KBUFFER_LSIZE_8;
4006 else
4007 long_size = KBUFFER_LSIZE_4;
4008
4009 if (tep_is_file_bigendian(handle->pevent))
4010 endian = KBUFFER_ENDIAN_BIG;
4011 else
4012 endian = KBUFFER_ENDIAN_LITTLE;
4013
4014 memset(&handle->cpu_data[cpu], 0, sizeof(handle->cpu_data[cpu]));
4015 handle->cpu_data[cpu].pipe_fd = fd;
4016 handle->cpu_data[cpu].cpu = cpu;
4017
4018 handle->cpu_data[cpu].kbuf = kbuffer_alloc(long_size, endian);
4019 if (!handle->cpu_data[cpu].kbuf)
4020 return -1;
4021 if (tep_is_old_format(handle->pevent))
4022 kbuffer_set_old_format(handle->cpu_data[cpu].kbuf);
4023
4024 handle->cpu_data[cpu].file_offset = 0;
4025 handle->cpu_data[cpu].file_size = -1;
4026
4027 init_cpu(handle, cpu);
4028
4029 return 0;
4030 }
4031
4032 /**
4033 * tracecmd_print_events - print the events that are stored in trace.dat
4034 * @handle: input handle for the trace.dat file
4035 * @regex: regex of events to print (NULL is all events)
4036 *
4037 * This is a debugging routine to print out the events that
4038 * are stored in a given trace.dat file.
4039 */
tracecmd_print_events(struct tracecmd_input * handle,const char * regex)4040 void tracecmd_print_events(struct tracecmd_input *handle, const char *regex)
4041 {
4042 if (!regex)
4043 regex = ".*";
4044
4045 if (!HAS_SECTIONS(handle))
4046 read_headers_v6(handle, TRACECMD_FILE_ALL_EVENTS, regex);
4047
4048 read_headers(handle, regex);
4049 }
4050
4051 /* Show the cpu data stats */
show_cpu_stats(struct tracecmd_input * handle)4052 static void show_cpu_stats(struct tracecmd_input *handle)
4053 {
4054 struct cpu_data *cpu_data;
4055 int i;
4056
4057 for (i = 0; i < handle->cpus; i++) {
4058 cpu_data = &handle->cpu_data[i];
4059 printf("CPU%d data recorded at offset=0x%llx\n",
4060 i, cpu_data->file_offset);
4061 printf(" %lld bytes in size\n", cpu_data->file_size);
4062 }
4063 }
4064
4065 /**
4066 * tracecmd_print_stats - prints the stats recorded in the options.
4067 * @handle: input handle for the trace.dat file
4068 *
4069 * Looks for the option TRACECMD_OPTION_CPUSTAT and prints out what's
4070 * stored there, if it is found. Otherwise it prints that none were found.
4071 */
tracecmd_print_stats(struct tracecmd_input * handle)4072 void tracecmd_print_stats(struct tracecmd_input *handle)
4073 {
4074 if (handle->cpustats)
4075 printf("%s\n", handle->cpustats);
4076 else
4077 printf(" No stats in this file\n");
4078
4079 show_cpu_stats(handle);
4080 }
4081
4082 /**
4083 * tracecmd_print_uname - prints the recorded uname if it was recorded
4084 * @handle: input handle for the trace.dat file
4085 *
4086 * Looks for the option TRACECMD_OPTION_UNAME and prints out what's
4087 * stored there, if it is found. Otherwise it prints that none were found.
4088 */
tracecmd_print_uname(struct tracecmd_input * handle)4089 void tracecmd_print_uname(struct tracecmd_input *handle)
4090 {
4091 if (handle->uname)
4092 printf("%s\n", handle->uname);
4093 else
4094 printf(" uname was not recorded in this file\n");
4095 }
4096
4097 /**
4098 * tracecmd_print_uname - prints the recorded uname if it was recorded
4099 * @handle: input handle for the trace.dat file
4100 *
4101 * Looks for the option TRACECMD_OPTION_VERSION and prints out what's
4102 * stored there, if it is found. Otherwise it prints that none were found.
4103 */
tracecmd_print_version(struct tracecmd_input * handle)4104 void tracecmd_print_version(struct tracecmd_input *handle)
4105 {
4106 if (handle->version)
4107 printf("%s\n", handle->version);
4108 else
4109 printf(" version was not recorded in this file\n");
4110 }
4111
4112 /**
4113 * tracecmd_hooks - return the event hooks that were used in record
4114 * @handle: input handle for the trace.dat file
4115 *
4116 * If trace-cmd record used -H to save hooks, they are parsed and
4117 * presented as hooks here.
4118 *
4119 * Returns the hook list (do not free it, they are freed on close)
4120 */
tracecmd_hooks(struct tracecmd_input * handle)4121 struct hook_list *tracecmd_hooks(struct tracecmd_input *handle)
4122 {
4123 return handle->hooks;
4124 }
4125
init_metadata_strings(struct tracecmd_input * handle,int size)4126 static int init_metadata_strings(struct tracecmd_input *handle, int size)
4127 {
4128 char *tmp;
4129
4130 tmp = realloc(handle->strings, handle->strings_size + size);
4131 if (!tmp)
4132 return -1;
4133
4134 handle->strings = tmp;
4135 if (do_read_check(handle, handle->strings + handle->strings_size, size))
4136 return -1;
4137
4138 handle->strings_size += size;
4139
4140 return 0;
4141 }
4142
read_metadata_strings(struct tracecmd_input * handle)4143 static int read_metadata_strings(struct tracecmd_input *handle)
4144 {
4145 unsigned short flags;
4146 int found = 0;
4147 unsigned short id;
4148 unsigned int csize, rsize;
4149 unsigned long long size;
4150 off64_t offset;
4151
4152 offset = lseek64(handle->fd, 0, SEEK_CUR);
4153 do {
4154 if (read_section_header(handle, &id, &flags, &size, NULL))
4155 break;
4156 if (id == TRACECMD_OPTION_STRINGS) {
4157 found++;
4158 if ((flags & TRACECMD_SEC_FL_COMPRESS)) {
4159 read4(handle, &csize);
4160 read4(handle, &rsize);
4161 do_lseek(handle, -8, SEEK_CUR);
4162 if (in_uncompress_block(handle))
4163 break;
4164 } else {
4165 rsize = size;
4166 }
4167 init_metadata_strings(handle, rsize);
4168 if (flags & TRACECMD_SEC_FL_COMPRESS)
4169 in_uncompress_reset(handle);
4170 } else {
4171 if (lseek64(handle->fd, size, SEEK_CUR) == (off_t)-1)
4172 break;
4173 }
4174 } while (1);
4175
4176 if (lseek64(handle->fd, offset, SEEK_SET) == (off_t)-1)
4177 return -1;
4178
4179 return found ? 0 : -1;
4180 }
4181
4182 /**
4183 * tracecmd_alloc_fd - create a tracecmd_input handle from a file descriptor
4184 * @fd: the file descriptor for the trace.dat file
4185 * @flags: bitmask of enum tracecmd_open_flags
4186 *
4187 * Allocate a tracecmd_input handle from a file descriptor and open the
4188 * file. This tests if the file is of trace-cmd format and allocates
4189 * a parse event descriptor.
4190 *
4191 * The returned pointer is not ready to be read yet. A tracecmd_read_headers()
4192 * and tracecmd_init_data() still need to be called on the descriptor.
4193 *
4194 * Unless you know what you are doing with this, you want to use
4195 * tracecmd_open_fd() instead.
4196 */
tracecmd_alloc_fd(int fd,int flags)4197 struct tracecmd_input *tracecmd_alloc_fd(int fd, int flags)
4198 {
4199 struct tracecmd_input *handle;
4200 char test[] = TRACECMD_MAGIC;
4201 unsigned int page_size;
4202 size_t offset;
4203 char *version = NULL;
4204 char *zver = NULL;
4205 char *zname = NULL;
4206 char buf[BUFSIZ];
4207 unsigned long ver;
4208
4209 handle = malloc(sizeof(*handle));
4210 if (!handle)
4211 return NULL;
4212 memset(handle, 0, sizeof(*handle));
4213
4214 handle->fd = fd;
4215 handle->ref = 1;
4216 handle->latz.fd = -1;
4217 /* By default, use usecs, unless told otherwise */
4218 handle->flags |= TRACECMD_FL_IN_USECS;
4219
4220 #ifdef INMEMORY_DECOMPRESS
4221 handle->read_zpage = 1;
4222 #endif
4223 if (do_read_check(handle, buf, 3))
4224 goto failed_read;
4225
4226 if (memcmp(buf, test, 3) != 0)
4227 goto failed_read;
4228
4229 if (do_read_check(handle, buf, 7))
4230 goto failed_read;
4231 if (memcmp(buf, "tracing", 7) != 0)
4232 goto failed_read;
4233
4234 version = read_string(handle);
4235 if (!version)
4236 goto failed_read;
4237 tracecmd_info("version = %s", version);
4238 ver = strtol(version, NULL, 10);
4239 if (!ver && errno)
4240 goto failed_read;
4241 if (!tracecmd_is_version_supported(ver)) {
4242 tracecmd_warning("Unsupported file version %lu", ver);
4243 goto failed_read;
4244 }
4245 handle->file_version = ver;
4246 free(version);
4247 version = NULL;
4248
4249 if (handle->file_version >= FILE_VERSION_SECTIONS)
4250 handle->flags |= TRACECMD_FL_SECTIONED;
4251 if (handle->file_version >= FILE_VERSION_COMPRESSION)
4252 handle->flags |= TRACECMD_FL_COMPRESSION;
4253
4254 if (do_read_check(handle, buf, 1))
4255 goto failed_read;
4256
4257 handle->pevent = tep_alloc();
4258 if (!handle->pevent)
4259 goto failed_read;
4260
4261 /* register default ftrace functions first */
4262 if (!(flags & TRACECMD_FL_LOAD_NO_PLUGINS) &&
4263 !(flags & TRACECMD_FL_LOAD_NO_SYSTEM_PLUGINS))
4264 tracecmd_ftrace_overrides(handle, &handle->finfo);
4265
4266 handle->plugin_list = trace_load_plugins(handle->pevent, flags);
4267
4268 tep_set_file_bigendian(handle->pevent, buf[0]);
4269 tep_set_local_bigendian(handle->pevent, tracecmd_host_bigendian());
4270
4271 do_read_check(handle, buf, 1);
4272 handle->long_size = buf[0];
4273 tep_set_long_size(handle->pevent, handle->long_size);
4274
4275 read4(handle, &page_size);
4276 handle->page_size = page_size;
4277 handle->next_offset = page_size;
4278
4279 offset = lseek64(handle->fd, 0, SEEK_CUR);
4280 handle->total_file_size = lseek64(handle->fd, 0, SEEK_END);
4281 lseek64(handle->fd, offset, SEEK_SET);
4282
4283 if (HAS_COMPRESSION(handle)) {
4284 zname = read_string(handle);
4285 if (!zname)
4286 goto failed_read;
4287
4288 zver = read_string(handle);
4289 if (!zver)
4290 goto failed_read;
4291
4292 if (strcmp(zname, "none") == 0) {
4293 handle->read_zpage = false;
4294 handle->flags &= ~TRACECMD_FL_COMPRESSION;
4295 } else {
4296 handle->compress = tracecmd_compress_alloc(zname, zver,
4297 handle->fd,
4298 handle->pevent, NULL);
4299 if (!handle->compress) {
4300 tracecmd_warning("Unsupported file compression %s %s", zname, zver);
4301 goto failed_read;
4302 }
4303 }
4304
4305 free(zname);
4306 free(zver);
4307 }
4308
4309 if (HAS_SECTIONS(handle)) {
4310 if (read8(handle, &(handle->options_start))) {
4311 tracecmd_warning("Filed to read the offset of the first option section");
4312 goto failed_read;
4313 }
4314 read_metadata_strings(handle);
4315 }
4316
4317 handle->file_state = TRACECMD_FILE_INIT;
4318
4319 return handle;
4320
4321 failed_read:
4322 free(version);
4323 free(zname);
4324 free(zver);
4325 free(handle);
4326
4327 return NULL;
4328 }
4329
4330 /**
4331 * tracecmd_alloc_fd - create a tracecmd_input handle from a file name
4332 * @file: the file name of the file that is of tracecmd data type.
4333 * @flags: bitmask of enum tracecmd_open_flags
4334 *
4335 * Allocate a tracecmd_input handle from a given file name and open the
4336 * file. This tests if the file is of trace-cmd format and allocates
4337 * a parse event descriptor.
4338 *
4339 * The returned pointer is not ready to be read yet. A tracecmd_read_headers()
4340 * and tracecmd_init_data() still need to be called on the descriptor.
4341 *
4342 * Unless you know what you are doing with this, you want to use
4343 * tracecmd_open() instead.
4344 */
tracecmd_alloc(const char * file,int flags)4345 struct tracecmd_input *tracecmd_alloc(const char *file, int flags)
4346 {
4347 int fd;
4348
4349 fd = open(file, O_RDONLY);
4350 if (fd < 0)
4351 return NULL;
4352
4353 return tracecmd_alloc_fd(fd, flags);
4354 }
4355
4356 /**
4357 * tracecmd_open_fd - create a tracecmd_handle from the trace.dat file descriptor
4358 * @fd: the file descriptor for the trace.dat file
4359 * @flags: bitmask of enum tracecmd_open_flags
4360 */
tracecmd_open_fd(int fd,int flags)4361 struct tracecmd_input *tracecmd_open_fd(int fd, int flags)
4362 {
4363 struct tracecmd_input *handle;
4364 int ret;
4365
4366 handle = tracecmd_alloc_fd(fd, flags);
4367 if (!handle)
4368 return NULL;
4369
4370 if (tracecmd_read_headers(handle, 0) < 0)
4371 goto fail;
4372
4373 if ((ret = tracecmd_init_data(handle)) < 0)
4374 goto fail;
4375
4376 return handle;
4377
4378 fail:
4379 tracecmd_close(handle);
4380 return NULL;
4381 }
4382
4383 /**
4384 * tracecmd_open - create a tracecmd_handle from a given file
4385 * @file: the file name of the file that is of tracecmd data type.
4386 * @flags: bitmask of enum tracecmd_open_flags
4387 */
tracecmd_open(const char * file,int flags)4388 struct tracecmd_input *tracecmd_open(const char *file, int flags)
4389 {
4390 int fd;
4391
4392 fd = open(file, O_RDONLY);
4393 if (fd < 0)
4394 return NULL;
4395
4396 return tracecmd_open_fd(fd, flags);
4397 }
4398
4399 /**
4400 * tracecmd_open_head - create a tracecmd_handle from a given file, read
4401 * and parse only the trace headers from the file
4402 * @file: the file name of the file that is of tracecmd data type.
4403 * @flags: bitmask of enum tracecmd_open_flags
4404 */
tracecmd_open_head(const char * file,int flags)4405 struct tracecmd_input *tracecmd_open_head(const char *file, int flags)
4406 {
4407 struct tracecmd_input *handle;
4408 int fd;
4409
4410 fd = open(file, O_RDONLY);
4411 if (fd < 0)
4412 return NULL;
4413
4414 handle = tracecmd_alloc_fd(fd, flags);
4415 if (!handle)
4416 return NULL;
4417
4418 if (tracecmd_read_headers(handle, 0) < 0)
4419 goto fail;
4420
4421 return handle;
4422
4423 fail:
4424 tracecmd_close(handle);
4425 return NULL;
4426 }
4427
4428 /**
4429 * tracecmd_ref - add a reference to the handle
4430 * @handle: input handle for the trace.dat file
4431 *
4432 * Some applications may share a handle between parts of
4433 * the application. Let those parts add reference counters
4434 * to the handle, and the last one to close it will free it.
4435 */
tracecmd_ref(struct tracecmd_input * handle)4436 void tracecmd_ref(struct tracecmd_input *handle)
4437 {
4438 if (!handle)
4439 return;
4440
4441 handle->ref++;
4442 }
4443
free_buffer(struct input_buffer_instance * buf)4444 static inline void free_buffer(struct input_buffer_instance *buf)
4445 {
4446 free(buf->name);
4447 free(buf->clock);
4448 free(buf->cpu_data);
4449 }
4450
4451 /**
4452 * tracecmd_close - close and free the trace.dat handle
4453 * @handle: input handle for the trace.dat file
4454 *
4455 * Close the file descriptor of the handle and frees
4456 * the resources allocated by the handle.
4457 */
tracecmd_close(struct tracecmd_input * handle)4458 void tracecmd_close(struct tracecmd_input *handle)
4459 {
4460 struct zchunk_cache *cache;
4461 struct file_section *del_sec;
4462 struct cpu_data *cpu_data;
4463 struct page_map *page_map, *n;
4464 int cpu;
4465 int i;
4466
4467 if (!handle)
4468 return;
4469
4470 if (handle->ref <= 0) {
4471 tracecmd_warning("tracecmd: bad ref count on handle");
4472 return;
4473 }
4474
4475 if (--handle->ref)
4476 return;
4477
4478 for (cpu = 0; cpu < handle->cpus; cpu++) {
4479 /* The tracecmd_peek_data may have cached a record */
4480 free_next(handle, cpu);
4481 free_page(handle, cpu);
4482 if (handle->cpu_data) {
4483 cpu_data = &handle->cpu_data[cpu];
4484 if (cpu_data->kbuf) {
4485 kbuffer_free(cpu_data->kbuf);
4486 if (cpu_data->page_map)
4487 free_page_map(cpu_data->page_map);
4488
4489 if (cpu_data->page_cnt)
4490 tracecmd_warning("%d pages still allocated on cpu %d%s",
4491 cpu_data->page_cnt, cpu,
4492 show_records(cpu_data->pages,
4493 cpu_data->nr_pages));
4494 free(cpu_data->pages);
4495 }
4496 if (cpu_data->compress.fd >= 0) {
4497 close(cpu_data->compress.fd);
4498 unlink(cpu_data->compress.file);
4499 }
4500 while (!list_empty(&cpu_data->compress.cache)) {
4501 cache = container_of(cpu_data->compress.cache.next,
4502 struct zchunk_cache, list);
4503 list_del(&cache->list);
4504 free(cache->map);
4505 free(cache);
4506 }
4507 free(cpu_data->compress.chunks);
4508 list_for_each_entry_safe(page_map, n, &cpu_data->page_maps, list) {
4509 list_del(&page_map->list);
4510 free(page_map);
4511 }
4512 }
4513 }
4514
4515 free(handle->cpustats);
4516 free(handle->cpu_data);
4517 free(handle->uname);
4518 free(handle->trace_clock);
4519 free(handle->strings);
4520 free(handle->version);
4521 close(handle->fd);
4522 free(handle->latz.chunks);
4523 if (handle->latz.fd >= 0) {
4524 close(handle->latz.fd);
4525 unlink(handle->latz.file);
4526 }
4527 while (handle->sections) {
4528 del_sec = handle->sections;
4529 handle->sections = handle->sections->next;
4530 free(del_sec);
4531 }
4532
4533 free_buffer(&handle->top_buffer);
4534 for (i = 0; i < handle->nr_buffers; i++)
4535 free_buffer(&handle->buffers[i]);
4536 free(handle->buffers);
4537
4538 tracecmd_free_hooks(handle->hooks);
4539 handle->hooks = NULL;
4540
4541 trace_pid_map_free(handle->pid_maps);
4542 handle->pid_maps = NULL;
4543
4544 trace_tsync_offset_free(&handle->host);
4545 trace_guests_free(handle);
4546
4547 if (handle->flags & TRACECMD_FL_BUFFER_INSTANCE)
4548 tracecmd_close(handle->parent);
4549 else {
4550 /* Only main handle frees plugins, pevent and compression context */
4551 tracecmd_compress_destroy(handle->compress);
4552 tep_unload_plugins(handle->plugin_list, handle->pevent);
4553 tep_free(handle->pevent);
4554 }
4555 free(handle);
4556 }
4557
read_copy_size8(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle,unsigned long long * size)4558 static int read_copy_size8(struct tracecmd_input *in_handle,
4559 struct tracecmd_output *out_handle, unsigned long long *size)
4560 {
4561 /* read size */
4562 if (do_read_check(in_handle, size, 8))
4563 return -1;
4564
4565 if (do_write_check(out_handle, size, 8))
4566 return -1;
4567
4568 *size = tep_read_number(in_handle->pevent, size, 8);
4569 return 0;
4570 }
4571
read_copy_size4(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle,unsigned int * size)4572 static int read_copy_size4(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle,
4573 unsigned int *size)
4574 {
4575 /* read size */
4576 if (do_read_check(in_handle, size, 4))
4577 return -1;
4578
4579 if (do_write_check(out_handle, size, 4))
4580 return -1;
4581
4582 *size = tep_read_number(in_handle->pevent, size, 4);
4583 return 0;
4584 }
4585
read_copy_data(struct tracecmd_input * in_handle,unsigned long long size,struct tracecmd_output * out_handle)4586 static int read_copy_data(struct tracecmd_input *in_handle,
4587 unsigned long long size,
4588 struct tracecmd_output *out_handle)
4589 {
4590 char *buf;
4591
4592 buf = malloc(size);
4593 if (!buf)
4594 return -1;
4595 if (do_read_check(in_handle, buf, size))
4596 goto failed_read;
4597
4598 if (do_write_check(out_handle, buf, size))
4599 goto failed_read;
4600
4601 free(buf);
4602
4603 return 0;
4604
4605 failed_read:
4606 free(buf);
4607 return -1;
4608 }
4609
4610
check_in_state(struct tracecmd_input * handle,int new_state)4611 static bool check_in_state(struct tracecmd_input *handle, int new_state)
4612 {
4613 return check_file_state(handle->file_version, handle->file_state, new_state);
4614 }
4615
copy_header_files(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle)4616 static int copy_header_files(struct tracecmd_input *in_handle,
4617 struct tracecmd_output *out_handle)
4618 {
4619 bool compress = out_check_compression(out_handle);
4620 struct file_section *sec;
4621 unsigned long long offset;
4622 unsigned long long size;
4623
4624 if (!check_in_state(in_handle, TRACECMD_FILE_HEADERS) ||
4625 !check_out_state(out_handle, TRACECMD_FILE_HEADERS))
4626 return -1;
4627
4628 sec = section_open(in_handle, TRACECMD_OPTION_HEADER_INFO);
4629 if (!sec)
4630 return -1;
4631
4632 offset = out_write_section_header(out_handle, TRACECMD_OPTION_HEADER_INFO,
4633 "headers", TRACECMD_SEC_FL_COMPRESS, true);
4634 out_compression_start(out_handle, compress);
4635
4636 /* "header_page" */
4637 if (read_copy_data(in_handle, 12, out_handle) < 0)
4638 goto error;
4639
4640 if (read_copy_size8(in_handle, out_handle, &size) < 0)
4641 goto error;
4642
4643 if (read_copy_data(in_handle, size, out_handle) < 0)
4644 goto error;
4645
4646 /* "header_event" */
4647 if (read_copy_data(in_handle, 13, out_handle) < 0)
4648 goto error;
4649
4650 if (read_copy_size8(in_handle, out_handle, &size) < 0)
4651 goto error;
4652
4653 if (read_copy_data(in_handle, size, out_handle) < 0)
4654 goto error;
4655
4656 in_handle->file_state = TRACECMD_FILE_HEADERS;
4657 if (out_compression_end(out_handle, compress))
4658 goto error;
4659
4660 out_set_file_state(out_handle, in_handle->file_state);
4661 section_close(in_handle, sec);
4662
4663 if (out_update_section_header(out_handle, offset))
4664 goto error;
4665
4666 return 0;
4667 error:
4668 out_compression_reset(out_handle, compress);
4669 section_close(in_handle, sec);
4670 return -1;
4671 }
4672
copy_ftrace_files(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle)4673 static int copy_ftrace_files(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle)
4674 {
4675 bool compress = out_check_compression(out_handle);
4676 struct file_section *sec;
4677 unsigned long long offset;
4678 unsigned long long size;
4679 unsigned int count;
4680 unsigned int i;
4681
4682 if (!check_in_state(in_handle, TRACECMD_FILE_FTRACE_EVENTS) ||
4683 !check_out_state(out_handle, TRACECMD_FILE_FTRACE_EVENTS))
4684 return -1;
4685
4686 sec = section_open(in_handle, TRACECMD_OPTION_FTRACE_EVENTS);
4687 if (!sec)
4688 return -1;
4689 offset = out_write_section_header(out_handle, TRACECMD_OPTION_FTRACE_EVENTS,
4690 "ftrace events", TRACECMD_SEC_FL_COMPRESS, true);
4691
4692 out_compression_start(out_handle, compress);
4693
4694 if (read_copy_size4(in_handle, out_handle, &count) < 0)
4695 goto error;
4696
4697 for (i = 0; i < count; i++) {
4698
4699 if (read_copy_size8(in_handle, out_handle, &size) < 0)
4700 goto error;
4701
4702 if (read_copy_data(in_handle, size, out_handle) < 0)
4703 goto error;
4704 }
4705
4706 in_handle->file_state = TRACECMD_FILE_FTRACE_EVENTS;
4707 if (out_compression_end(out_handle, compress))
4708 goto error;
4709
4710 out_set_file_state(out_handle, in_handle->file_state);
4711
4712 section_close(in_handle, sec);
4713
4714 if (out_update_section_header(out_handle, offset))
4715 goto error;
4716
4717 return 0;
4718 error:
4719 out_compression_reset(out_handle, compress);
4720 section_close(in_handle, sec);
4721 return -1;
4722 }
4723
copy_event_files(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle)4724 static int copy_event_files(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle)
4725 {
4726 bool compress = out_check_compression(out_handle);
4727 struct file_section *sec;
4728 unsigned long long offset;
4729 unsigned long long size;
4730 char *system;
4731 unsigned int systems;
4732 unsigned int count;
4733 unsigned int i,x;
4734
4735 if (!check_in_state(in_handle, TRACECMD_FILE_ALL_EVENTS) ||
4736 !check_out_state(out_handle, TRACECMD_FILE_ALL_EVENTS))
4737 return -1;
4738
4739 sec = section_open(in_handle, TRACECMD_OPTION_EVENT_FORMATS);
4740 if (!sec)
4741 return -1;
4742 offset = out_write_section_header(out_handle, TRACECMD_OPTION_EVENT_FORMATS,
4743 "events format", TRACECMD_SEC_FL_COMPRESS, true);
4744
4745 out_compression_start(out_handle, compress);
4746
4747 if (read_copy_size4(in_handle, out_handle, &systems) < 0)
4748 goto error;
4749
4750 for (i = 0; i < systems; i++) {
4751 system = read_string(in_handle);
4752 if (!system)
4753 goto error;
4754 if (do_write_check(out_handle, system, strlen(system) + 1)) {
4755 free(system);
4756 goto error;
4757 }
4758 free(system);
4759
4760 if (read_copy_size4(in_handle, out_handle, &count) < 0)
4761 goto error;
4762
4763 for (x=0; x < count; x++) {
4764 if (read_copy_size8(in_handle, out_handle, &size) < 0)
4765 goto error;
4766
4767 if (read_copy_data(in_handle, size, out_handle) < 0)
4768 goto error;
4769 }
4770 }
4771
4772 in_handle->file_state = TRACECMD_FILE_ALL_EVENTS;
4773 if (out_compression_end(out_handle, compress))
4774 goto error;
4775
4776 out_set_file_state(out_handle, in_handle->file_state);
4777
4778 section_close(in_handle, sec);
4779
4780 if (out_update_section_header(out_handle, offset))
4781 goto error;
4782
4783 return 0;
4784 error:
4785 out_compression_reset(out_handle, compress);
4786 section_close(in_handle, sec);
4787 return -1;
4788 }
4789
copy_proc_kallsyms(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle)4790 static int copy_proc_kallsyms(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle)
4791 {
4792 bool compress = out_check_compression(out_handle);
4793 struct file_section *sec;
4794 unsigned long long offset;
4795 unsigned int size;
4796
4797 if (!check_in_state(in_handle, TRACECMD_FILE_KALLSYMS) ||
4798 !check_out_state(out_handle, TRACECMD_FILE_KALLSYMS))
4799 return -1;
4800
4801 sec = section_open(in_handle, TRACECMD_OPTION_KALLSYMS);
4802 if (!sec)
4803 return -1;
4804 offset = out_write_section_header(out_handle, TRACECMD_OPTION_KALLSYMS,
4805 "kallsyms", TRACECMD_SEC_FL_COMPRESS, true);
4806
4807 out_compression_start(out_handle, compress);
4808 if (read_copy_size4(in_handle, out_handle, &size) < 0)
4809 goto error;
4810
4811 if (!size)
4812 goto out; /* OK? */
4813
4814 if (read_copy_data(in_handle, size, out_handle) < 0)
4815 goto error;
4816 out:
4817 in_handle->file_state = TRACECMD_FILE_KALLSYMS;
4818 if (out_compression_end(out_handle, compress))
4819 goto error;
4820
4821 out_set_file_state(out_handle, in_handle->file_state);
4822
4823 section_close(in_handle, sec);
4824
4825 if (out_update_section_header(out_handle, offset))
4826 goto error;
4827
4828 return 0;
4829 error:
4830 out_compression_reset(out_handle, compress);
4831 section_close(in_handle, sec);
4832 return -1;
4833 }
4834
copy_ftrace_printk(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle)4835 static int copy_ftrace_printk(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle)
4836 {
4837 bool compress = out_check_compression(out_handle);
4838 struct file_section *sec;
4839 unsigned long long offset;
4840 unsigned int size;
4841
4842 if (!check_in_state(in_handle, TRACECMD_FILE_PRINTK) ||
4843 !check_out_state(out_handle, TRACECMD_FILE_PRINTK))
4844 return -1;
4845
4846 sec = section_open(in_handle, TRACECMD_OPTION_PRINTK);
4847 if (!sec)
4848 return -1;
4849
4850 offset = out_write_section_header(out_handle, TRACECMD_OPTION_PRINTK,
4851 "printk", TRACECMD_SEC_FL_COMPRESS, true);
4852
4853 out_compression_start(out_handle, compress);
4854
4855 if (read_copy_size4(in_handle, out_handle, &size) < 0)
4856 goto error;
4857
4858 if (!size)
4859 goto out; /* OK? */
4860
4861 if (read_copy_data(in_handle, size, out_handle) < 0)
4862 goto error;
4863
4864 out:
4865 in_handle->file_state = TRACECMD_FILE_PRINTK;
4866 if (out_compression_end(out_handle, compress))
4867 goto error;
4868
4869 out_set_file_state(out_handle, in_handle->file_state);
4870
4871 section_close(in_handle, sec);
4872
4873 if (out_update_section_header(out_handle, offset))
4874 goto error;
4875
4876 return 0;
4877 error:
4878 out_compression_reset(out_handle, compress);
4879 section_close(in_handle, sec);
4880 return -1;
4881 }
4882
copy_command_lines(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle)4883 static int copy_command_lines(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle)
4884 {
4885 bool compress = out_check_compression(out_handle);
4886 struct file_section *sec;
4887 unsigned long long offset;
4888 unsigned long long size;
4889
4890 if (!check_in_state(in_handle, TRACECMD_FILE_CMD_LINES) ||
4891 !check_out_state(out_handle, TRACECMD_FILE_CMD_LINES))
4892 return -1;
4893
4894 sec = section_open(in_handle, TRACECMD_OPTION_CMDLINES);
4895 if (!sec)
4896 return -1;
4897 offset = out_write_section_header(out_handle, TRACECMD_OPTION_CMDLINES,
4898 "command lines", TRACECMD_SEC_FL_COMPRESS, true);
4899
4900 out_compression_start(out_handle, compress);
4901
4902 if (read_copy_size8(in_handle, out_handle, &size) < 0)
4903 goto error;
4904
4905 if (!size)
4906 goto out; /* OK? */
4907
4908 if (read_copy_data(in_handle, size, out_handle) < 0)
4909 goto error;
4910
4911 out:
4912 in_handle->file_state = TRACECMD_FILE_CMD_LINES;
4913 if (out_compression_end(out_handle, compress))
4914 goto error;
4915
4916 out_set_file_state(out_handle, in_handle->file_state);
4917
4918 section_close(in_handle, sec);
4919
4920 if (out_update_section_header(out_handle, offset))
4921 goto error;
4922
4923 return 0;
4924 error:
4925 out_compression_reset(out_handle, compress);
4926 section_close(in_handle, sec);
4927 return -1;
4928 }
4929
copy_cpu_count(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle)4930 static int copy_cpu_count(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle)
4931 {
4932 unsigned int cpus;
4933
4934 if (!check_in_state(in_handle, TRACECMD_FILE_CPU_COUNT) ||
4935 !check_out_state(out_handle, TRACECMD_FILE_CPU_COUNT))
4936 return -1;
4937
4938 if (!HAS_SECTIONS(in_handle)) {
4939 if (read4(in_handle, &cpus))
4940 return -1;
4941 } else {
4942 cpus = in_handle->max_cpu;
4943 }
4944
4945 if (tracecmd_get_out_file_version(out_handle) < FILE_VERSION_SECTIONS) {
4946 cpus = tep_read_number(in_handle->pevent, &cpus, 4);
4947 if (do_write_check(out_handle, &cpus, 4))
4948 return -1;
4949 } else {
4950 tracecmd_add_option(out_handle, TRACECMD_OPTION_CPUCOUNT, sizeof(int), &cpus);
4951 }
4952
4953 in_handle->file_state = TRACECMD_FILE_CPU_COUNT;
4954 out_set_file_state(out_handle, in_handle->file_state);
4955
4956 return 0;
4957 }
4958
4959 /**
4960 * tracecmd_copy_headers - Copy headers from a tracecmd_input handle to a file descriptor
4961 * @in_handle: input handle for the trace.dat file to copy from.
4962 * @out_handle: output handle to the trace.dat file to copy to.
4963 * @start_state: The file state to start copying from (zero for the beginnig)
4964 * @end_state: The file state to stop at (zero for up to cmdlines)
4965 *
4966 * This is used to copy trace header data of a trace.dat file to a
4967 * file descriptor. Using @start_state and @end_state it may be used
4968 * multiple times against the input handle.
4969 *
4970 * NOTE: The input handle is also modified, and ends at the end
4971 * state as well.
4972 */
tracecmd_copy_headers(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle,enum tracecmd_file_states start_state,enum tracecmd_file_states end_state)4973 int tracecmd_copy_headers(struct tracecmd_input *in_handle,
4974 struct tracecmd_output *out_handle,
4975 enum tracecmd_file_states start_state,
4976 enum tracecmd_file_states end_state)
4977 {
4978 struct file_section *sec = NULL;
4979 int ret;
4980
4981 if (!start_state)
4982 start_state = TRACECMD_FILE_HEADERS;
4983 if (!end_state)
4984 end_state = TRACECMD_FILE_CMD_LINES;
4985
4986 if (start_state > end_state)
4987 return -1;
4988
4989 if (end_state < TRACECMD_FILE_HEADERS)
4990 return 0;
4991
4992 if (in_handle->file_state >= start_state) {
4993 /* Set the handle to just before the start state */
4994 sec = section_open(in_handle, TRACECMD_OPTION_HEADER_INFO);
4995 if (!sec)
4996 return -1;
4997 /* Now that the file handle has moved, change its state */
4998 in_handle->file_state = TRACECMD_FILE_INIT;
4999 }
5000
5001 /* Try to bring the input up to the start state - 1 */
5002 ret = tracecmd_read_headers(in_handle, start_state - 1);
5003 if (sec)
5004 section_close(in_handle, sec);
5005 if (ret < 0)
5006 goto out;
5007
5008 switch (start_state) {
5009 case TRACECMD_FILE_HEADERS:
5010 ret = copy_header_files(in_handle, out_handle);
5011 if (ret < 0)
5012 goto out;
5013
5014 /* fallthrough */
5015 case TRACECMD_FILE_FTRACE_EVENTS:
5016 /* handle's state is now updating with the copies */
5017 if (end_state <= in_handle->file_state)
5018 return 0;
5019
5020 ret = copy_ftrace_files(in_handle, out_handle);
5021 if (ret < 0)
5022 goto out;
5023
5024 /* fallthrough */
5025 case TRACECMD_FILE_ALL_EVENTS:
5026 if (end_state <= in_handle->file_state)
5027 return 0;
5028
5029 ret = copy_event_files(in_handle, out_handle);
5030 if (ret < 0)
5031 goto out;
5032
5033 /* fallthrough */
5034 case TRACECMD_FILE_KALLSYMS:
5035 if (end_state <= in_handle->file_state)
5036 return 0;
5037
5038 ret = copy_proc_kallsyms(in_handle, out_handle);
5039 if (ret < 0)
5040 goto out;
5041
5042 /* fallthrough */
5043 case TRACECMD_FILE_PRINTK:
5044 if (end_state <= in_handle->file_state)
5045 return 0;
5046
5047 ret = copy_ftrace_printk(in_handle, out_handle);
5048 if (ret < 0)
5049 goto out;
5050
5051 /* fallthrough */
5052 case TRACECMD_FILE_CMD_LINES:
5053 if (end_state <= in_handle->file_state)
5054 return 0;
5055
5056 ret = copy_command_lines(in_handle, out_handle);
5057 if (ret < 0)
5058 goto out;
5059
5060 /* fallthrough */
5061 case TRACECMD_FILE_CPU_COUNT:
5062 if (end_state <= in_handle->file_state)
5063 return 0;
5064
5065 ret = copy_cpu_count(in_handle, out_handle);
5066 if (ret < 0)
5067 goto out;
5068
5069 /* fallthrough */
5070 default:
5071 break;
5072 }
5073
5074 out:
5075 return ret < 0 ? -1 : 0;
5076 }
5077
tracecmd_copy_buffer_descr(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle)5078 int tracecmd_copy_buffer_descr(struct tracecmd_input *in_handle,
5079 struct tracecmd_output *out_handle)
5080 {
5081 int i;
5082
5083 if (tracecmd_get_out_file_version(out_handle) >= FILE_VERSION_SECTIONS)
5084 return 0;
5085
5086 for (i = 0; i < in_handle->nr_buffers; i++)
5087 tracecmd_add_buffer_info(out_handle, in_handle->buffers[i].name, 0);
5088
5089 return tracecmd_write_buffer_info(out_handle);
5090 }
5091
copy_options_recursive(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle)5092 static int copy_options_recursive(struct tracecmd_input *in_handle,
5093 struct tracecmd_output *out_handle)
5094 {
5095 unsigned short id, flags = 0;
5096 unsigned short option, en2;
5097 unsigned long long next;
5098 unsigned int size, en4;
5099 bool skip;
5100
5101 for (;;) {
5102 if (do_read_check(in_handle, &option, 2))
5103 return -1;
5104
5105 en2 = tep_read_number(in_handle->pevent, &option, 2);
5106
5107 if (en2 == TRACECMD_OPTION_DONE && !HAS_SECTIONS(in_handle))
5108 return 0;
5109
5110 /* next 4 bytes is the size of the option */
5111 if (do_read_check(in_handle, &size, 4))
5112 return -1;
5113
5114 en4 = tep_read_number(in_handle->pevent, &size, 4);
5115 if (en2 == TRACECMD_OPTION_DONE) {
5116 /* option done v7 */
5117 if (en4 < 8)
5118 return -1;
5119
5120 if (read8(in_handle, &next))
5121 return -1;
5122
5123 if (!next)
5124 break;
5125
5126 if (do_lseek(in_handle, next, SEEK_SET) == (off64_t)-1)
5127 return -1;
5128
5129 if (read_section_header(in_handle, &id, &flags, NULL, NULL))
5130 return -1;
5131
5132 if (id != TRACECMD_OPTION_DONE)
5133 return -1;
5134
5135 if (flags & TRACECMD_SEC_FL_COMPRESS && in_uncompress_block(in_handle))
5136 return -1;
5137
5138 return copy_options_recursive(in_handle, out_handle);
5139 }
5140 /* Do not copy these, as they have file specific offsets */
5141 switch (en2) {
5142 case TRACECMD_OPTION_BUFFER:
5143 case TRACECMD_OPTION_BUFFER_TEXT:
5144 case TRACECMD_OPTION_HEADER_INFO:
5145 case TRACECMD_OPTION_FTRACE_EVENTS:
5146 case TRACECMD_OPTION_EVENT_FORMATS:
5147 case TRACECMD_OPTION_KALLSYMS:
5148 case TRACECMD_OPTION_PRINTK:
5149 case TRACECMD_OPTION_CMDLINES:
5150 skip = true;
5151 break;
5152 default:
5153 skip = false;
5154 break;
5155 }
5156 if (skip) {
5157 do_lseek(in_handle, en4, SEEK_CUR);
5158 continue;
5159 }
5160 if (do_write_check(out_handle, &option, 2))
5161 return -1;
5162
5163 if (do_write_check(out_handle, &size, 4))
5164 return -1;
5165
5166 if (read_copy_data(in_handle, en4, out_handle))
5167 return -1;
5168 }
5169
5170 return 0;
5171 }
5172
copy_options(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle)5173 static int copy_options(struct tracecmd_input *in_handle, struct tracecmd_output *out_handle)
5174 {
5175 unsigned long long offset, start;
5176 unsigned short id, en2, flags = 0;
5177 int tmp;
5178
5179 if (HAS_SECTIONS(in_handle)) {
5180 if (read_section_header(in_handle, &id, &flags, NULL, NULL))
5181 return -1;
5182
5183 if (id != TRACECMD_OPTION_DONE)
5184 return -1;
5185
5186 if (flags & TRACECMD_SEC_FL_COMPRESS && in_uncompress_block(in_handle))
5187 return -1;
5188 }
5189 start = tracecmd_get_out_file_offset(out_handle);
5190 if (tracecmd_get_out_file_version(out_handle) < FILE_VERSION_SECTIONS) {
5191 if (do_write_check(out_handle, "options ", 10))
5192 return -1;
5193 }
5194
5195 offset = out_write_section_header(out_handle, TRACECMD_OPTION_DONE, "options", 0, false);
5196
5197 if (copy_options_recursive(in_handle, out_handle))
5198 goto error;
5199
5200 id = TRACECMD_OPTION_DONE;
5201 en2 = tep_read_number(in_handle->pevent, &id, 2);
5202 if (do_write_check(out_handle, &en2, 2))
5203 goto error;
5204
5205 if (tracecmd_get_out_file_version(out_handle) < FILE_VERSION_SECTIONS) {
5206 out_save_options_offset(out_handle, start);
5207 } else {
5208 tmp = 8;
5209 if (do_write_check(out_handle, &tmp, 4))
5210 goto error;
5211
5212 out_save_options_offset(out_handle, start);
5213 start = 0;
5214 if (do_write_check(out_handle, &start, 8))
5215 goto error;
5216 }
5217 out_update_section_header(out_handle, offset);
5218 if (flags & TRACECMD_SEC_FL_COMPRESS)
5219 in_uncompress_reset(in_handle);
5220 in_handle->file_state = TRACECMD_FILE_OPTIONS;
5221 out_set_file_state(out_handle, in_handle->file_state);
5222 /* Append local options */
5223 return tracecmd_append_options(out_handle);
5224
5225 error:
5226 if (flags & TRACECMD_SEC_FL_COMPRESS)
5227 in_uncompress_reset(in_handle);
5228 return 0;
5229 }
5230
tracecmd_copy_options(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle)5231 int tracecmd_copy_options(struct tracecmd_input *in_handle,
5232 struct tracecmd_output *out_handle)
5233 {
5234 if (!check_in_state(in_handle, TRACECMD_FILE_OPTIONS) ||
5235 !check_out_state(out_handle, TRACECMD_FILE_OPTIONS))
5236 return -1;
5237
5238 if (!in_handle->options_start)
5239 return 0;
5240
5241 if (lseek64(in_handle->fd, in_handle->options_start, SEEK_SET) == (off64_t)-1)
5242 return -1;
5243
5244 if (copy_options(in_handle, out_handle) < 0)
5245 return -1;
5246
5247 return 0;
5248 }
5249
copy_trace_latency(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle,const char * buf_name)5250 static int copy_trace_latency(struct tracecmd_input *in_handle,
5251 struct tracecmd_output *out_handle, const char *buf_name)
5252 {
5253 int page_size = getpagesize();
5254 unsigned long long wsize;
5255 unsigned long long offset;
5256 int fd;
5257
5258 if (tracecmd_get_out_file_version(out_handle) < FILE_VERSION_SECTIONS &&
5259 do_write_check(out_handle, "latency ", 10))
5260 return -1;
5261
5262 offset = tracecmd_get_out_file_offset(out_handle);
5263
5264 if (tracecmd_get_out_file_version(out_handle) >= FILE_VERSION_SECTIONS &&
5265 !out_add_buffer_option(out_handle, buf_name, TRACECMD_OPTION_BUFFER_TEXT,
5266 offset, 0, NULL, page_size))
5267 return -1;
5268
5269 offset = out_write_section_header(out_handle, TRACECMD_OPTION_BUFFER_TEXT,
5270 "buffer latency", TRACECMD_SEC_FL_COMPRESS, false);
5271
5272 if (in_handle->latz.fd >= 0)
5273 fd = in_handle->latz.fd;
5274 else
5275 fd = in_handle->fd;
5276
5277 if (!out_copy_fd_compress(out_handle, fd, 0, &wsize, page_size))
5278 return -1;
5279
5280 if (out_update_section_header(out_handle, offset))
5281 return -1;
5282
5283 out_set_file_state(out_handle, TRACECMD_FILE_CPU_LATENCY);
5284 return 0;
5285 }
5286
copy_trace_flyrecord_data(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle,const char * buff_name)5287 static int copy_trace_flyrecord_data(struct tracecmd_input *in_handle,
5288 struct tracecmd_output *out_handle, const char *buff_name)
5289 {
5290 struct cpu_data_source *data;
5291 int total_size = 0;
5292 int cpus;
5293 int ret;
5294 int i, j;
5295
5296 if (tracecmd_get_out_file_version(out_handle) < FILE_VERSION_SECTIONS)
5297 cpus = in_handle->max_cpu;
5298 else
5299 cpus = in_handle->cpus;
5300
5301 data = calloc(cpus, sizeof(struct cpu_data_source));
5302 if (!data)
5303 return -1;
5304
5305 for (i = 0; i < in_handle->cpus; i++) {
5306 j = in_handle->cpu_data[i].cpu;
5307 data[j].size = in_handle->cpu_data[i].file_size;
5308 total_size += data[j].size;
5309 if (in_handle->cpu_data[i].compress.fd >= 0) {
5310 data[j].fd = in_handle->cpu_data[i].compress.fd;
5311 data[j].offset = 0;
5312 } else {
5313 data[j].fd = in_handle->fd;
5314 data[j].offset = in_handle->cpu_data[i].file_offset;
5315 }
5316 }
5317 if (total_size || tracecmd_get_out_file_version(out_handle) < FILE_VERSION_SECTIONS)
5318 ret = out_write_cpu_data(out_handle, cpus, data, buff_name);
5319 else
5320 ret = 0;
5321 free(data);
5322
5323 return ret;
5324 }
5325
copy_flyrecord_buffer(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle,int index)5326 static int copy_flyrecord_buffer(struct tracecmd_input *in_handle,
5327 struct tracecmd_output *out_handle, int index)
5328 {
5329 struct tracecmd_input *instance;
5330 const char *name;
5331 int ret;
5332
5333 name = tracecmd_buffer_instance_name(in_handle, index);
5334 if (!name)
5335 return -1;
5336
5337 instance = tracecmd_buffer_instance_handle(in_handle, index);
5338 if (!instance)
5339 return -1;
5340
5341 if (!tracecmd_get_quiet(out_handle) && *name)
5342 fprintf(stderr, "\nBuffer: %s\n\n", name);
5343
5344 if (in_handle->buffers[index].latency)
5345 ret = copy_trace_latency(in_handle, out_handle, name);
5346 else
5347 ret = copy_trace_flyrecord_data(instance, out_handle, name);
5348 tracecmd_close(instance);
5349
5350 return ret;
5351 }
5352
copy_trace_data_from_v6(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle)5353 static int copy_trace_data_from_v6(struct tracecmd_input *in_handle,
5354 struct tracecmd_output *out_handle)
5355 {
5356 char buf[10];
5357 int ret;
5358 int i;
5359
5360 if (do_read_check(in_handle, buf, 10))
5361 return -1;
5362
5363 if (strncmp(buf, "latency", 7) == 0)
5364 in_handle->file_state = TRACECMD_FILE_CPU_LATENCY;
5365 else if (strncmp(buf, "flyrecord", 9) == 0)
5366 in_handle->file_state = TRACECMD_FILE_CPU_FLYRECORD;
5367
5368 tracecmd_init_data(in_handle);
5369 tracecmd_set_out_clock(out_handle, in_handle->trace_clock);
5370
5371 if (in_handle->file_state == TRACECMD_FILE_CPU_LATENCY)
5372 return copy_trace_latency(in_handle, out_handle, "");
5373
5374 /* top instance */
5375 ret = copy_trace_flyrecord_data(in_handle, out_handle, "");
5376 if (ret)
5377 return ret;
5378
5379 for (i = 0; i < in_handle->nr_buffers; i++)
5380 copy_flyrecord_buffer(in_handle, out_handle, i);
5381
5382 return 0;
5383 }
5384
copy_trace_data_from_v7(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle)5385 static int copy_trace_data_from_v7(struct tracecmd_input *in_handle,
5386 struct tracecmd_output *out_handle)
5387 {
5388 int ret = 0;
5389 int i;
5390
5391 /* Force using temporary files for trace data decompression */
5392 in_handle->read_zpage = false;
5393 tracecmd_init_data(in_handle);
5394 tracecmd_set_out_clock(out_handle, in_handle->trace_clock);
5395
5396 /* copy top buffer */
5397 if (in_handle->top_buffer.latency)
5398 ret = copy_trace_latency(in_handle, out_handle, in_handle->top_buffer.name);
5399 else if (in_handle->top_buffer.cpus)
5400 ret = copy_trace_flyrecord_data(in_handle, out_handle,
5401 in_handle->top_buffer.name);
5402 else if (tracecmd_get_out_file_version(out_handle) < FILE_VERSION_SECTIONS)
5403 ret = out_write_emty_cpu_data(out_handle, in_handle->max_cpu);
5404 if (ret)
5405 return ret;
5406
5407 for (i = 0; i < in_handle->nr_buffers; i++)
5408 copy_flyrecord_buffer(in_handle, out_handle, i);
5409
5410 return 0;
5411 }
5412
tracecmd_copy_trace_data(struct tracecmd_input * in_handle,struct tracecmd_output * out_handle)5413 __hidden int tracecmd_copy_trace_data(struct tracecmd_input *in_handle,
5414 struct tracecmd_output *out_handle)
5415 {
5416 int ret;
5417
5418 if (!check_in_state(in_handle, TRACECMD_FILE_CPU_FLYRECORD) ||
5419 !check_out_state(out_handle, TRACECMD_FILE_CPU_FLYRECORD))
5420 return -1;
5421
5422 if (in_handle->file_version < FILE_VERSION_SECTIONS)
5423 ret = copy_trace_data_from_v6(in_handle, out_handle);
5424 else
5425 ret = copy_trace_data_from_v7(in_handle, out_handle);
5426
5427 return ret;
5428 }
5429
5430 /**
5431 * tracecmd_record_at_buffer_start - return true if record is first on subbuffer
5432 * @handle: input handle for the trace.dat file
5433 * @record: The record to test if it is the first record on page
5434 *
5435 * Returns true if the record is the first record on the page.
5436 */
tracecmd_record_at_buffer_start(struct tracecmd_input * handle,struct tep_record * record)5437 int tracecmd_record_at_buffer_start(struct tracecmd_input *handle,
5438 struct tep_record *record)
5439 {
5440 struct page *page = record->priv;
5441 struct kbuffer *kbuf = handle->cpu_data[record->cpu].kbuf;
5442 int offset;
5443
5444 if (!page || !kbuf)
5445 return 0;
5446
5447 offset = record->offset - page->offset;
5448 return offset == kbuffer_start_of_data(kbuf);
5449 }
5450
tracecmd_page_ts(struct tracecmd_input * handle,struct tep_record * record)5451 unsigned long long tracecmd_page_ts(struct tracecmd_input *handle,
5452 struct tep_record *record)
5453 {
5454 struct page *page = record->priv;
5455 struct kbuffer *kbuf = handle->cpu_data[record->cpu].kbuf;
5456
5457 if (!page || !kbuf)
5458 return 0;
5459
5460 return kbuffer_subbuf_timestamp(kbuf, page->map);
5461 }
5462
tracecmd_record_ts_delta(struct tracecmd_input * handle,struct tep_record * record)5463 unsigned int tracecmd_record_ts_delta(struct tracecmd_input *handle,
5464 struct tep_record *record)
5465 {
5466 struct kbuffer *kbuf = handle->cpu_data[record->cpu].kbuf;
5467 struct page *page = record->priv;
5468 int offset;
5469
5470 if (!page || !kbuf)
5471 return 0;
5472
5473 offset = record->offset - page->offset;
5474
5475 return kbuffer_ptr_delta(kbuf, page->map + offset);
5476 }
5477
tracecmd_record_kbuf(struct tracecmd_input * handle,struct tep_record * record)5478 struct kbuffer *tracecmd_record_kbuf(struct tracecmd_input *handle,
5479 struct tep_record *record)
5480 {
5481 return handle->cpu_data[record->cpu].kbuf;
5482 }
5483
tracecmd_record_page(struct tracecmd_input * handle,struct tep_record * record)5484 void *tracecmd_record_page(struct tracecmd_input *handle,
5485 struct tep_record *record)
5486 {
5487 struct page *page = record->priv;
5488
5489 return page ? page->map : NULL;
5490 }
5491
tracecmd_record_offset(struct tracecmd_input * handle,struct tep_record * record)5492 void *tracecmd_record_offset(struct tracecmd_input *handle,
5493 struct tep_record *record)
5494 {
5495 struct page *page = record->priv;
5496 int offset;
5497
5498 if (!page)
5499 return NULL;
5500
5501 offset = record->offset - page->offset;
5502
5503 return page->map + offset;
5504 }
5505
tracecmd_buffer_instances(struct tracecmd_input * handle)5506 int tracecmd_buffer_instances(struct tracecmd_input *handle)
5507 {
5508 return handle->nr_buffers;
5509 }
5510
tracecmd_buffer_instance_name(struct tracecmd_input * handle,int indx)5511 const char *tracecmd_buffer_instance_name(struct tracecmd_input *handle, int indx)
5512 {
5513 if (indx >= handle->nr_buffers)
5514 return NULL;
5515
5516 return handle->buffers[indx].name;
5517 }
5518
5519 struct tracecmd_input *
tracecmd_buffer_instance_handle(struct tracecmd_input * handle,int indx)5520 tracecmd_buffer_instance_handle(struct tracecmd_input *handle, int indx)
5521 {
5522 struct tracecmd_input *new_handle;
5523 struct input_buffer_instance *buffer = &handle->buffers[indx];
5524 size_t offset;
5525 ssize_t ret;
5526
5527 if (indx >= handle->nr_buffers)
5528 return NULL;
5529
5530 /*
5531 * We make a copy of the current handle, but we substitute
5532 * the cpu data with the cpu data for this buffer.
5533 */
5534 new_handle = malloc(sizeof(*handle));
5535 if (!new_handle)
5536 return NULL;
5537
5538 *new_handle = *handle;
5539 memset(&new_handle->top_buffer, 0, sizeof(new_handle->top_buffer));
5540 new_handle->cpu_data = NULL;
5541 new_handle->nr_buffers = 0;
5542 new_handle->buffers = NULL;
5543 new_handle->version = NULL;
5544 new_handle->sections = NULL;
5545 new_handle->strings = NULL;
5546 new_handle->guest = NULL;
5547 new_handle->ref = 1;
5548 if (handle->trace_clock) {
5549 new_handle->trace_clock = strdup(handle->trace_clock);
5550 if (!new_handle->trace_clock) {
5551 free(new_handle);
5552 return NULL;
5553 }
5554 }
5555 memset(&new_handle->host, 0, sizeof(new_handle->host));
5556 new_handle->parent = handle;
5557 new_handle->cpustats = NULL;
5558 new_handle->hooks = NULL;
5559 if (handle->uname)
5560 /* Ignore if fails to malloc, no biggy */
5561 new_handle->uname = strdup(handle->uname);
5562 tracecmd_ref(handle);
5563
5564 new_handle->fd = dup(handle->fd);
5565
5566 new_handle->flags |= TRACECMD_FL_BUFFER_INSTANCE;
5567
5568 new_handle->pid_maps = NULL;
5569 if (!HAS_SECTIONS(handle)) {
5570 /* Save where we currently are */
5571 offset = lseek64(handle->fd, 0, SEEK_CUR);
5572
5573 ret = lseek64(handle->fd, buffer->offset, SEEK_SET);
5574 if (ret == (off64_t)-1) {
5575 tracecmd_warning("could not seek to buffer %s offset %ld",
5576 buffer->name, buffer->offset);
5577 goto error;
5578 }
5579 /*
5580 * read_options_type() is called right after the CPU count so update
5581 * file state accordingly.
5582 */
5583 new_handle->file_state = TRACECMD_FILE_CPU_COUNT;
5584 ret = read_options_type(new_handle);
5585 if (!ret)
5586 ret = read_cpu_data(new_handle);
5587
5588 if (ret < 0) {
5589 tracecmd_warning("failed to read sub buffer %s", buffer->name);
5590 goto error;
5591 }
5592 ret = lseek64(handle->fd, offset, SEEK_SET);
5593 if (ret < 0) {
5594 tracecmd_warning("could not seek to back to offset %ld", offset);
5595 goto error;
5596 }
5597 } else {
5598 new_handle->page_size = handle->buffers[indx].page_size;
5599 if (init_buffer_cpu_data(new_handle, buffer) < 0)
5600 goto error;
5601 }
5602
5603 return new_handle;
5604
5605 error:
5606 tracecmd_close(new_handle);
5607 return NULL;
5608 }
5609
tracecmd_is_buffer_instance(struct tracecmd_input * handle)5610 int tracecmd_is_buffer_instance(struct tracecmd_input *handle)
5611 {
5612 return handle->flags & TRACECMD_FL_BUFFER_INSTANCE;
5613 }
5614
5615 /**
5616 * tracecmd_long_size - return the size of "long" for the arch
5617 * @handle: input handle for the trace.dat file
5618 */
tracecmd_long_size(struct tracecmd_input * handle)5619 int tracecmd_long_size(struct tracecmd_input *handle)
5620 {
5621 return handle->long_size;
5622 }
5623
5624 /**
5625 * tracecmd_page_size - return the PAGE_SIZE for the arch
5626 * @handle: input handle for the trace.dat file
5627 */
tracecmd_page_size(struct tracecmd_input * handle)5628 int tracecmd_page_size(struct tracecmd_input *handle)
5629 {
5630 return handle->page_size;
5631 }
5632
5633 /**
5634 * tracecmd_page_size - return the number of CPUs recorded
5635 * @handle: input handle for the trace.dat file
5636 */
tracecmd_cpus(struct tracecmd_input * handle)5637 int tracecmd_cpus(struct tracecmd_input *handle)
5638 {
5639 return handle->max_cpu;
5640 }
5641
5642 /**
5643 * tracecmd_get_tep - return the tep handle
5644 * @handle: input handle for the trace.dat file
5645 */
tracecmd_get_tep(struct tracecmd_input * handle)5646 struct tep_handle *tracecmd_get_tep(struct tracecmd_input *handle)
5647 {
5648 return handle->pevent;
5649 }
5650
5651 /**
5652 * tracecmd_get_in_file_version - return the trace.dat file version
5653 * @handle: input handle for the trace.dat file
5654 */
tracecmd_get_in_file_version(struct tracecmd_input * handle)5655 unsigned long tracecmd_get_in_file_version(struct tracecmd_input *handle)
5656 {
5657 return handle->file_version;
5658 }
5659
5660 /**
5661 * tracecmd_get_file_compress_proto - get name and version of compression algorithm
5662 * @handle: input handle for the trace.dat file
5663 * @name: return, name of the compression algorithm.
5664 * @version: return, version of the compression algorithm.
5665 *
5666 * Get the name and the version of the compression algorithm, used to
5667 * compress the file associated with @handle.
5668 * Returns 0 on success, or -1 in case of an error. If 0 is returned,
5669 * the name and version of the algorithm are stored in @name and @version.
5670 * The returned strings must *not* be freed.
5671 */
tracecmd_get_file_compress_proto(struct tracecmd_input * handle,const char ** name,const char ** version)5672 int tracecmd_get_file_compress_proto(struct tracecmd_input *handle,
5673 const char **name, const char **version)
5674 {
5675 return tracecmd_compress_proto_get_name(handle->compress, name, version);
5676 }
5677
5678 /**
5679 * tracecmd_get_use_trace_clock - return use_trace_clock
5680 * @handle: input handle for the trace.dat file
5681 */
tracecmd_get_use_trace_clock(struct tracecmd_input * handle)5682 bool tracecmd_get_use_trace_clock(struct tracecmd_input *handle)
5683 {
5684 return handle->use_trace_clock;
5685 }
5686
5687 /**
5688 * tracecmd_get_options_offset - get offset of the options sections in the file
5689 * @handle: input handle for the trace.dat file
5690 */
tracecmd_get_options_offset(struct tracecmd_input * handle)5691 size_t tracecmd_get_options_offset(struct tracecmd_input *handle)
5692 {
5693 return handle->options_start;
5694 }
5695
5696 /**
5697 * tracecmd_get_trace_clock - return the saved trace clock
5698 * @handle: input handle for the trace.dat file
5699 *
5700 * Returns a string of the clock that was saved in the trace.dat file.
5701 * The string should not be freed, as it points to the internal
5702 * structure data.
5703 */
tracecmd_get_trace_clock(struct tracecmd_input * handle)5704 const char *tracecmd_get_trace_clock(struct tracecmd_input *handle)
5705 {
5706 return handle->trace_clock;
5707 }
5708
5709 /**
5710 * tracecmd_get_cpustats - return the saved cpu stats
5711 * @handle: input handle for the trace.dat file
5712 *
5713 * Provides a method to extract the cpu stats saved in @handle.
5714 *
5715 * Returns a string of the cpu stats that was saved in the trace.dat file.
5716 * The string should not be freed, as it points to the internal
5717 * structure data.
5718 */
tracecmd_get_cpustats(struct tracecmd_input * handle)5719 const char *tracecmd_get_cpustats(struct tracecmd_input *handle)
5720 {
5721 return handle->cpustats;
5722 }
5723
5724 /**
5725 * tracecmd_get_uname - return the saved name and kernel information
5726 * @handle: input handle for the trace.dat file
5727 *
5728 * Provides a method to extract the system information saved in @handle.
5729 *
5730 * Returns a string of the system information that was saved in the
5731 * trace.dat file.
5732 * The string should not be freed, as it points to the internal
5733 * structure data.
5734 */
tracecmd_get_uname(struct tracecmd_input * handle)5735 const char *tracecmd_get_uname(struct tracecmd_input *handle)
5736 {
5737 return handle->uname;
5738 }
5739
5740 /**
5741 * tracecmd_get_version - return the saved version information
5742 * @handle: input handle for the trace.dat file
5743 *
5744 * Provides a method to extract the version string saved in @handle.
5745 *
5746 * Returns a string of the version that was saved in the trace.dat file.
5747 * The string should not be freed, as it points to the internal
5748 * structure data.
5749 */
tracecmd_get_version(struct tracecmd_input * handle)5750 const char *tracecmd_get_version(struct tracecmd_input *handle)
5751 {
5752 return handle->version;
5753 }
5754
5755 /**
5756 * tracecmd_get_cpu_file_size - return the saved cpu file size
5757 * @handle: input handle for the trace.dat file
5758 * @cpu: cpu index
5759 *
5760 * Provides a method to extract the cpu file size saved in @handle.
5761 *
5762 * Returns the cpu file size saved in trace.dat file or (off64_t)-1 for
5763 * invalid cpu index.
5764 */
tracecmd_get_cpu_file_size(struct tracecmd_input * handle,int cpu)5765 off64_t tracecmd_get_cpu_file_size(struct tracecmd_input *handle, int cpu)
5766 {
5767 if (cpu < 0 || cpu >= handle->cpus)
5768 return (off64_t)-1;
5769 return handle->cpu_data[cpu].file_size;
5770 }
5771
5772 /**
5773 * tracecmd_get_show_data_func - return the show data func
5774 * @handle: input handle for the trace.dat file
5775 */
5776 tracecmd_show_data_func
tracecmd_get_show_data_func(struct tracecmd_input * handle)5777 tracecmd_get_show_data_func(struct tracecmd_input *handle)
5778 {
5779 return handle->show_data_func;
5780 }
5781
5782 /**
5783 * tracecmd_set_show_data_func - set the show data func
5784 * @handle: input handle for the trace.dat file
5785 */
tracecmd_set_show_data_func(struct tracecmd_input * handle,tracecmd_show_data_func func)5786 void tracecmd_set_show_data_func(struct tracecmd_input *handle,
5787 tracecmd_show_data_func func)
5788 {
5789 handle->show_data_func = func;
5790 }
5791
5792 /**
5793 * tracecmd_get_traceid - get the trace id of the session
5794 * @handle: input handle for the trace.dat file
5795 *
5796 * Returns the trace id, written in the trace file
5797 */
tracecmd_get_traceid(struct tracecmd_input * handle)5798 unsigned long long tracecmd_get_traceid(struct tracecmd_input *handle)
5799 {
5800 return handle->trace_id;
5801 }
5802
5803 /**
5804 * tracecmd_get_first_ts - get the timestamp of the first recorded event
5805 * @handle: input handle for the trace.dat file
5806 *
5807 * Returns the timestamp of the first recorded event
5808 */
tracecmd_get_first_ts(struct tracecmd_input * handle)5809 unsigned long long tracecmd_get_first_ts(struct tracecmd_input *handle)
5810 {
5811 unsigned long long ts = 0;
5812 bool first = true;
5813 int i;
5814
5815 for (i = 0; i < handle->cpus; i++) {
5816 /* Ignore empty buffers */
5817 if (!handle->cpu_data[i].size)
5818 continue;
5819 if (first || ts > handle->cpu_data[i].first_ts)
5820 ts = handle->cpu_data[i].first_ts;
5821 first = false;
5822 }
5823
5824 return ts;
5825 }
5826
5827 /**
5828 * tracecmd_get_guest_cpumap - get the mapping of guest VCPU to host process
5829 * @handle: input handle for the trace.dat file
5830 * @trace_id: ID of the guest tracing session
5831 * @name: return, name of the guest
5832 * @vcpu_count: return, number of VPUs
5833 * @cpu_pid: return, array with guest VCPU to host process mapping
5834 *
5835 * Returns @name of the guest, number of VPUs (@vcpu_count)
5836 * and array @cpu_pid with size @vcpu_count. Array index is VCPU id, array
5837 * content is PID of the host process, running this VCPU.
5838 *
5839 * This information is stored in host trace.dat file
5840 */
tracecmd_get_guest_cpumap(struct tracecmd_input * handle,unsigned long long trace_id,const char ** name,int * vcpu_count,const int ** cpu_pid)5841 int tracecmd_get_guest_cpumap(struct tracecmd_input *handle,
5842 unsigned long long trace_id,
5843 const char **name,
5844 int *vcpu_count, const int **cpu_pid)
5845 {
5846 struct guest_trace_info *guest = handle->guest;
5847
5848 while (guest) {
5849 if (guest->trace_id == trace_id)
5850 break;
5851 guest = guest->next;
5852 }
5853 if (!guest)
5854 return -1;
5855
5856 if (name)
5857 *name = guest->name;
5858 if (vcpu_count)
5859 *vcpu_count = guest->vcpu_count;
5860 if (cpu_pid)
5861 *cpu_pid = guest->cpu_pid;
5862 return 0;
5863 }
5864
5865 /**
5866 * tracecmd_enable_tsync - enable / disable the timestamps correction
5867 * @handle: input handle for the trace.dat file
5868 * @enable: enable / disable the timestamps correction
5869 *
5870 * Enables or disables timestamps correction on file load, using the array of
5871 * recorded time offsets. If "enable" is true, but there are no time offsets,
5872 * function fails and -1 is returned.
5873 *
5874 * Returns -1 in case of an error, or 0 otherwise
5875 */
tracecmd_enable_tsync(struct tracecmd_input * handle,bool enable)5876 int tracecmd_enable_tsync(struct tracecmd_input *handle, bool enable)
5877 {
5878 if (enable &&
5879 (!handle->host.ts_offsets || !handle->host.cpu_count))
5880 return -1;
5881
5882 handle->host.sync_enable = enable;
5883
5884 return 0;
5885 }
5886
5887