aboutsummaryrefslogtreecommitdiffstats
path: root/trace-split.c
diff options
context:
space:
mode:
Diffstat (limited to 'trace-split.c')
-rw-r--r--trace-split.c514
1 files changed, 514 insertions, 0 deletions
diff --git a/trace-split.c b/trace-split.c
new file mode 100644
index 0000000..96cda2d
--- /dev/null
+++ b/trace-split.c
@@ -0,0 +1,514 @@
1/*
2 * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
3 *
4 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License (not later!)
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20 */
21#define _LARGEFILE64_SOURCE
22#define _GNU_SOURCE
23#include <dirent.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <getopt.h>
28#include <stdarg.h>
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <sys/wait.h>
32#include <sys/mman.h>
33#include <pthread.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <ctype.h>
37#include <errno.h>
38
39#include "trace-local.h"
40
41static unsigned int page_size;
42static const char *default_input_file = "trace.dat";
43static const char *input_file;
44
45enum split_types {
46 SPLIT_NONE,
47 /* The order of these must be reverse of the case statement in the options */
48 SPLIT_SECONDS,
49 SPLIT_MSECS,
50 SPLIT_USECS,
51 SPLIT_EVENTS,
52 SPLIT_PAGES,
53 SPLIT_NR_TYPES,
54};
55
56struct cpu_data {
57 unsigned long long ts;
58 unsigned long long offset;
59 struct record *record;
60 int cpu;
61 int fd;
62 int index;
63 void *commit;
64 void *page;
65 char *file;
66};
67
68static int create_type_len(struct pevent *pevent, int time, int len)
69{
70 static int bigendian = -1;
71 char *ptr;
72 int test;
73
74 if (bigendian < 0) {
75 test = 0x4321;
76 ptr = (char *)&test;
77 if (*ptr == 0x12)
78 bigendian = 0;
79 else
80 bigendian = 1;
81 }
82
83 if (pevent->file_bigendian)
84 time |= (len << 27);
85 else
86 time = (time << 5) | len;
87
88 return __data2host4(pevent, time);
89}
90
91static int write_record(struct tracecmd_input *handle,
92 struct record *record,
93 struct cpu_data *cpu_data,
94 enum split_types type)
95{
96 unsigned long long diff;
97 struct pevent *pevent;
98 void *page;
99 int len;
100 char *ptr;
101 int index = 0;
102 int time;
103
104 page = cpu_data->page;
105
106 pevent = tracecmd_get_pevent(handle);
107
108 ptr = page + cpu_data->index;
109
110 diff = record->ts - cpu_data->ts;
111 if (diff > (1 << 27)) {
112 /* Add a time stamp */
113 len = RINGBUF_TYPE_TIME_EXTEND;
114 time = (unsigned int)(diff & ((1ULL << 27) - 1));
115 time = create_type_len(pevent, time, len);
116 *(unsigned *)ptr = time;
117 ptr += 4;
118 time = (unsigned int)(diff >> 27);
119 *(unsigned *)ptr = __data2host4(pevent, time);
120 cpu_data->ts = record->ts;
121 cpu_data->index += 8;
122 return 0;
123 }
124
125 if (record->size) {
126 if (record->size < 28 * 4)
127 len = record->size / 4;
128 else
129 len = 0;
130 }
131
132 time = (unsigned)diff;
133 time = create_type_len(pevent, time, len);
134
135 memcpy(ptr, &time, 4);
136 ptr += 4;
137 index = 4;
138
139 if (!len) {
140 len = record->size / 4;
141 len += 4;
142 memcpy(ptr, &len, 4);
143 ptr += 4;
144 index += 4;
145 }
146
147 len = (record->size + 3) & ~3;
148 index += len;
149
150 memcpy(ptr, record->data, len);
151
152 cpu_data->index += index;
153 cpu_data->ts = record->ts;
154
155 return 1;
156}
157
158static void write_page(struct cpu_data *cpu_data, int long_size)
159{
160 if (long_size == 8)
161 *(unsigned long long *)cpu_data->commit =
162 (unsigned long long)cpu_data->index;
163 else
164 *(unsigned int *)cpu_data->commit =
165 cpu_data->index;
166 write(cpu_data->fd, cpu_data->page, page_size);
167}
168
169static struct record *read_record(struct tracecmd_input *handle,
170 int percpu, int *cpu)
171{
172 if (percpu)
173 return tracecmd_read_data(handle, *cpu);
174
175 return tracecmd_read_next_data(handle, cpu);
176}
177
178static int parse_cpu(struct tracecmd_input *handle,
179 struct cpu_data *cpu_data,
180 unsigned long long start,
181 unsigned long long end,
182 int count_limit, int percpu, int cpu,
183 enum split_types type)
184{
185 struct record *record;
186 struct pevent *pevent;
187 void *ptr;
188 int page_size;
189 int long_size = 0;
190 int cpus;
191 int count = 0;
192 int pages = 0;
193
194 cpus = tracecmd_cpus(handle);
195
196 long_size = tracecmd_long_size(handle);
197 page_size = tracecmd_page_size(handle);
198 pevent = tracecmd_get_pevent(handle);
199
200 /* Force new creation of first page */
201 if (percpu) {
202 cpu_data[cpu].index = page_size + 1;
203 cpu_data[cpu].page = NULL;
204 } else {
205 for (cpu = 0; cpu < cpus; cpu++) {
206 cpu_data[cpu].index = page_size + 1;
207 cpu_data[cpu].page = NULL;
208 }
209 }
210
211 /*
212 * Get the cpu pointers up to the start of the
213 * start time stamp.
214 */
215
216 record = read_record(handle, percpu, &cpu);
217
218 if (start) {
219 while (record && record->ts < start) {
220 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 if (cpu_data[cpu].page)
229 write_page(&cpu_data[cpu], long_size);
230 else
231 cpu_data[cpu].page = malloc_or_die(page_size);
232
233 if (type == SPLIT_PAGES && pages++ > count_limit)
234 break;
235
236 memset(cpu_data[cpu].page, 0, page_size);
237 ptr = cpu_data[cpu].page;
238
239 *(unsigned long long*)ptr =
240 __data2host8(pevent, record->ts);
241 cpu_data[cpu].ts = record->ts;
242 ptr += 8;
243 cpu_data[cpu].commit = ptr;
244 ptr += long_size;
245 cpu_data[cpu].index = 8 + long_size;
246 }
247
248 cpu_data[cpu].offset = record->offset;
249
250 if (write_record(handle, record, &cpu_data[cpu], type)) {
251 free_record(record);
252 record = read_record(handle, percpu, &cpu);
253
254 /* if we hit the end of the cpu, clear the offset */
255 if (!record) {
256 if (percpu)
257 cpu_data[cpu].offset = 0;
258 else
259 for (cpu = 0; cpu < cpus; cpu++)
260 cpu_data[cpu].offset = 0;
261 }
262
263 switch (type) {
264 case SPLIT_NONE:
265 break;
266 case SPLIT_SECONDS:
267 if (record &&
268 record->ts >
269 (start + (unsigned long long)count_limit * 1000000000ULL)) {
270 free_record(record);
271 record = NULL;
272 }
273 break;
274 case SPLIT_MSECS:
275 if (record &&
276 record->ts >
277 (start + (unsigned long long)count_limit * 1000000ULL)) {
278 free_record(record);
279 record = NULL;
280 }
281 break;
282 case SPLIT_USECS:
283 if (record &&
284 record->ts >
285 (start + (unsigned long long)count_limit * 1000ULL)) {
286 free_record(record);
287 record = NULL;
288 }
289 break;
290 case SPLIT_EVENTS:
291 if (++count >= count_limit) {
292 free_record(record);
293 record = NULL;
294 }
295 break;
296 default:
297 break;
298 }
299 }
300 }
301
302 if (record)
303 free_record(record);
304
305 if (percpu) {
306 if (cpu_data[cpu].page) {
307 write_page(&cpu_data[cpu], long_size);
308 free(cpu_data[cpu].page);
309 cpu_data[cpu].page = NULL;
310 }
311 } else {
312 for (cpu = 0; cpu < cpus; cpu++) {
313 if (cpu_data[cpu].page) {
314 write_page(&cpu_data[cpu], long_size);
315 free(cpu_data[cpu].page);
316 cpu_data[cpu].page = NULL;
317 }
318 }
319 }
320
321 return 0;
322}
323
324static double parse_file(struct tracecmd_input *handle,
325 const char *output_file,
326 unsigned long long start,
327 unsigned long long end, int percpu,
328 int count, enum split_types type)
329{
330 unsigned long long current;
331 struct tracecmd_output *ohandle;
332 struct cpu_data *cpu_data;
333 struct record *record;
334 char **cpu_list;
335 char *file;
336 int cpus;
337 int cpu;
338 int fd;
339
340 ohandle = tracecmd_copy(handle, output_file);
341
342 cpus = tracecmd_cpus(handle);
343 cpu_data = malloc_or_die(sizeof(*cpu_data) * cpus);
344
345 for (cpu = 0; cpu < cpus; cpu++) {
346 file = malloc_or_die(strlen(output_file) + 50);
347 sprintf(file, ".tmp.%s.%d", output_file, cpu);
348 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
349 cpu_data[cpu].cpu = cpu;
350 cpu_data[cpu].fd = fd;
351 cpu_data[cpu].file = file;
352 cpu_data[cpu].offset = 0;
353 if (start)
354 tracecmd_set_cpu_to_timestamp(handle, cpu, start);
355 }
356
357 if (percpu) {
358 for (cpu = 0; cpu < cpus; cpu++)
359 parse_cpu(handle, cpu_data, start,
360 end, count, percpu, cpu, type);
361 } else
362 parse_cpu(handle, cpu_data, start,
363 end, count, percpu, -1, type);
364
365 cpu_list = malloc_or_die(sizeof(*cpu_list) * cpus);
366 for (cpu = 0; cpu < cpus; cpu ++)
367 cpu_list[cpu] = cpu_data[cpu].file;
368
369 tracecmd_append_cpu_data(ohandle, cpus, cpu_list);
370
371 current = end;
372 for (cpu = 0; cpu < cpus; cpu++) {
373 /* Set the tracecmd cursor to the next set of records */
374 if (cpu_data[cpu].offset) {
375 record = tracecmd_read_at(handle, cpu_data[cpu].offset, NULL);
376 if (record && (!current || record->ts > current))
377 current = record->ts + 1;
378 free_record(record);
379 }
380 unlink(cpu_data[cpu].file);
381 free(cpu_data[cpu].file);
382 }
383 free(cpu_data);
384 free(cpu_list);
385 tracecmd_output_close(ohandle);
386
387 return current;
388}
389
390void trace_split (int argc, char **argv)
391{
392 struct tracecmd_input *handle;
393 struct pevent *pevent;
394 unsigned long long start_ns = 0, end_ns = 0;
395 unsigned long long current;
396 double start, end;
397 char *output = NULL;
398 char *output_file;
399 enum split_types split_type = SPLIT_NONE;
400 enum split_types type = SPLIT_NONE;
401 int count;
402 int repeat = 0;
403 int percpu = 0;
404 int cpu = -1;
405 int ac;
406 int c;
407
408 if (strcmp(argv[1], "split") != 0)
409 usage(argv);
410
411 while ((c = getopt(argc-1, argv+1, "+ho:i:s:m:u:e:p:rcC:")) >= 0) {
412 switch (c) {
413 case 'h':
414 usage(argv);
415 break;
416 case 'p':
417 type++;
418 case 'e':
419 type++;
420 case 'u':
421 type++;
422 case 'm':
423 type++;
424 case 's':
425 type++;
426 if (split_type != SPLIT_NONE)
427 die("Only one type of split is allowed");
428 count = atoi(optarg);
429 split_type = type;
430 break;
431 case 'r':
432 repeat = 1;
433 break;
434 case 'c':
435 percpu = 1;
436 break;
437 case 'C':
438 cpu = atoi(optarg);
439 break;
440 case 'o':
441 if (output)
442 die("only one output file allowed");
443 output = strdup(optarg);
444 break;
445 case 'i':
446 input_file = optarg;
447 break;
448 default:
449 usage(argv);
450 }
451 }
452
453 ac = (argc - optind);
454
455 if (ac >= 2) {
456 optind++;
457 start = strtod(argv[optind], NULL);
458 if (ac > 3)
459 usage(argv);
460
461 start_ns = (unsigned long long)(start * 1000000000.0);
462 optind++;
463 if (ac == 3) {
464 end = strtod(argv[optind], NULL);
465 end_ns = (unsigned long long)(end * 1000000000.0);
466 if (end_ns < start_ns)
467 die("Error: end is less than start");
468 }
469 }
470
471 if (!input_file)
472 input_file = default_input_file;
473
474 handle = tracecmd_open(input_file);
475 if (!handle)
476 die("error reading %s", input_file);
477
478 page_size = tracecmd_page_size(handle);
479
480 pevent = tracecmd_get_pevent(handle);
481
482 if (!output) {
483 if (repeat)
484 output = strdup(input_file);
485 else {
486 output = malloc_or_die(strlen(input_file) + 3);
487 sprintf(output, "%s.1", input_file);
488 }
489 }
490
491 current = start_ns;
492 output_file = malloc_or_die(strlen(output) + 50);
493 c = 1;
494
495 do {
496 if (repeat)
497 sprintf(output_file, "%s.%04d", output, c++);
498 else
499 strcpy(output_file, output);
500
501 current = parse_file(handle, output_file, start_ns, end_ns,
502 percpu, count, type);
503 if (!repeat)
504 break;
505 start_ns = 0;
506 } while (current && (!end_ns || current < end_ns));
507
508 free(output);
509 free(output_file);
510
511 tracecmd_close(handle);
512
513 return;
514}