/* \param xdf pointer of a valid xdf file * * Allocate all the buffers and temporary objects needed for the transfer */ static int alloc_transfer_objects(struct xdf* xdf) { unsigned int samsize; xdf->sample_size = samsize = compute_sample_size(xdf, 1); xdf->filerec_size = compute_sample_size(xdf, 0) * xdf->ns_per_rec; if ( !(xdf->convdata = malloc(xdf->numch*sizeof(*(xdf->convdata)))) || !(xdf->batch = malloc(xdf->nbatch*sizeof(*(xdf->batch)))) || !(xdf->buff = malloc(xdf->ns_per_rec * samsize)) || !(xdf->backbuff = malloc(xdf->ns_per_rec * samsize)) || !(xdf->tmpbuff[0] = malloc(xdf->ns_per_rec * 8)) || !(xdf->tmpbuff[1] = malloc(xdf->ns_per_rec * 8)) ) { return -1; } return 0; }
/* * Arguments: * -i input file. Default: perf.data. */ int main(int argc, char **argv) { int ret, c; char *input_fname = "perf.data"; char *output_fname = "perf.data.manicured"; char *optval = NULL; perf_file_header f_header, f_header_manicured; while ((c = getopt (argc, argv, "b:e:i:o:s:")) != -1) { switch(c) { case 'b': begin_time = parse_timestamp_and_exit_on_error(optarg, "begin"); break; case 'e': end_time = parse_timestamp_and_exit_on_error(optarg, "end"); break; case 'i': input_fname = optarg; break; case 'o': output_fname = optarg; break; case 's': user_base_time = parse_timestamp_and_exit_on_error(optarg, "start"); break; case '?': default: usage(argv[0]); exit(-1); } } printf("Begin timestamp: %" PRIu64 " \n" "End timestamp: %" PRIu64 " \n" "Start (of program) timestamp: %" PRIu64 " \n", begin_time, end_time, user_base_time); if(user_base_time == 0 && (begin_time != 0)) printf("Warning: zero starting timestamp provided. Your begin and end timestamps will not be correctly " "calibrated to perf timestamps.\n"); if(begin_time < user_base_time || end_time < user_base_time) { printf("Your begin or end timestamps are smaller than the starting timestamp. Cannot proceed.\n"); usage(argv[0]); exit(-1); } /* Let's reset begin and end timestamps to be relative to the start-of-program timestamp, * so we don't have to perform this computation on every event. */ begin_time -= user_base_time; end_time -= user_base_time; int ifd = open(input_fname, O_RDONLY); if(ifd == -1) { fprintf(stderr, "Could not open %s: %s\n", input_fname, strerror(errno)); usage(argv[0]); exit(-1); } int ofd = open(output_fname, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if(ofd == -1) { fprintf(stderr, "Could not open %s: %s\n", output_fname, strerror(errno)); usage(argv[0]); exit(-1); } ret = check_and_copy_header(ifd, ofd, &f_header); if(ret) exit(-1); printf("Successful header check...\n"); /* * Sanity check that perf.data was written cleanly; data size is * initialized to 0 and updated only if the on_exit function is run. * If data size is still 0 then the file contains only partial * information. Just warn user and process it as much as it can. */ if (f_header.data.size == 0) { fprintf(stderr, "WARNING: The %s file's data size field is 0 which is unexpected.\n" "Was the 'perf record' command properly terminated?\n", input_fname); } /* Copy the event attribute section */ { /* SANITY CHECK. * If these two quantities are not equal, we must * be processing the perf file format that this tool * does not support. */ if(f_header.attr_size != sizeof(struct perf_file_attr)) { fprintf(stderr, "header attr_size (%" PRIu64 ") not equal to " "the size of struct perf_file_attr (%" PRIu64 "). " "Your perf.data file is the format that this tool " "does not understand. Sorry!\n", f_header.attr_size, sizeof(struct perf_file_attr)); exit(-1); } /* Now read and copy file sections corresponding to individual attributes. * We read and copy attributes one by one, because each attribute contains * a pointer to another file location containing more data. And we need to * copy that data as well. */ int i, nr_attrs = f_header.attrs.size / f_header.attr_size; for(i = 0; i < nr_attrs; i++) { perf_file_attr f_attr; struct perf_event_attr *attr = &(f_attr.attr); lseek(ifd, f_header.attrs.offset + i*f_header.attr_size, SEEK_SET); lseek(ofd, f_header.attrs.offset + i*f_header.attr_size, SEEK_SET); read_and_exit_on_error(ifd, &f_attr, f_header.attr_size, __FILE__, __LINE__); write_and_exit_on_error(ofd, &f_attr, f_header.attr_size, __FILE__, __LINE__); printf("Set to offset %ld and read %ld bytes of perf_file_attr (%ld size)\n", f_header.attrs.offset + i*f_header.attr_size, f_header.attr_size, sizeof(f_attr)); if(f_attr.ids.size > 0) { printf("There's %" PRId64 " bytes of data at offset %" PRId64 "\n", f_attr.ids.size, f_attr.ids.offset); void *buffer = malloc_and_exit_on_error(f_attr.ids.size, __FILE__, __LINE__); lseek(ifd, f_attr.ids.offset, SEEK_SET); lseek(ofd, f_attr.ids.offset, SEEK_SET); read_and_exit_on_error(ifd, buffer, f_attr.ids.size, __FILE__, __LINE__); write_and_exit_on_error(ofd, buffer, f_attr.ids.size, __FILE__, __LINE__); free(buffer); } if(!(attr->sample_type & PERF_SAMPLE_TIME) && !attr->sample_id_all) { fprintf(stderr, "Event %s does not sample time. " "We do not know how to process such events.\n", attr->type < PERF_TYPE_MAX ? event_attr_names[attr->type]: "UNKNOWN"); exit(-1); } /* Keep this event attribute in the list. We will later use them to parse samples. * Technically, we need only the first one (see comment in event_do_we_care()), but * let's keep them all just in case. */ event_descr *attr_to_keep = malloc_and_exit_on_error(sizeof(event_descr), __FILE__, __LINE__); attr_to_keep->attr = *attr; attr_to_keep->sample_size = compute_sample_size(attr->sample_type); list_insert_and_exit_on_error(&event_attr_list, (void*)attr_to_keep, __FILE__, __LINE__); printf("Found event %s, sample type is %" PRIu64 ", sample size is %" PRIu64 "\n", attr->type < PERF_TYPE_MAX ? event_attr_names[attr->type]: "UNKNOWN", attr->sample_type, attr_to_keep->sample_size); char *what_we_are_sampling = what_are_we_sampling(attr->sample_type); printf("%s\n", what_we_are_sampling); free(what_we_are_sampling); if(!attr->sample_id_all) { fprintf(stderr, "This perf file does not have sample IDs for all data " "(sample_id_all not set on an event attribute)." "We rely on sample id timestamp in the COMM event to calibrate " "timestamps, so this program won't work without sample id data. " "Try using a more recent version of perf. Sorry!\n"); exit(-1); } } } /* Copy the event section */ { void *buffer = malloc_and_exit_on_error(f_header.event_types.size, __FILE__, __LINE__); lseek(ifd, f_header.event_types.offset, SEEK_SET); lseek(ofd, f_header.event_types.offset, SEEK_SET); read_and_exit_on_error(ifd, buffer, f_header.event_types.size, __FILE__, __LINE__); write_and_exit_on_error(ofd, buffer, f_header.event_types.size, __FILE__, __LINE__); printf("read event_types: %ld bytes at offset %ld\n", f_header.event_types.size, f_header.event_types.offset); free(buffer); } /* Cull the data section. * This section is structured as a collection of perf_event records. Each * record begins with a header, which has a size field. We don't know in advance how large a * record is or how it is structured. * So we are going to read the header first to find out the size and type, and then we read * the rest. */ { size_t bytes_processed = 0, bytes_written_to_manicured_file = 0; /* We position the input and output file at the start of the data section, * but from now on, the files may not be moving synchronously if we are * copying only selected records to the output file. */ lseek(ifd, f_header.data.offset, SEEK_SET); lseek(ofd, f_header.data.offset, SEEK_SET); /* We now copy records one by one and decide if we care about them. */ while(bytes_processed < f_header.data.size) { union perf_event *event; perf_event_header *event_header = (perf_event_header*) &event; size_t this_event_size; read_and_exit_on_error(ifd, &event, sizeof(perf_event_header), __FILE__, __LINE__); /* Ok, now we know the size of this event record */ this_event_size = event_header->size; event = malloc(this_event_size); /* Read the entire event from the file. * We don't have to re-read the header, because we already have it, * but let's do it anyway, this makes the code simpler. */ lseek(ifd, -sizeof(perf_event_header), SEEK_CUR); read_and_exit_on_error(ifd, event, this_event_size, __FILE__, __LINE__); /* Ok, now determine if we care about this event. * Its timestamp must fall between the begin and end timestamps * supplied as arguments. */ if(event_do_we_care(event, begin_time, end_time)) { write_and_exit_on_error(ofd, event, this_event_size, __FILE__, __LINE__); bytes_written_to_manicured_file += this_event_size; } bytes_processed += this_event_size; printf("IF offset: %ld, OF offset: %ld\n,", lseek(ifd, 0, SEEK_CUR), lseek(ofd, 0, SEEK_CUR)); free(event); } printf("data section: %ld bytes at offset %ld. Processed %ld bytes \n", f_header.data.size, f_header.data.offset, bytes_processed); /* Now let's re-write the file header section of the output file * to update the data section size. */ f_header_manicured = f_header; f_header_manicured.data.size = bytes_written_to_manicured_file; lseek(ofd, 0, SEEK_SET); write_and_exit_on_error(ofd, &f_header_manicured, sizeof(perf_file_header), __FILE__, __LINE__); } /* Copy the additional features section. * This section begins at the end of the data section and has a number of * records of type perf_file_section. How many records there are is determined * by the number of set bits in the adds_features bitmap in the file header. * * If we have not copied the entire data section from the input file to the * output file, the output file will have a hole in it. That's ok. * * These additional features (defined in tools/perf/util/header.h) include * things like hostname, OS release, NUMA topology, etc. So we copy them * unchanged into the manicured file. */ { size_t feat_offset = f_header.data.offset + f_header.data.size; int i, nr_records; nr_records = bitmap_weight(f_header.adds_features, HEADER_FEAT_BITS); /* Each record in this section is a perf_file_section, so it * has a pointer to another file location with data. So we * process these records one by one and copy the record itself and * the data to which it points. */ for(i = 0; i < nr_records; i++) { perf_file_section rec; /* For the output file we use the same offset as the * one in the original input file, even though the * manicured output file is most likely shorter than * the original (because we skipped data records). * That is probably okay. Only means that our output file * will have a "hole" in it. */ lseek(ifd, feat_offset + i * sizeof(rec), SEEK_SET); lseek(ofd, feat_offset + i * sizeof(rec), SEEK_SET); read_and_exit_on_error(ifd, &rec, sizeof(rec), __FILE__, __LINE__); write_and_exit_on_error(ofd, &rec, sizeof(rec), __FILE__, __LINE__); printf("Adds feats: read %ld bytes at offset %ld\n", sizeof(rec), feat_offset + i * sizeof(rec)); printf("There's %ld more bytes at offset %ld\n", rec.size, rec.offset); lseek(ifd, rec.offset, SEEK_SET); lseek(ofd, rec.offset, SEEK_SET); void *buffer = malloc_and_exit_on_error(rec.size, __FILE__, __LINE__); read_and_exit_on_error(ifd, buffer, rec.size, __FILE__, __LINE__); write_and_exit_on_error(ofd, buffer, rec.size, __FILE__, __LINE__); free(buffer); } } }