xref: /aosp_15_r20/external/trace-cmd/tracecmd/trace-split.c (revision 58e6ee5f017f6a8912852c892d18457e4bafb554)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <[email protected]>
4  *
5  */
6 #define _LARGEFILE64_SOURCE
7 #include <dirent.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <libgen.h>
12 #include <getopt.h>
13 #include <stdarg.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <sys/wait.h>
17 #include <sys/mman.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <ctype.h>
21 #include <errno.h>
22 
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 *input_file;
28 
29 enum split_types {
30 	SPLIT_NONE,
31 	/* The order of these must be reverse of the case statement in the options */
32 	SPLIT_SECONDS,
33 	SPLIT_MSECS,
34 	SPLIT_USECS,
35 	SPLIT_EVENTS,
36 	SPLIT_PAGES,
37 	SPLIT_NR_TYPES,
38 };
39 
40 struct cpu_data {
41 	unsigned long long		ts;
42 	unsigned long long		offset;
43 	struct tep_record		*record;
44 	int				cpu;
45 	int				fd;
46 	int				index;
47 	void				*commit;
48 	void				*page;
49 	char				*file;
50 };
51 
create_type_len(struct tep_handle * pevent,int time,int len)52 static int create_type_len(struct tep_handle *pevent, int time, int len)
53 {
54 	static int bigendian = -1;
55 	char *ptr;
56 	int test;
57 
58 	if (bigendian < 0) {
59 		test = 0x4321;
60 		ptr = (char *)&test;
61 		if (*ptr == 0x21)
62 			bigendian = 0;
63 		else
64 			bigendian = 1;
65 	}
66 
67 	if (tep_is_file_bigendian(pevent))
68 		time |= (len << 27);
69 	else
70 		time = (time << 5) | len;
71 
72 	return tep_read_number(pevent, &time, 4);
73 }
74 
write_record(struct tracecmd_input * handle,struct tep_record * record,struct cpu_data * cpu_data,enum split_types type)75 static int write_record(struct tracecmd_input *handle,
76 			struct tep_record *record,
77 			struct cpu_data *cpu_data,
78 			enum split_types type)
79 {
80 	unsigned long long diff;
81 	struct tep_handle *pevent;
82 	void *page;
83 	int len = 0;
84 	char *ptr;
85 	int index = 0;
86 	int time;
87 
88 	page = cpu_data->page;
89 
90 	pevent = tracecmd_get_tep(handle);
91 
92 	ptr = page + cpu_data->index;
93 
94 	diff = record->ts - cpu_data->ts;
95 	if (diff > (1 << 27)) {
96 		/* Add a time stamp */
97 		len = RINGBUF_TYPE_TIME_EXTEND;
98 		time = (unsigned int)(diff & ((1ULL << 27) - 1));
99 		time = create_type_len(pevent, time, len);
100 		*(unsigned *)ptr = time;
101 		ptr += 4;
102 		time = (unsigned int)(diff >> 27);
103 		*(unsigned *)ptr = tep_read_number(pevent, &time, 4);
104 		cpu_data->ts = record->ts;
105 		cpu_data->index += 8;
106 		return 0;
107 	}
108 
109 	if (record->size && (record->size <= 28 * 4))
110 		len = record->size / 4;
111 
112 	time = (unsigned)diff;
113 	time = create_type_len(pevent, time, len);
114 
115 	memcpy(ptr, &time, 4);
116 	ptr += 4;
117 	index = 4;
118 
119 	if (!len) {
120 		len = record->size + 4;
121 		if ((len + 4) > record->record_size)
122 			die("Bad calculation of record len (expect:%d actual:%d)",
123 			    record->record_size, len + 4);
124 		*(unsigned *)ptr = tep_read_number(pevent, &len, 4);
125 		ptr += 4;
126 		index += 4;
127 	}
128 
129 	len = (record->size + 3) & ~3;
130 	index += len;
131 
132 	memcpy(ptr, record->data, len);
133 
134 	cpu_data->index += index;
135 	cpu_data->ts = record->ts;
136 
137 	return 1;
138 }
139 
write_page(struct tep_handle * pevent,struct cpu_data * cpu_data,int long_size)140 static void write_page(struct tep_handle *pevent,
141 		       struct cpu_data *cpu_data, int long_size)
142 {
143 	if (long_size == 8) {
144 		unsigned long long index = cpu_data->index - 16;
145 		*(unsigned long long *)cpu_data->commit =
146 				tep_read_number(pevent, &index, 8);
147 	} else {
148 		unsigned int index = cpu_data->index - 12;
149 		*(unsigned int *)cpu_data->commit =
150 			tep_read_number(pevent, &index, 4);
151 	}
152 	write(cpu_data->fd, cpu_data->page, page_size);
153 }
154 
read_record(struct tracecmd_input * handle,int percpu,int * cpu)155 static struct tep_record *read_record(struct tracecmd_input *handle,
156 				      int percpu, int *cpu)
157 {
158 	if (percpu)
159 		return tracecmd_read_data(handle, *cpu);
160 
161 	return tracecmd_read_next_data(handle, cpu);
162 }
163 
set_cpu_time(struct tracecmd_input * handle,int percpu,unsigned long long start,int cpu,int cpus)164 static void set_cpu_time(struct tracecmd_input *handle,
165 			 int percpu, unsigned long long start, int cpu, int cpus)
166 {
167 	if (percpu) {
168 		tracecmd_set_cpu_to_timestamp(handle, cpu, start);
169 		return;
170 	}
171 
172 	for (cpu = 0; cpu < cpus; cpu++)
173 		tracecmd_set_cpu_to_timestamp(handle, cpu, start);
174 	return;
175 }
176 
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)177 static int parse_cpu(struct tracecmd_input *handle,
178 		     struct cpu_data *cpu_data,
179 		     unsigned long long start,
180 		     unsigned long long end,
181 		     int count_limit, int percpu, int cpu,
182 		     enum split_types type)
183 {
184 	struct tep_record *record;
185 	struct tep_handle *pevent;
186 	void *ptr;
187 	int page_size;
188 	int long_size = 0;
189 	int cpus;
190 	int count = 0;
191 	int pages = 0;
192 
193 	cpus = tracecmd_cpus(handle);
194 
195 	long_size = tracecmd_long_size(handle);
196 	page_size = tracecmd_page_size(handle);
197 	pevent = tracecmd_get_tep(handle);
198 
199 	/* Force new creation of first page */
200 	if (percpu) {
201 		cpu_data[cpu].index = page_size + 1;
202 		cpu_data[cpu].page = NULL;
203 	} else {
204 		for (cpu = 0; cpu < cpus; cpu++) {
205 			cpu_data[cpu].index = page_size + 1;
206 			cpu_data[cpu].page = NULL;
207 		}
208 	}
209 
210 	/*
211 	 * Get the cpu pointers up to the start of the
212 	 * start time stamp.
213 	 */
214 
215 	record = read_record(handle, percpu, &cpu);
216 
217 	if (start) {
218 		set_cpu_time(handle, percpu, start, cpu, cpus);
219 		while (record && record->ts < start) {
220 			tracecmd_free_record(record);
221 			record = read_record(handle, percpu, &cpu);
222 		}
223 	} else if (record)
224 		start = record->ts;
225 
226 	while (record && (!end || record->ts <= end)) {
227 		if (cpu_data[cpu].index + record->record_size > page_size) {
228 
229 			if (type == SPLIT_PAGES && ++pages > count_limit)
230 				break;
231 
232 			if (cpu_data[cpu].page)
233 				write_page(pevent, &cpu_data[cpu], long_size);
234 			else {
235 				cpu_data[cpu].page = malloc(page_size);
236 				if (!cpu_data[cpu].page)
237 					die("Failed to allocate page");
238 			}
239 
240 			memset(cpu_data[cpu].page, 0, page_size);
241 			ptr = cpu_data[cpu].page;
242 
243 			*(unsigned long long*)ptr =
244 				tep_read_number(pevent, &(record->ts), 8);
245 			cpu_data[cpu].ts = record->ts;
246 			ptr += 8;
247 			cpu_data[cpu].commit = ptr;
248 			ptr += long_size;
249 			cpu_data[cpu].index = 8 + long_size;
250 		}
251 
252 		cpu_data[cpu].offset = record->offset;
253 
254 		if (write_record(handle, record, &cpu_data[cpu], type)) {
255 			tracecmd_free_record(record);
256 			record = read_record(handle, percpu, &cpu);
257 
258 			/* if we hit the end of the cpu, clear the offset */
259 			if (!record) {
260 				if (percpu)
261 					cpu_data[cpu].offset = 0;
262 				else
263 					for (cpu = 0; cpu < cpus; cpu++)
264 						cpu_data[cpu].offset = 0;
265 			}
266 
267 			switch (type) {
268 			case SPLIT_NONE:
269 				break;
270 			case SPLIT_SECONDS:
271 				if (record &&
272 				    record->ts >
273 				    (start + (unsigned long long)count_limit * 1000000000ULL)) {
274 					tracecmd_free_record(record);
275 					record = NULL;
276 				}
277 				break;
278 			case SPLIT_MSECS:
279 				if (record &&
280 				    record->ts >
281 				    (start + (unsigned long long)count_limit * 1000000ULL)) {
282 					tracecmd_free_record(record);
283 					record = NULL;
284 				}
285 				break;
286 			case SPLIT_USECS:
287 				if (record &&
288 				    record->ts >
289 				    (start + (unsigned long long)count_limit * 1000ULL)) {
290 					tracecmd_free_record(record);
291 					record = NULL;
292 				}
293 				break;
294 			case SPLIT_EVENTS:
295 				if (++count >= count_limit) {
296 					tracecmd_free_record(record);
297 					record = NULL;
298 				}
299 				break;
300 			default:
301 				break;
302 			}
303 		}
304 	}
305 
306 	if (record)
307 		tracecmd_free_record(record);
308 
309 	if (percpu) {
310 		if (cpu_data[cpu].page) {
311 			write_page(pevent, &cpu_data[cpu], long_size);
312 			free(cpu_data[cpu].page);
313 			cpu_data[cpu].page = NULL;
314 		}
315 	} else {
316 		for (cpu = 0; cpu < cpus; cpu++) {
317 			if (cpu_data[cpu].page) {
318 				write_page(pevent, &cpu_data[cpu], long_size);
319 				free(cpu_data[cpu].page);
320 				cpu_data[cpu].page = NULL;
321 			}
322 		}
323 	}
324 
325 	return 0;
326 }
327 
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)328 static double parse_file(struct tracecmd_input *handle,
329 			 const char *output_file,
330 			 unsigned long long start,
331 			 unsigned long long end, int percpu, int only_cpu,
332 			 int count, enum split_types type)
333 {
334 	unsigned long long current;
335 	struct tracecmd_output *ohandle;
336 	struct cpu_data *cpu_data;
337 	struct tep_record *record;
338 	char **cpu_list;
339 	char *output;
340 	char *base;
341 	char *file;
342 	char *dir;
343 	int cpus;
344 	int cpu;
345 	int fd;
346 
347 	output = strdup(output_file);
348 	dir = dirname(output);
349 	base = basename(output);
350 
351 	ohandle = tracecmd_copy(handle, output_file, TRACECMD_FILE_CMD_LINES, 0, NULL);
352 
353 	cpus = tracecmd_cpus(handle);
354 	cpu_data = malloc(sizeof(*cpu_data) * cpus);
355 	if (!cpu_data)
356 		die("Failed to allocate cpu_data for %d cpus", cpus);
357 
358 	for (cpu = 0; cpu < cpus; cpu++) {
359 		int ret;
360 
361 		ret = asprintf(&file, "%s/.tmp.%s.%d", dir, base, cpu);
362 		if (ret < 0)
363 			die("Failed to allocate file for %s %s %d", dir, base, cpu);
364 		fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
365 		cpu_data[cpu].cpu = cpu;
366 		cpu_data[cpu].fd = fd;
367 		cpu_data[cpu].file = file;
368 		cpu_data[cpu].offset = 0;
369 		if (start)
370 			tracecmd_set_cpu_to_timestamp(handle, cpu, start);
371 	}
372 
373 	if (only_cpu >= 0) {
374 		parse_cpu(handle, cpu_data, start, end, count,
375 			  1, only_cpu, type);
376 	} else if (percpu) {
377 		for (cpu = 0; cpu < cpus; cpu++)
378 			parse_cpu(handle, cpu_data, start,
379 				  end, count, percpu, cpu, type);
380 	} else
381 		parse_cpu(handle, cpu_data, start,
382 			  end, count, percpu, -1, type);
383 
384 	cpu_list = malloc(sizeof(*cpu_list) * cpus);
385 	if (!cpu_list)
386 		die("Failed to allocate cpu_list for %d cpus", cpus);
387 	for (cpu = 0; cpu < cpus; cpu ++)
388 		cpu_list[cpu] = cpu_data[cpu].file;
389 
390 	tracecmd_set_out_clock(ohandle, tracecmd_get_trace_clock(handle));
391 	if (tracecmd_append_cpu_data(ohandle, cpus, cpu_list) < 0)
392 		die("Failed to append tracing data\n");
393 
394 	current = end;
395 	for (cpu = 0; cpu < cpus; cpu++) {
396 		/* Set the tracecmd cursor to the next set of records */
397 		if (cpu_data[cpu].offset) {
398 			record = tracecmd_read_at(handle, cpu_data[cpu].offset, NULL);
399 			if (record && (!current || record->ts > current))
400 				current = record->ts + 1;
401 			tracecmd_free_record(record);
402 		}
403 		unlink(cpu_data[cpu].file);
404 		free(cpu_data[cpu].file);
405 	}
406 	free(cpu_data);
407 	free(cpu_list);
408 	free(output);
409 	tracecmd_output_close(ohandle);
410 
411 	return current;
412 }
413 
trace_split(int argc,char ** argv)414 void trace_split (int argc, char **argv)
415 {
416 	struct tracecmd_input *handle;
417 	unsigned long long start_ns = 0, end_ns = 0;
418 	unsigned long long current;
419 	double start, end;
420 	char *endptr;
421 	char *output = NULL;
422 	char *output_file;
423 	enum split_types split_type = SPLIT_NONE;
424 	enum split_types type = SPLIT_NONE;
425 	int count;
426 	int repeat = 0;
427 	int percpu = 0;
428 	int cpu = -1;
429 	int ac;
430 	int c;
431 
432 	if (strcmp(argv[1], "split") != 0)
433 		usage(argv);
434 
435 	while ((c = getopt(argc-1, argv+1, "+ho:i:s:m:u:e:p:rcC:")) >= 0) {
436 		switch (c) {
437 		case 'h':
438 			usage(argv);
439 			break;
440 		case 'p':
441 			type++;
442 		case 'e':
443 			type++;
444 		case 'u':
445 			type++;
446 		case 'm':
447 			type++;
448 		case 's':
449 			type++;
450 			if (split_type != SPLIT_NONE)
451 				die("Only one type of split is allowed");
452 			count = atoi(optarg);
453 			if (count <= 0)
454 				die("Units must be greater than 0");
455 			split_type = type;
456 
457 			/* Spliting by pages only makes sense per cpu */
458 			if (type == SPLIT_PAGES)
459 				percpu = 1;
460 			break;
461 		case 'r':
462 			repeat = 1;
463 			break;
464 		case 'c':
465 			percpu = 1;
466 			break;
467 		case 'C':
468 			cpu = atoi(optarg);
469 			break;
470 		case 'o':
471 			if (output)
472 				die("only one output file allowed");
473 			output = strdup(optarg);
474 			break;
475 		case 'i':
476 			input_file = optarg;
477 			break;
478 		default:
479 			usage(argv);
480 		}
481 	}
482 
483 	ac = (argc - optind);
484 
485 	if (ac >= 2) {
486 		optind++;
487 		start = strtod(argv[optind], &endptr);
488 		if (ac > 3)
489 			usage(argv);
490 
491 		/* Make sure a true start value was entered */
492 		if (*endptr != 0)
493 			die("Start value not floating point: %s", argv[optind]);
494 
495 		start_ns = (unsigned long long)(start * 1000000000.0);
496 		optind++;
497 		if (ac == 3) {
498 			end = strtod(argv[optind], &endptr);
499 
500 			/* Make sure a true end value was entered */
501 			if (*endptr != 0)
502 				die("End value not floating point: %s",
503 				    argv[optind]);
504 
505 			end_ns = (unsigned long long)(end * 1000000000.0);
506 			if (end_ns < start_ns)
507 				die("Error: end is less than start");
508 		}
509 	}
510 
511 	if (!input_file)
512 		input_file = default_input_file;
513 
514 	handle = tracecmd_open(input_file, 0);
515 	if (!handle)
516 		die("error reading %s", input_file);
517 
518 	if (tracecmd_get_file_state(handle) == TRACECMD_FILE_CPU_LATENCY)
519 		die("trace-cmd split does not work with latency traces\n");
520 
521 	page_size = tracecmd_page_size(handle);
522 
523 	if (!output)
524 		output = strdup(input_file);
525 
526 	if (!repeat) {
527 		output = realloc(output, strlen(output) + 3);
528 		strcat(output, ".1");
529 	}
530 
531 	current = start_ns;
532 	output_file = malloc(strlen(output) + 50);
533 	if (!output_file)
534 		die("Failed to allocate for %s", output);
535 	c = 1;
536 
537 	do {
538 		if (repeat)
539 			sprintf(output_file, "%s.%04d", output, c++);
540 		else
541 			strcpy(output_file, output);
542 
543 		current = parse_file(handle, output_file, start_ns, end_ns,
544 				     percpu, cpu, count, type);
545 		if (!repeat)
546 			break;
547 		start_ns = 0;
548 	} while (current && (!end_ns || current < end_ns));
549 
550 	free(output);
551 	free(output_file);
552 
553 	tracecmd_close(handle);
554 
555 	return;
556 }
557