• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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