|
@@ -63,9 +63,20 @@ char *perf_header__find_event(u64 id)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-static const char *__perf_magic = "PERFFILE";
|
|
|
+/*
|
|
|
+ * magic2 = "PERFILE2"
|
|
|
+ * must be a numerical value to let the endianness
|
|
|
+ * determine the memory layout. That way we are able
|
|
|
+ * to detect endianness when reading the perf.data file
|
|
|
+ * back.
|
|
|
+ *
|
|
|
+ * we check for legacy (PERFFILE) format.
|
|
|
+ */
|
|
|
+static const char *__perf_magic1 = "PERFFILE";
|
|
|
+static const u64 __perf_magic2 = 0x32454c4946524550ULL;
|
|
|
+static const u64 __perf_magic2_sw = 0x50455246494c4532ULL;
|
|
|
|
|
|
-#define PERF_MAGIC (*(u64 *)__perf_magic)
|
|
|
+#define PERF_MAGIC __perf_magic2
|
|
|
|
|
|
struct perf_file_attr {
|
|
|
struct perf_event_attr attr;
|
|
@@ -1305,25 +1316,198 @@ static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
|
|
|
free(str);
|
|
|
}
|
|
|
|
|
|
+static int __event_process_build_id(struct build_id_event *bev,
|
|
|
+ char *filename,
|
|
|
+ struct perf_session *session)
|
|
|
+{
|
|
|
+ int err = -1;
|
|
|
+ struct list_head *head;
|
|
|
+ struct machine *machine;
|
|
|
+ u16 misc;
|
|
|
+ struct dso *dso;
|
|
|
+ enum dso_kernel_type dso_type;
|
|
|
+
|
|
|
+ machine = perf_session__findnew_machine(session, bev->pid);
|
|
|
+ if (!machine)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
|
|
+
|
|
|
+ switch (misc) {
|
|
|
+ case PERF_RECORD_MISC_KERNEL:
|
|
|
+ dso_type = DSO_TYPE_KERNEL;
|
|
|
+ head = &machine->kernel_dsos;
|
|
|
+ break;
|
|
|
+ case PERF_RECORD_MISC_GUEST_KERNEL:
|
|
|
+ dso_type = DSO_TYPE_GUEST_KERNEL;
|
|
|
+ head = &machine->kernel_dsos;
|
|
|
+ break;
|
|
|
+ case PERF_RECORD_MISC_USER:
|
|
|
+ case PERF_RECORD_MISC_GUEST_USER:
|
|
|
+ dso_type = DSO_TYPE_USER;
|
|
|
+ head = &machine->user_dsos;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ dso = __dsos__findnew(head, filename);
|
|
|
+ if (dso != NULL) {
|
|
|
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
|
|
+
|
|
|
+ dso__set_build_id(dso, &bev->build_id);
|
|
|
+
|
|
|
+ if (filename[0] == '[')
|
|
|
+ dso->kernel = dso_type;
|
|
|
+
|
|
|
+ build_id__sprintf(dso->build_id, sizeof(dso->build_id),
|
|
|
+ sbuild_id);
|
|
|
+ pr_debug("build id event received for %s: %s\n",
|
|
|
+ dso->long_name, sbuild_id);
|
|
|
+ }
|
|
|
+
|
|
|
+ err = 0;
|
|
|
+out:
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int perf_header__read_build_ids_abi_quirk(struct perf_header *header,
|
|
|
+ int input, u64 offset, u64 size)
|
|
|
+{
|
|
|
+ struct perf_session *session = container_of(header, struct perf_session, header);
|
|
|
+ struct {
|
|
|
+ struct perf_event_header header;
|
|
|
+ u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
|
|
|
+ char filename[0];
|
|
|
+ } old_bev;
|
|
|
+ struct build_id_event bev;
|
|
|
+ char filename[PATH_MAX];
|
|
|
+ u64 limit = offset + size;
|
|
|
+
|
|
|
+ while (offset < limit) {
|
|
|
+ ssize_t len;
|
|
|
+
|
|
|
+ if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev))
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ if (header->needs_swap)
|
|
|
+ perf_event_header__bswap(&old_bev.header);
|
|
|
+
|
|
|
+ len = old_bev.header.size - sizeof(old_bev);
|
|
|
+ if (read(input, filename, len) != len)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ bev.header = old_bev.header;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * As the pid is the missing value, we need to fill
|
|
|
+ * it properly. The header.misc value give us nice hint.
|
|
|
+ */
|
|
|
+ bev.pid = HOST_KERNEL_ID;
|
|
|
+ if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER ||
|
|
|
+ bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL)
|
|
|
+ bev.pid = DEFAULT_GUEST_KERNEL_ID;
|
|
|
+
|
|
|
+ memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id));
|
|
|
+ __event_process_build_id(&bev, filename, session);
|
|
|
+
|
|
|
+ offset += bev.header.size;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int perf_header__read_build_ids(struct perf_header *header,
|
|
|
+ int input, u64 offset, u64 size)
|
|
|
+{
|
|
|
+ struct perf_session *session = container_of(header, struct perf_session, header);
|
|
|
+ struct build_id_event bev;
|
|
|
+ char filename[PATH_MAX];
|
|
|
+ u64 limit = offset + size, orig_offset = offset;
|
|
|
+ int err = -1;
|
|
|
+
|
|
|
+ while (offset < limit) {
|
|
|
+ ssize_t len;
|
|
|
+
|
|
|
+ if (read(input, &bev, sizeof(bev)) != sizeof(bev))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (header->needs_swap)
|
|
|
+ perf_event_header__bswap(&bev.header);
|
|
|
+
|
|
|
+ len = bev.header.size - sizeof(bev);
|
|
|
+ if (read(input, filename, len) != len)
|
|
|
+ goto out;
|
|
|
+ /*
|
|
|
+ * The a1645ce1 changeset:
|
|
|
+ *
|
|
|
+ * "perf: 'perf kvm' tool for monitoring guest performance from host"
|
|
|
+ *
|
|
|
+ * Added a field to struct build_id_event that broke the file
|
|
|
+ * format.
|
|
|
+ *
|
|
|
+ * Since the kernel build-id is the first entry, process the
|
|
|
+ * table using the old format if the well known
|
|
|
+ * '[kernel.kallsyms]' string for the kernel build-id has the
|
|
|
+ * first 4 characters chopped off (where the pid_t sits).
|
|
|
+ */
|
|
|
+ if (memcmp(filename, "nel.kallsyms]", 13) == 0) {
|
|
|
+ if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1)
|
|
|
+ return -1;
|
|
|
+ return perf_header__read_build_ids_abi_quirk(header, input, offset, size);
|
|
|
+ }
|
|
|
+
|
|
|
+ __event_process_build_id(&bev, filename, session);
|
|
|
+
|
|
|
+ offset += bev.header.size;
|
|
|
+ }
|
|
|
+ err = 0;
|
|
|
+out:
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int process_trace_info(struct perf_file_section *section __unused,
|
|
|
+ struct perf_header *ph __unused,
|
|
|
+ int feat __unused, int fd)
|
|
|
+{
|
|
|
+ trace_report(fd, false);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int process_build_id(struct perf_file_section *section,
|
|
|
+ struct perf_header *ph,
|
|
|
+ int feat __unused, int fd)
|
|
|
+{
|
|
|
+ if (perf_header__read_build_ids(ph, fd, section->offset, section->size))
|
|
|
+ pr_debug("Failed to read buildids, continuing...\n");
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
struct feature_ops {
|
|
|
int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
|
|
|
void (*print)(struct perf_header *h, int fd, FILE *fp);
|
|
|
+ int (*process)(struct perf_file_section *section,
|
|
|
+ struct perf_header *h, int feat, int fd);
|
|
|
const char *name;
|
|
|
bool full_only;
|
|
|
};
|
|
|
|
|
|
#define FEAT_OPA(n, func) \
|
|
|
[n] = { .name = #n, .write = write_##func, .print = print_##func }
|
|
|
+#define FEAT_OPP(n, func) \
|
|
|
+ [n] = { .name = #n, .write = write_##func, .print = print_##func, \
|
|
|
+ .process = process_##func }
|
|
|
#define FEAT_OPF(n, func) \
|
|
|
- [n] = { .name = #n, .write = write_##func, .print = print_##func, .full_only = true }
|
|
|
+ [n] = { .name = #n, .write = write_##func, .print = print_##func, \
|
|
|
+ .full_only = true }
|
|
|
|
|
|
/* feature_ops not implemented: */
|
|
|
#define print_trace_info NULL
|
|
|
#define print_build_id NULL
|
|
|
|
|
|
static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
|
|
|
- FEAT_OPA(HEADER_TRACE_INFO, trace_info),
|
|
|
- FEAT_OPA(HEADER_BUILD_ID, build_id),
|
|
|
+ FEAT_OPP(HEADER_TRACE_INFO, trace_info),
|
|
|
+ FEAT_OPP(HEADER_BUILD_ID, build_id),
|
|
|
FEAT_OPA(HEADER_HOSTNAME, hostname),
|
|
|
FEAT_OPA(HEADER_OSRELEASE, osrelease),
|
|
|
FEAT_OPA(HEADER_VERSION, version),
|
|
@@ -1620,24 +1804,59 @@ out_free:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static int check_magic_endian(u64 *magic, struct perf_file_header *header,
|
|
|
+ struct perf_header *ph)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* check for legacy format */
|
|
|
+ ret = memcmp(magic, __perf_magic1, sizeof(*magic));
|
|
|
+ if (ret == 0) {
|
|
|
+ pr_debug("legacy perf.data format\n");
|
|
|
+ if (!header)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ if (header->attr_size != sizeof(struct perf_file_attr)) {
|
|
|
+ u64 attr_size = bswap_64(header->attr_size);
|
|
|
+
|
|
|
+ if (attr_size != sizeof(struct perf_file_attr))
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ ph->needs_swap = true;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* check magic number with same endianness */
|
|
|
+ if (*magic == __perf_magic2)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* check magic number but opposite endianness */
|
|
|
+ if (*magic != __perf_magic2_sw)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ ph->needs_swap = true;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
int perf_file_header__read(struct perf_file_header *header,
|
|
|
struct perf_header *ph, int fd)
|
|
|
{
|
|
|
+ int ret;
|
|
|
+
|
|
|
lseek(fd, 0, SEEK_SET);
|
|
|
|
|
|
- if (readn(fd, header, sizeof(*header)) <= 0 ||
|
|
|
- memcmp(&header->magic, __perf_magic, sizeof(header->magic)))
|
|
|
+ ret = readn(fd, header, sizeof(*header));
|
|
|
+ if (ret <= 0)
|
|
|
return -1;
|
|
|
|
|
|
- if (header->attr_size != sizeof(struct perf_file_attr)) {
|
|
|
- u64 attr_size = bswap_64(header->attr_size);
|
|
|
-
|
|
|
- if (attr_size != sizeof(struct perf_file_attr))
|
|
|
- return -1;
|
|
|
+ if (check_magic_endian(&header->magic, header, ph) < 0)
|
|
|
+ return -1;
|
|
|
|
|
|
+ if (ph->needs_swap) {
|
|
|
mem_bswap_64(header, offsetof(struct perf_file_header,
|
|
|
- adds_features));
|
|
|
- ph->needs_swap = true;
|
|
|
+ adds_features));
|
|
|
}
|
|
|
|
|
|
if (header->size != sizeof(*header)) {
|
|
@@ -1689,156 +1908,6 @@ int perf_file_header__read(struct perf_file_header *header,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int __event_process_build_id(struct build_id_event *bev,
|
|
|
- char *filename,
|
|
|
- struct perf_session *session)
|
|
|
-{
|
|
|
- int err = -1;
|
|
|
- struct list_head *head;
|
|
|
- struct machine *machine;
|
|
|
- u16 misc;
|
|
|
- struct dso *dso;
|
|
|
- enum dso_kernel_type dso_type;
|
|
|
-
|
|
|
- machine = perf_session__findnew_machine(session, bev->pid);
|
|
|
- if (!machine)
|
|
|
- goto out;
|
|
|
-
|
|
|
- misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
|
|
-
|
|
|
- switch (misc) {
|
|
|
- case PERF_RECORD_MISC_KERNEL:
|
|
|
- dso_type = DSO_TYPE_KERNEL;
|
|
|
- head = &machine->kernel_dsos;
|
|
|
- break;
|
|
|
- case PERF_RECORD_MISC_GUEST_KERNEL:
|
|
|
- dso_type = DSO_TYPE_GUEST_KERNEL;
|
|
|
- head = &machine->kernel_dsos;
|
|
|
- break;
|
|
|
- case PERF_RECORD_MISC_USER:
|
|
|
- case PERF_RECORD_MISC_GUEST_USER:
|
|
|
- dso_type = DSO_TYPE_USER;
|
|
|
- head = &machine->user_dsos;
|
|
|
- break;
|
|
|
- default:
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- dso = __dsos__findnew(head, filename);
|
|
|
- if (dso != NULL) {
|
|
|
- char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
|
|
-
|
|
|
- dso__set_build_id(dso, &bev->build_id);
|
|
|
-
|
|
|
- if (filename[0] == '[')
|
|
|
- dso->kernel = dso_type;
|
|
|
-
|
|
|
- build_id__sprintf(dso->build_id, sizeof(dso->build_id),
|
|
|
- sbuild_id);
|
|
|
- pr_debug("build id event received for %s: %s\n",
|
|
|
- dso->long_name, sbuild_id);
|
|
|
- }
|
|
|
-
|
|
|
- err = 0;
|
|
|
-out:
|
|
|
- return err;
|
|
|
-}
|
|
|
-
|
|
|
-static int perf_header__read_build_ids_abi_quirk(struct perf_header *header,
|
|
|
- int input, u64 offset, u64 size)
|
|
|
-{
|
|
|
- struct perf_session *session = container_of(header, struct perf_session, header);
|
|
|
- struct {
|
|
|
- struct perf_event_header header;
|
|
|
- u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
|
|
|
- char filename[0];
|
|
|
- } old_bev;
|
|
|
- struct build_id_event bev;
|
|
|
- char filename[PATH_MAX];
|
|
|
- u64 limit = offset + size;
|
|
|
-
|
|
|
- while (offset < limit) {
|
|
|
- ssize_t len;
|
|
|
-
|
|
|
- if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev))
|
|
|
- return -1;
|
|
|
-
|
|
|
- if (header->needs_swap)
|
|
|
- perf_event_header__bswap(&old_bev.header);
|
|
|
-
|
|
|
- len = old_bev.header.size - sizeof(old_bev);
|
|
|
- if (read(input, filename, len) != len)
|
|
|
- return -1;
|
|
|
-
|
|
|
- bev.header = old_bev.header;
|
|
|
-
|
|
|
- /*
|
|
|
- * As the pid is the missing value, we need to fill
|
|
|
- * it properly. The header.misc value give us nice hint.
|
|
|
- */
|
|
|
- bev.pid = HOST_KERNEL_ID;
|
|
|
- if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER ||
|
|
|
- bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL)
|
|
|
- bev.pid = DEFAULT_GUEST_KERNEL_ID;
|
|
|
-
|
|
|
- memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id));
|
|
|
- __event_process_build_id(&bev, filename, session);
|
|
|
-
|
|
|
- offset += bev.header.size;
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int perf_header__read_build_ids(struct perf_header *header,
|
|
|
- int input, u64 offset, u64 size)
|
|
|
-{
|
|
|
- struct perf_session *session = container_of(header, struct perf_session, header);
|
|
|
- struct build_id_event bev;
|
|
|
- char filename[PATH_MAX];
|
|
|
- u64 limit = offset + size, orig_offset = offset;
|
|
|
- int err = -1;
|
|
|
-
|
|
|
- while (offset < limit) {
|
|
|
- ssize_t len;
|
|
|
-
|
|
|
- if (read(input, &bev, sizeof(bev)) != sizeof(bev))
|
|
|
- goto out;
|
|
|
-
|
|
|
- if (header->needs_swap)
|
|
|
- perf_event_header__bswap(&bev.header);
|
|
|
-
|
|
|
- len = bev.header.size - sizeof(bev);
|
|
|
- if (read(input, filename, len) != len)
|
|
|
- goto out;
|
|
|
- /*
|
|
|
- * The a1645ce1 changeset:
|
|
|
- *
|
|
|
- * "perf: 'perf kvm' tool for monitoring guest performance from host"
|
|
|
- *
|
|
|
- * Added a field to struct build_id_event that broke the file
|
|
|
- * format.
|
|
|
- *
|
|
|
- * Since the kernel build-id is the first entry, process the
|
|
|
- * table using the old format if the well known
|
|
|
- * '[kernel.kallsyms]' string for the kernel build-id has the
|
|
|
- * first 4 characters chopped off (where the pid_t sits).
|
|
|
- */
|
|
|
- if (memcmp(filename, "nel.kallsyms]", 13) == 0) {
|
|
|
- if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1)
|
|
|
- return -1;
|
|
|
- return perf_header__read_build_ids_abi_quirk(header, input, offset, size);
|
|
|
- }
|
|
|
-
|
|
|
- __event_process_build_id(&bev, filename, session);
|
|
|
-
|
|
|
- offset += bev.header.size;
|
|
|
- }
|
|
|
- err = 0;
|
|
|
-out:
|
|
|
- return err;
|
|
|
-}
|
|
|
-
|
|
|
static int perf_file_section__process(struct perf_file_section *section,
|
|
|
struct perf_header *ph,
|
|
|
int feat, int fd, void *data __used)
|
|
@@ -1854,27 +1923,23 @@ static int perf_file_section__process(struct perf_file_section *section,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- switch (feat) {
|
|
|
- case HEADER_TRACE_INFO:
|
|
|
- trace_report(fd, false);
|
|
|
- break;
|
|
|
- case HEADER_BUILD_ID:
|
|
|
- if (perf_header__read_build_ids(ph, fd, section->offset, section->size))
|
|
|
- pr_debug("Failed to read buildids, continuing...\n");
|
|
|
- break;
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
+ if (!feat_ops[feat].process)
|
|
|
+ return 0;
|
|
|
|
|
|
- return 0;
|
|
|
+ return feat_ops[feat].process(section, ph, feat, fd);
|
|
|
}
|
|
|
|
|
|
static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,
|
|
|
struct perf_header *ph, int fd,
|
|
|
bool repipe)
|
|
|
{
|
|
|
- if (readn(fd, header, sizeof(*header)) <= 0 ||
|
|
|
- memcmp(&header->magic, __perf_magic, sizeof(header->magic)))
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = readn(fd, header, sizeof(*header));
|
|
|
+ if (ret <= 0)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ if (check_magic_endian(&header->magic, NULL, ph) < 0)
|
|
|
return -1;
|
|
|
|
|
|
if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0)
|