1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
4 *
5 */
6 #include <dirent.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <libgen.h>
11 #include <getopt.h>
12 #include <stdarg.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <sys/wait.h>
16 #include <sys/mman.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <ctype.h>
20 #include <errno.h>
21
22 #include "list.h"
23 #include "trace-local.h"
24
25 static unsigned int page_size;
26 static const char *default_input_file = DEFAULT_INPUT_FILE;
27 static const char *default_top_instance_name = "top";
28 static const char *input_file;
29
30 enum split_types {
31 SPLIT_NONE,
32 /* The order of these must be reverse of the case statement in the options */
33 SPLIT_SECONDS,
34 SPLIT_MSECS,
35 SPLIT_USECS,
36 SPLIT_EVENTS,
37 SPLIT_PAGES,
38 SPLIT_NR_TYPES,
39 };
40
41 struct cpu_data {
42 unsigned long long ts;
43 unsigned long long offset;
44 unsigned long long missed_events;
45 struct tep_record *record;
46 int cpu;
47 int fd;
48 int index;
49 void *commit;
50 void *page;
51 char *file;
52 };
53
54 struct handle_list {
55 struct list_head list;
56 char *name;
57 int index;
58 struct tracecmd_input *handle;
59
60 /* Identify the top instance in the input trace. */
61 bool was_top_instance;
62 };
63
64 static struct list_head handle_list;
65
66 /**
67 * get_handle - Obtain a handle that must be closed once finished.
68 */
get_handle(struct handle_list * item)69 static struct tracecmd_input *get_handle(struct handle_list *item)
70 {
71 struct tracecmd_input *top_handle, *handle;
72
73 top_handle = tracecmd_open(input_file, 0);
74 if (!top_handle)
75 die("Error reading %s", input_file);
76
77 if (item->was_top_instance) {
78 return top_handle;
79 } else {
80 handle = tracecmd_buffer_instance_handle(top_handle, item->index);
81 if (!handle)
82 warning("Could not retrieve handle %s", item->name);
83
84 tracecmd_close(top_handle);
85 return handle;
86 }
87 }
88
add_handle(const char * name,int index,bool was_top_instance)89 static void add_handle(const char *name, int index, bool was_top_instance)
90 {
91 struct handle_list *item;
92
93 item = calloc(1, sizeof(*item));
94 if (!item)
95 die("Failed to allocate handle item");
96
97 item->name = strdup(name);
98 if (!item->name)
99 die("Failed to duplicate %s", name);
100
101 item->index = index;
102 item->was_top_instance = was_top_instance;
103 item->handle = get_handle(item);
104 list_add_tail(&item->list, &handle_list);
105 }
106
free_handles(struct list_head * list)107 static void free_handles(struct list_head *list)
108 {
109 struct handle_list *item, *n;
110
111 list_for_each_entry_safe(item, n, list, list) {
112 list_del(&item->list);
113 free(item->name);
114 tracecmd_close(item->handle);
115 free(item);
116 }
117 }
118
119 static struct list_head inst_list;
120
121 struct inst_list {
122 struct list_head list;
123 char *name;
124 struct handle_list *handle;
125
126 /* Identify the top instance in the input trace. */
127 bool was_top_instance;
128
129 /* Identify the top instance in the output trace. */
130 bool is_top_instance;
131 };
132
free_inst(struct list_head * list)133 static void free_inst(struct list_head *list)
134 {
135 struct inst_list *item, *n;
136
137 list_for_each_entry_safe(item, n, list, list) {
138 list_del(&item->list);
139 free(item->name);
140 free(item);
141 }
142 }
143
add_inst(const char * name,bool was_top_instance,bool is_top_instance)144 static struct inst_list *add_inst(const char *name, bool was_top_instance,
145 bool is_top_instance)
146 {
147 struct inst_list *item;
148
149 item = calloc(1, sizeof(*item));
150 if (!item)
151 die("Failed to allocate output_file item");
152
153 item->name = strdup(name);
154 if (!item->name)
155 die("Failed to duplicate %s", name);
156
157 item->was_top_instance = was_top_instance;
158 item->is_top_instance = is_top_instance;
159 list_add_tail(&item->list, &inst_list);
160 return item;
161 }
162
create_type_len(struct tep_handle * pevent,int time,int len)163 static int create_type_len(struct tep_handle *pevent, int time, int len)
164 {
165 static int bigendian = -1;
166 char *ptr;
167 int test;
168
169 if (bigendian < 0) {
170 test = 0x4321;
171 ptr = (char *)&test;
172 if (*ptr == 0x21)
173 bigendian = 0;
174 else
175 bigendian = 1;
176 }
177
178 if (tep_is_file_bigendian(pevent))
179 time |= (len << 27);
180 else
181 time = (time << 5) | len;
182
183 return tep_read_number(pevent, &time, 4);
184 }
185
write_record(struct tracecmd_input * handle,struct tep_record * record,struct cpu_data * cpu_data,enum split_types type)186 static int write_record(struct tracecmd_input *handle,
187 struct tep_record *record,
188 struct cpu_data *cpu_data,
189 enum split_types type)
190 {
191 unsigned long long diff;
192 struct tep_handle *pevent;
193 void *page;
194 int len = 0;
195 char *ptr;
196 int index = 0;
197 int time;
198
199 page = cpu_data->page;
200
201 pevent = tracecmd_get_tep(handle);
202
203 ptr = page + cpu_data->index;
204
205 diff = record->ts - cpu_data->ts;
206 if (diff > (1 << 27)) {
207 /* Add a time stamp */
208 len = RINGBUF_TYPE_TIME_EXTEND;
209 time = (unsigned int)(diff & ((1ULL << 27) - 1));
210 time = create_type_len(pevent, time, len);
211 *(unsigned *)ptr = time;
212 ptr += 4;
213 time = (unsigned int)(diff >> 27);
214 *(unsigned *)ptr = tep_read_number(pevent, &time, 4);
215 cpu_data->ts = record->ts;
216 cpu_data->index += 8;
217 return 0;
218 }
219
220 if (record->size && (record->size <= 28 * 4))
221 len = record->size / 4;
222
223 time = (unsigned)diff;
224 time = create_type_len(pevent, time, len);
225
226 memcpy(ptr, &time, 4);
227 ptr += 4;
228 index = 4;
229
230 if (!len) {
231 len = record->size + 4;
232 if ((len + 4) > record->record_size)
233 die("Bad calculation of record len (expect:%d actual:%d)",
234 record->record_size, len + 4);
235 *(unsigned *)ptr = tep_read_number(pevent, &len, 4);
236 ptr += 4;
237 index += 4;
238 }
239
240 len = (record->size + 3) & ~3;
241 index += len;
242
243 memcpy(ptr, record->data, len);
244
245 cpu_data->index += index;
246 cpu_data->ts = record->ts;
247
248 return 1;
249 }
250
251 #define MISSING_EVENTS (1UL << 31)
252 #define MISSING_STORED (1UL << 30)
253
254 #define COMMIT_MASK ((1 << 27) - 1)
255
write_page(struct tep_handle * pevent,struct cpu_data * cpu_data,int long_size)256 static void write_page(struct tep_handle *pevent,
257 struct cpu_data *cpu_data, int long_size)
258 {
259 unsigned long long *ptr = NULL;
260 unsigned int flags = 0;
261
262 if (cpu_data->missed_events) {
263 flags |= MISSING_EVENTS;
264 if (cpu_data->missed_events > 0) {
265 flags |= MISSING_STORED;
266 ptr = cpu_data->page + cpu_data->index;
267 }
268 }
269
270 if (long_size == 8) {
271 unsigned long long index = cpu_data->index - 16 + flags;;
272 *(unsigned long long *)cpu_data->commit =
273 tep_read_number(pevent, &index, 8);
274 } else {
275 unsigned int index = cpu_data->index - 12 + flags;;
276 *(unsigned int *)cpu_data->commit =
277 tep_read_number(pevent, &index, 4);
278 }
279 if (ptr)
280 *ptr = tep_read_number(pevent, &cpu_data->missed_events, 8);
281
282 write(cpu_data->fd, cpu_data->page, page_size);
283 }
284
read_record(struct tracecmd_input * handle,int percpu,int * cpu)285 static struct tep_record *read_record(struct tracecmd_input *handle,
286 int percpu, int *cpu)
287 {
288 if (percpu)
289 return tracecmd_read_data(handle, *cpu);
290
291 return tracecmd_read_next_data(handle, cpu);
292 }
293
set_cpu_time(struct tracecmd_input * handle,int percpu,unsigned long long start,int cpu,int cpus)294 static void set_cpu_time(struct tracecmd_input *handle,
295 int percpu, unsigned long long start, int cpu, int cpus)
296 {
297 if (percpu) {
298 tracecmd_set_cpu_to_timestamp(handle, cpu, start);
299 return;
300 }
301
302 for (cpu = 0; cpu < cpus; cpu++)
303 tracecmd_set_cpu_to_timestamp(handle, cpu, start);
304 return;
305 }
306
parse_cpu(struct tracecmd_input * handle,struct cpu_data * cpu_data,unsigned long long start,unsigned long long end,int count_limit,int percpu,int cpu,enum split_types type,bool * end_reached)307 static int parse_cpu(struct tracecmd_input *handle,
308 struct cpu_data *cpu_data,
309 unsigned long long start,
310 unsigned long long end,
311 int count_limit, int percpu, int cpu,
312 enum split_types type, bool *end_reached)
313 {
314 struct tep_record *record;
315 struct tep_handle *pevent;
316 void *ptr;
317 int page_size;
318 int long_size = 0;
319 int cpus;
320 int count = 0;
321 int pages = 0;
322
323 cpus = tracecmd_cpus(handle);
324
325 long_size = tracecmd_long_size(handle);
326 page_size = tracecmd_page_size(handle);
327 pevent = tracecmd_get_tep(handle);
328
329 /* Force new creation of first page */
330 if (percpu) {
331 cpu_data[cpu].index = page_size + 1;
332 cpu_data[cpu].page = NULL;
333 } else {
334 for (cpu = 0; cpu < cpus; cpu++) {
335 cpu_data[cpu].index = page_size + 1;
336 cpu_data[cpu].page = NULL;
337 }
338 }
339
340 /*
341 * Get the cpu pointers up to the start of the
342 * start time stamp.
343 */
344
345 record = read_record(handle, percpu, &cpu);
346
347 if (start) {
348 set_cpu_time(handle, percpu, start, cpu, cpus);
349 while (record && record->ts < start) {
350 tracecmd_free_record(record);
351 record = read_record(handle, percpu, &cpu);
352 }
353 } else if (record)
354 start = record->ts;
355
356 while (record && (!end || record->ts <= end)) {
357 if ((cpu_data[cpu].index + record->record_size > page_size) ||
358 record->missed_events) {
359
360 if (type == SPLIT_PAGES && ++pages > count_limit)
361 break;
362
363 if (cpu_data[cpu].page)
364 write_page(pevent, &cpu_data[cpu], long_size);
365 else {
366 cpu_data[cpu].page = malloc(page_size);
367 if (!cpu_data[cpu].page)
368 die("Failed to allocate page");
369 }
370
371 cpu_data[cpu].missed_events = record->missed_events;
372
373 memset(cpu_data[cpu].page, 0, page_size);
374 ptr = cpu_data[cpu].page;
375
376 *(unsigned long long*)ptr =
377 tep_read_number(pevent, &(record->ts), 8);
378 cpu_data[cpu].ts = record->ts;
379 ptr += 8;
380 cpu_data[cpu].commit = ptr;
381 ptr += long_size;
382 cpu_data[cpu].index = 8 + long_size;
383 }
384
385 cpu_data[cpu].offset = record->offset;
386
387 if (write_record(handle, record, &cpu_data[cpu], type)) {
388 tracecmd_free_record(record);
389 record = read_record(handle, percpu, &cpu);
390
391 /* if we hit the end of the cpu, clear the offset */
392 if (!record) {
393 if (percpu)
394 cpu_data[cpu].offset = 0;
395 else
396 for (cpu = 0; cpu < cpus; cpu++)
397 cpu_data[cpu].offset = 0;
398 }
399
400 switch (type) {
401 case SPLIT_NONE:
402 break;
403 case SPLIT_SECONDS:
404 if (record &&
405 record->ts >
406 (start + (unsigned long long)count_limit * 1000000000ULL)) {
407 tracecmd_free_record(record);
408 record = NULL;
409 }
410 break;
411 case SPLIT_MSECS:
412 if (record &&
413 record->ts >
414 (start + (unsigned long long)count_limit * 1000000ULL)) {
415 tracecmd_free_record(record);
416 record = NULL;
417 }
418 break;
419 case SPLIT_USECS:
420 if (record &&
421 record->ts >
422 (start + (unsigned long long)count_limit * 1000ULL)) {
423 tracecmd_free_record(record);
424 record = NULL;
425 }
426 break;
427 case SPLIT_EVENTS:
428 if (++count >= count_limit) {
429 tracecmd_free_record(record);
430 record = NULL;
431 }
432 break;
433 default:
434 break;
435 }
436 }
437 }
438
439 if (record && (record->ts > end))
440 *end_reached = true;
441 else
442 *end_reached = false;
443
444 if (record)
445 tracecmd_free_record(record);
446
447 if (percpu) {
448 if (cpu_data[cpu].page) {
449 write_page(pevent, &cpu_data[cpu], long_size);
450 free(cpu_data[cpu].page);
451 cpu_data[cpu].page = NULL;
452 }
453 } else {
454 for (cpu = 0; cpu < cpus; cpu++) {
455 if (cpu_data[cpu].page) {
456 write_page(pevent, &cpu_data[cpu], long_size);
457 free(cpu_data[cpu].page);
458 cpu_data[cpu].page = NULL;
459 }
460 }
461 }
462
463 return 0;
464 }
465
get_temp_file(const char * output_file,const char * name,int cpu)466 static char *get_temp_file(const char *output_file, const char *name, int cpu)
467 {
468 const char *dot;
469 char *file = NULL;
470 char *output;
471 char *base;
472 char *dir;
473 int ret;
474
475 if (name)
476 dot = ".";
477 else
478 dot = name = "";
479
480 output = strdup(output_file);
481 if (!output)
482 die("Failed to duplicate %s", output_file);
483
484 /* Extract basename() first, as dirname() truncates output */
485 base = basename(output);
486 dir = dirname(output);
487
488 ret = asprintf(&file, "%s/.tmp.%s.%s%s%d", dir, base, name, dot, cpu);
489 if (ret < 0)
490 die("Failed to allocate file for %s %s %s %d", dir, base, name, cpu);
491 free(output);
492 return file;
493 }
494
delete_temp_file(const char * name)495 static void delete_temp_file(const char *name)
496 {
497 unlink(name);
498 }
499
put_temp_file(char * file)500 static void put_temp_file(char *file)
501 {
502 free(file);
503 }
504
touch_file(const char * file)505 static void touch_file(const char *file)
506 {
507 int fd;
508
509 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
510 if (fd < 0)
511 die("could not create file %s\n", file);
512 close(fd);
513 }
514
parse_file(struct tracecmd_input * handle,const char * output_file,unsigned long long start,unsigned long long end,int percpu,int only_cpu,int count,enum split_types type,bool * end_reached)515 static unsigned long long parse_file(struct tracecmd_input *handle,
516 const char *output_file,
517 unsigned long long start,
518 unsigned long long end, int percpu,
519 int only_cpu, int count,
520 enum split_types type,
521 bool *end_reached)
522 {
523 unsigned long long current = 0;
524 struct tracecmd_output *ohandle;
525 struct inst_list *inst_entry;
526 struct cpu_data *cpu_data;
527 struct tep_record *record;
528 bool all_end_reached = true;
529 char **cpu_list;
530 char *file;
531 int cpus;
532 int cpu;
533 int ret;
534 int fd;
535
536 ohandle = tracecmd_copy(handle, output_file, TRACECMD_FILE_CMD_LINES, 0, NULL);
537 tracecmd_set_out_clock(ohandle, tracecmd_get_trace_clock(handle));
538
539 list_for_each_entry(inst_entry, &inst_list, list) {
540 struct tracecmd_input *curr_handle;
541 bool curr_end_reached = false;
542
543 curr_handle = inst_entry->handle->handle;
544 cpus = tracecmd_cpus(curr_handle);
545 cpu_data = malloc(sizeof(*cpu_data) * cpus);
546 if (!cpu_data)
547 die("Failed to allocate cpu_data for %d cpus", cpus);
548
549 for (cpu = 0; cpu < cpus; cpu++) {
550 file = get_temp_file(output_file, inst_entry->name, cpu);
551 touch_file(file);
552
553 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
554 cpu_data[cpu].cpu = cpu;
555 cpu_data[cpu].fd = fd;
556 cpu_data[cpu].file = file;
557 cpu_data[cpu].offset = 0;
558 if (start)
559 tracecmd_set_cpu_to_timestamp(curr_handle, cpu, start);
560 }
561
562 if (only_cpu >= 0) {
563 parse_cpu(curr_handle, cpu_data, start, end, count,
564 1, only_cpu, type, &curr_end_reached);
565 } else if (percpu) {
566 for (cpu = 0; cpu < cpus; cpu++)
567 parse_cpu(curr_handle, cpu_data, start,
568 end, count, percpu, cpu, type, &curr_end_reached);
569 } else {
570 parse_cpu(curr_handle, cpu_data, start,
571 end, count, percpu, -1, type, &curr_end_reached);
572 }
573
574 /* End is reached when all instances finished. */
575 all_end_reached &= curr_end_reached;
576
577 cpu_list = malloc(sizeof(*cpu_list) * cpus);
578 if (!cpu_list)
579 die("Failed to allocate cpu_list for %d cpus", cpus);
580 for (cpu = 0; cpu < cpus; cpu++)
581 cpu_list[cpu] = cpu_data[cpu].file;
582
583 if (inst_entry->is_top_instance)
584 ret = tracecmd_append_cpu_data(ohandle, cpus, cpu_list);
585 else
586 ret = tracecmd_append_buffer_cpu_data(ohandle, inst_entry->name, cpus,
587 cpu_list);
588 if (ret < 0)
589 die("Failed to append tracing data\n");
590
591 for (cpu = 0; cpu < cpus; cpu++) {
592 /* Set the tracecmd cursor to the next set of records */
593 if (cpu_data[cpu].offset) {
594 record = tracecmd_read_at(curr_handle, cpu_data[cpu].offset, NULL);
595 if (record && (!current || record->ts > current))
596 current = record->ts + 1;
597 tracecmd_free_record(record);
598 }
599 }
600
601 for (cpu = 0; cpu < cpus; cpu++) {
602 close(cpu_data[cpu].fd);
603 delete_temp_file(cpu_data[cpu].file);
604 put_temp_file(cpu_data[cpu].file);
605 }
606 free(cpu_data);
607 free(cpu_list);
608 }
609
610 tracecmd_output_close(ohandle);
611
612 *end_reached = all_end_reached;
613 return current;
614 }
615
616 /* Map the instance names to their handle. */
map_inst_handle(void)617 static void map_inst_handle(void)
618 {
619 struct handle_list *handle_entry;
620 struct inst_list *inst_entry;
621
622 /*
623 * No specific instance was given for this output file.
624 * Add all the available instances.
625 */
626 if (list_empty(&inst_list)) {
627 list_for_each_entry(handle_entry, &handle_list, list) {
628 add_inst(handle_entry->name, handle_entry->was_top_instance,
629 handle_entry->was_top_instance);
630 }
631 }
632
633 list_for_each_entry(inst_entry, &inst_list, list) {
634 list_for_each_entry(handle_entry, &handle_list, list) {
635 if ((inst_entry->was_top_instance &&
636 handle_entry->was_top_instance) ||
637 (!inst_entry->was_top_instance &&
638 !strcmp(handle_entry->name, inst_entry->name))) {
639 inst_entry->handle = handle_entry;
640 goto found;
641 }
642 }
643
644 warning("Requested instance %s was not found in trace.", inst_entry->name);
645 break;
646 found:
647 continue;
648 }
649 }
650
is_top_instance_unique(void)651 static bool is_top_instance_unique(void)
652 {
653 struct inst_list *inst_entry;
654 bool has_top_buffer = false;
655
656 /* Check there is at most one top buffer. */
657 list_for_each_entry(inst_entry, &inst_list, list) {
658 if (inst_entry->is_top_instance) {
659 if (has_top_buffer)
660 return false;
661 has_top_buffer = true;
662 }
663 }
664
665 return true;
666 }
667
668 enum {
669 OPT_top = 237,
670 };
671
672 /*
673 * Used to identify the arg. previously parsed.
674 * E.g. '-b' can only follow '--top'.
675 */
676 enum prev_arg_type {
677 PREV_IS_NONE,
678 PREV_IS_TOP,
679 PREV_IS_BUFFER,
680 };
681
trace_split(int argc,char ** argv)682 void trace_split (int argc, char **argv)
683 {
684 struct tracecmd_input *handle;
685 unsigned long long start_ns = 0, end_ns = 0;
686 unsigned long long current;
687 enum prev_arg_type prev_arg_type;
688 struct inst_list *prev_inst = NULL;
689 int prev_arg_idx;
690 bool end_reached = false;
691 double start, end;
692 char *endptr;
693 char *output = NULL;
694 char *output_file;
695 enum split_types split_type = SPLIT_NONE;
696 enum split_types type = SPLIT_NONE;
697 int instances;
698 int count;
699 int repeat = 0;
700 int percpu = 0;
701 int cpu = -1;
702 int ac;
703 int c;
704
705 static struct option long_options[] = {
706 {"top", optional_argument, NULL, OPT_top},
707 {NULL, 0, NULL, 0},
708 };
709 int option_index = 0;
710
711 prev_arg_type = PREV_IS_NONE;
712
713 list_head_init(&handle_list);
714 list_head_init(&inst_list);
715
716 if (strcmp(argv[1], "split") != 0)
717 usage(argv);
718
719 while ((c = getopt_long(argc - 1, argv + 1, "+ho:i:s:m:u:e:p:rcC:B:b:t",
720 long_options, &option_index)) >= 0) {
721 switch (c) {
722 case 'h':
723 usage(argv);
724 break;
725 case 'p':
726 type++;
727 case 'e':
728 type++;
729 case 'u':
730 type++;
731 case 'm':
732 type++;
733 case 's':
734 type++;
735 if (split_type != SPLIT_NONE)
736 die("Only one type of split is allowed");
737 count = atoi(optarg);
738 if (count <= 0)
739 die("Units must be greater than 0");
740 split_type = type;
741
742 /* Spliting by pages only makes sense per cpu */
743 if (type == SPLIT_PAGES)
744 percpu = 1;
745 break;
746 case 'r':
747 repeat = 1;
748 break;
749 case 'c':
750 percpu = 1;
751 break;
752 case 'C':
753 cpu = atoi(optarg);
754 break;
755 case 'o':
756 if (output)
757 die("only one output file allowed");
758 output = strdup(optarg);
759 break;
760 case 'i':
761 input_file = optarg;
762 break;
763 case OPT_top:
764 prev_arg_type = PREV_IS_TOP;
765 prev_arg_idx = optind;
766 prev_inst = add_inst(default_top_instance_name, true, true);
767 break;
768 case 'b':
769 /* 1 as --top takes no argument. */
770 if (prev_arg_type != PREV_IS_TOP &&
771 (prev_arg_idx != optind - 1))
772 usage(argv);
773 prev_arg_type = PREV_IS_NONE;
774
775 prev_inst->is_top_instance = false;
776
777 free(prev_inst->name);
778 prev_inst->name = strdup(optarg);
779 if (!prev_inst->name)
780 die("Failed to duplicate %s", optarg);
781 break;
782 case 'B':
783 prev_arg_type = PREV_IS_BUFFER;
784 prev_arg_idx = optind;
785 prev_inst = add_inst(optarg, false, false);
786 break;
787 case 't':
788 /* 2 as -B takes an argument. */
789 if (prev_arg_type != PREV_IS_BUFFER &&
790 (prev_arg_idx != optind - 2))
791 usage(argv);
792 prev_arg_type = PREV_IS_NONE;
793
794 prev_inst->is_top_instance = true;
795 break;
796 default:
797 usage(argv);
798 }
799 }
800
801 if (!is_top_instance_unique())
802 die("Can only have one top instance.");
803
804 ac = (argc - optind);
805
806 if (ac >= 2) {
807 optind++;
808 start = strtod(argv[optind], &endptr);
809 if (ac > 3)
810 usage(argv);
811
812 /* Make sure a true start value was entered */
813 if (*endptr != 0)
814 die("Start value not floating point: %s", argv[optind]);
815
816 start_ns = (unsigned long long)(start * 1000000000.0);
817 optind++;
818 if (ac == 3) {
819 end = strtod(argv[optind], &endptr);
820
821 /* Make sure a true end value was entered */
822 if (*endptr != 0)
823 die("End value not floating point: %s",
824 argv[optind]);
825
826 end_ns = (unsigned long long)(end * 1000000000.0);
827 if (end_ns < start_ns)
828 die("Error: end is less than start");
829 }
830 }
831
832 if (!input_file)
833 input_file = default_input_file;
834
835 handle = tracecmd_open(input_file, 0);
836 if (!handle)
837 die("error reading %s", input_file);
838
839 if (tracecmd_get_file_state(handle) == TRACECMD_FILE_CPU_LATENCY)
840 die("trace-cmd split does not work with latency traces\n");
841
842 page_size = tracecmd_page_size(handle);
843
844 if (!output)
845 output = strdup(input_file);
846
847 if (!repeat && strcmp(output, input_file) == 0) {
848 output = realloc(output, strlen(output) + 3);
849 strcat(output, ".1");
850 }
851
852 output_file = malloc(strlen(output) + 50);
853 if (!output_file)
854 die("Failed to allocate for %s", output);
855 c = 1;
856
857 add_handle(default_top_instance_name, -1, true);
858 instances = tracecmd_buffer_instances(handle);
859 if (instances) {
860 const char *name;
861 int i;
862
863 for (i = 0; i < instances; i++) {
864 name = tracecmd_buffer_instance_name(handle, i);
865 if (!name)
866 die("error in reading buffer instance");
867 add_handle(name, i, false);
868 }
869 }
870
871 map_inst_handle();
872
873 do {
874 if (repeat)
875 sprintf(output_file, "%s.%04d", output, c++);
876 else
877 strcpy(output_file, output);
878
879 current = parse_file(handle, output_file, start_ns, end_ns,
880 percpu, cpu, count, type, &end_reached);
881
882 if (!repeat)
883 break;
884 start_ns = 0;
885 } while (!end_reached && (current && (!end_ns || current < end_ns)));
886
887 free(output);
888 free(output_file);
889
890 tracecmd_close(handle);
891 free_handles(&handle_list);
892 free_inst(&inst_list);
893
894 return;
895 }
896