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