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