/* * 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.
*/ staticconstchar *__perf_magic1 = "PERFFILE"; staticconst u64 __perf_magic2 = 0x32454c4946524550ULL; staticconst u64 __perf_magic2_sw = 0x50455246494c4532ULL;
/* * write number of events
*/
ret = do_write(ff, &nre, sizeof(nre)); if (ret < 0) return ret;
/* * size of perf_event_attr struct
*/
sz = (u32)sizeof(evsel->core.attr);
ret = do_write(ff, &sz, sizeof(sz)); if (ret < 0) return ret;
evlist__for_each_entry(evlist, evsel) {
ret = do_write(ff, &evsel->core.attr, sz); if (ret < 0) return ret; /* * write number of unique id per event * there is one id per instance of an event * * copy into an nri to be independent of the * type of ids,
*/
nri = evsel->core.ids;
ret = do_write(ff, &nri, sizeof(nri)); if (ret < 0) return ret;
/* * write event string as passed on cmdline
*/
ret = do_write_string(ff, evsel__name(evsel)); if (ret < 0) return ret; /* * write unique ids for this event
*/
ret = do_write(ff, evsel->core.id, evsel->core.ids * sizeof(u64)); if (ret < 0) return ret;
} return 0;
}
ret = do_write(ff, &tp->package_cpus_lists, sizeof(tp->package_cpus_lists)); if (ret < 0) goto done;
for (i = 0; i < tp->package_cpus_lists; i++) {
ret = do_write_string(ff, tp->package_cpus_list[i]); if (ret < 0) goto done;
}
ret = do_write(ff, &tp->core_cpus_lists, sizeof(tp->core_cpus_lists)); if (ret < 0) goto done;
for (i = 0; i < tp->core_cpus_lists; i++) {
ret = do_write_string(ff, tp->core_cpus_list[i]); if (ret < 0) break;
}
ret = perf_env__read_cpu_topology_map(env); if (ret < 0) goto done;
for (j = 0; j < env->nr_cpus_avail; j++) {
ret = do_write(ff, &env->cpu[j].core_id, sizeof(env->cpu[j].core_id)); if (ret < 0) return ret;
ret = do_write(ff, &env->cpu[j].socket_id, sizeof(env->cpu[j].socket_id)); if (ret < 0) return ret;
}
if (!tp->die_cpus_lists) goto done;
ret = do_write(ff, &tp->die_cpus_lists, sizeof(tp->die_cpus_lists)); if (ret < 0) goto done;
for (i = 0; i < tp->die_cpus_lists; i++) {
ret = do_write_string(ff, tp->die_cpus_list[i]); if (ret < 0) goto done;
}
for (j = 0; j < env->nr_cpus_avail; j++) {
ret = do_write(ff, &env->cpu[j].die_id, sizeof(env->cpu[j].die_id)); if (ret < 0) return ret;
}
done:
cpu_topology__delete(tp); return ret;
}
staticint write_total_mem(struct feat_fd *ff, struct evlist *evlist __maybe_unused)
{ char *buf = NULL;
FILE *fp;
size_t len = 0; int ret = -1, n;
uint64_t mem;
fp = fopen("/proc/meminfo", "r"); if (!fp) return -1;
while (getline(&buf, &len, fp) > 0) {
ret = strncmp(buf, "MemTotal:", 9); if (!ret) break;
} if (!ret) {
n = sscanf(buf, "%*s %"PRIu64, &mem); if (n == 1)
ret = do_write(ff, &mem, sizeof(mem));
} else
ret = -1;
free(buf);
fclose(fp); return ret;
}
staticint write_numa_topology(struct feat_fd *ff, struct evlist *evlist __maybe_unused)
{ struct numa_topology *tp; int ret = -1;
u32 i;
tp = numa_topology__new(); if (!tp) return -ENOMEM;
ret = do_write(ff, &tp->nr, sizeof(u32)); if (ret < 0) goto err;
for (i = 0; i < tp->nr; i++) { struct numa_topology_node *n = &tp->nodes[i];
ret = do_write(ff, &n->node, sizeof(u32)); if (ret < 0) goto err;
ret = do_write(ff, &n->mem_total, sizeof(u64)); if (ret) goto err;
ret = do_write(ff, &n->mem_free, sizeof(u64)); if (ret) goto err;
ret = do_write_string(ff, n->cpus); if (ret < 0) goto err;
}
ret = do_write_string(ff, name); if (ret < 0) return ret;
ret = do_write(ff, &leader_idx, sizeof(leader_idx)); if (ret < 0) return ret;
ret = do_write(ff, &nr_members, sizeof(nr_members)); if (ret < 0) return ret;
}
} return 0;
}
/* * Return the CPU id as a raw string. * * Each architecture should provide a more precise id string that * can be use to match the architecture's "mapfile".
*/ char * __weak get_cpuid_str(struct perf_cpu cpu __maybe_unused)
{ return NULL;
}
/* Return zero when the cpuid from the mapfile.csv matches the * cpuid string generated on this platform. * Otherwise return non-zero.
*/ int __weak strcmp_cpuid_str(constchar *mapcpuid, constchar *cpuid)
{
regex_t re;
regmatch_t pmatch[1]; int match;
if (regcomp(&re, mapcpuid, REG_EXTENDED) != 0) { /* Warn unable to generate match particular string. */
pr_info("Invalid regular expression %s\n", mapcpuid); return 1;
}
match = !regexec(&re, cpuid, 1, pmatch, 0);
regfree(&re); if (match) {
size_t match_len = (pmatch[0].rm_eo - pmatch[0].rm_so);
/* Verify the entire string matched. */ if (match_len == strlen(cpuid)) return 0;
} return 1;
}
/* * default get_cpuid(): nothing gets recorded * actual implementation must be in arch/$(SRCARCH)/util/header.c
*/ int __weak get_cpuid(char *buffer __maybe_unused, size_t sz __maybe_unused, struct perf_cpu cpu __maybe_unused)
{ return ENOSYS; /* Not implemented */
}
staticint write_cpuid(struct feat_fd *ff, struct evlist *evlist)
{ struct perf_cpu cpu = perf_cpu_map__min(evlist->core.all_cpus); char buffer[64]; int ret;
ret = get_cpuid(buffer, sizeof(buffer), cpu); if (ret) return -1;
ret = do_write(ff, &env->bpf_progs.infos_cnt, sizeof(env->bpf_progs.infos_cnt)); if (ret < 0 || env->bpf_progs.infos_cnt == 0) goto out;
root = &env->bpf_progs.infos;
next = rb_first(root); while (next) { struct bpf_prog_info_node *node;
size_t len;
node = rb_entry(next, struct bpf_prog_info_node, rb_node);
next = rb_next(&node->rb_node);
len = sizeof(struct perf_bpil) +
node->info_linear->data_len;
/* before writing to file, translate address to offset */
bpil_addr_to_offs(node->info_linear);
ret = do_write(ff, node->info_linear, len); /* * translate back to address even when do_write() fails, * so that this function never changes the data.
*/
bpil_offs_to_addr(node->info_linear); if (ret < 0) goto out;
}
out:
up_read(&env->bpf_progs.lock); return ret;
}
/* * Build caches levels for a particular CPU from the data in * /sys/devices/system/cpu/cpu<cpu>/cache/ * The cache level data is stored in caches[] from index at * *cntp.
*/ int build_caches_for_cpu(u32 cpu, struct cpu_cache_level caches[], u32 *cntp)
{
u16 level;
for (level = 0; level < MAX_CACHE_LVL; level++) { struct cpu_cache_level c; int err;
u32 i;
if (!new_nodes) {
pr_err("Failed to write MEM_TOPOLOGY, size %zd nodes\n", size);
ret = -ENOMEM; goto out;
}
nodes = new_nodes;
size += 4;
}
ret = memory_node__read(&nodes[cnt], idx); if (!ret)
cnt += 1;
}
out:
close(dir.dirfd); if (!ret) {
*cntp = cnt;
*nodesp = nodes;
qsort(nodes, cnt, sizeof(nodes[0]), memory_node__sort);
} else
memory_node__delete_nodes(nodes, cnt);
return ret;
}
/* * The MEM_TOPOLOGY holds physical memory map for every * node in system. The format of data is as follows: * * 0 - version | for future changes * 8 - block_size_bytes | /sys/devices/system/memory/block_size_bytes * 16 - count | number of nodes * * For each node we store map of physical indexes for * each node: * * 32 - node id | node index * 40 - size | size of bitmap * 48 - bitmap | bitmap of memory indexes that belongs to node
*/ staticint write_mem_topology(struct feat_fd *ff __maybe_unused, struct evlist *evlist __maybe_unused)
{ struct memory_node *nodes = NULL;
u64 bsize, version = 1, i, nr = 0; int ret;
ret = sysfs__read_xll("devices/system/memory/block_size_bytes",
(unsignedlonglong *) &bsize); if (ret) return ret;
ret = build_mem_topology(&nodes, &nr); if (ret) return ret;
ret = do_write(ff, &version, sizeof(version)); if (ret < 0) goto out;
ret = do_write(ff, &bsize, sizeof(bsize)); if (ret < 0) goto out;
ret = do_write(ff, &nr, sizeof(nr)); if (ret < 0) goto out;
for (i = 0; i < nr; i++) { struct memory_node *n = &nodes[i];
#define _W(v) \
ret = do_write(ff, &n->v, sizeof(n->v)); \ if (ret < 0) \ goto out;
_W(node)
_W(size)
#undef _W
ret = do_write_bitmap(ff, n->set, n->size); if (ret < 0) goto out;
}
ret = perf_pmu__caps_parse(cpu_pmu); if (ret < 0) return ret;
return __write_pmu_caps(ff, cpu_pmu, false);
}
staticint write_pmu_caps(struct feat_fd *ff, struct evlist *evlist __maybe_unused)
{ struct perf_pmu *pmu = NULL; int nr_pmu = 0; int ret;
while ((pmu = perf_pmus__scan(pmu))) { if (!strcmp(pmu->name, "cpu")) { /* * The "cpu" PMU is special and covered by * HEADER_CPU_PMU_CAPS. Note, core PMUs are * counted/written here for ARM, s390 and Intel hybrid.
*/ continue;
} if (perf_pmu__caps_parse(pmu) <= 0) continue;
nr_pmu++;
}
ret = do_write(ff, &nr_pmu, sizeof(nr_pmu)); if (ret < 0) return ret;
if (!nr_pmu) return 0;
/* * Note older perf tools assume core PMUs come first, this is a property * of perf_pmus__scan.
*/
pmu = NULL; while ((pmu = perf_pmus__scan(pmu))) { if (!strcmp(pmu->name, "cpu")) { /* Skip as above. */ continue;
} if (perf_pmu__caps_parse(pmu) <= 0) continue;
ret = __write_pmu_caps(ff, pmu, true); if (ret < 0) return ret;
} return 0;
}
nr = ph->env.nr_sibling_cores;
str = ph->env.sibling_cores;
for (i = 0; i < nr; i++) {
fprintf(fp, "# sibling sockets : %s\n", str);
str += strlen(str) + 1;
}
if (ph->env.nr_sibling_dies) {
nr = ph->env.nr_sibling_dies;
str = ph->env.sibling_dies;
for (i = 0; i < nr; i++) {
fprintf(fp, "# sibling dies : %s\n", str);
str += strlen(str) + 1;
}
}
nr = ph->env.nr_sibling_threads;
str = ph->env.sibling_threads;
for (i = 0; i < nr; i++) {
fprintf(fp, "# sibling threads : %s\n", str);
str += strlen(str) + 1;
}
if (ph->env.nr_sibling_dies) { if (ph->env.cpu != NULL) { for (i = 0; i < cpu_nr; i++)
fprintf(fp, "# CPU %d: Core ID %d, " "Die ID %d, Socket ID %d\n",
i, ph->env.cpu[i].core_id,
ph->env.cpu[i].die_id,
ph->env.cpu[i].socket_id);
} else
fprintf(fp, "# Core ID, Die ID and Socket ID " "information is not available\n");
} else { if (ph->env.cpu != NULL) { for (i = 0; i < cpu_nr; i++)
fprintf(fp, "# CPU %d: Core ID %d, " "Socket ID %d\n",
i, ph->env.cpu[i].core_id,
ph->env.cpu[i].socket_id);
} else
fprintf(fp, "# Core ID and Socket ID " "information is not available\n");
}
}
fprintf(fp, "# hybrid cpu system:\n"); for (i = 0; i < ff->ph->env.nr_hybrid_nodes; i++) {
n = &ff->ph->env.hybrid_nodes[i];
fprintf(fp, "# %s cpu list : %s\n", n->pmu_name, n->cpus);
}
}
if (max_precise != NULL && atoi(max_precise) == 0)
fprintf(fp, "# AMD systems uses ibs_op// PMU for some precise events, e.g.: cycles:p, see the 'perf list' man page for further details.\n");
}
}
if (readn(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 (readn(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;
if (readn(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 (readn(input, filename, len) != len) goto out; /* * The a1645ce1 changeset: * * "perf: 'perf kvm' tool for monitoring guest performance from host" * * Added a field to struct perf_record_header_build_id 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);
}
if (session->data->is_pipe) { /* Save events for reading later by print_event_desc,
* since they can't be read again in pipe mode. */
ff->events = events;
}
for (evsel = events; evsel->core.attr.size; evsel++)
evlist__set_event_name(session->evlist, evsel);
if (!session->data->is_pipe)
free_event_desc(events);
for (i = 0; i < nr; i++) {
str = do_read_string(ff); if (!str) goto error;
/* include a NULL character at the end */ if (strbuf_add(&sb, str, strlen(str) + 1) < 0) goto error;
size += string_size(str);
zfree(&str);
}
env->sibling_cores = strbuf_detach(&sb, NULL);
for (i = 0; i < nr; i++) {
str = do_read_string(ff); if (!str) goto error;
/* include a NULL character at the end */ if (strbuf_add(&sb, str, strlen(str) + 1) < 0) goto error;
size += string_size(str);
zfree(&str);
}
env->sibling_threads = strbuf_detach(&sb, NULL);
/* * The header may be from old perf, * which doesn't include core id and socket id information.
*/ if (ff->size <= size) {
zfree(&env->cpu); return 0;
}
for (i = 0; i < (u32)cpu_nr; i++) { if (do_read_u32(ff, &nr)) goto free_cpu;
/* * The header may be from old perf, * which doesn't include die information.
*/ if (ff->size <= size) return 0;
if (do_read_u32(ff, &nr)) return -1;
env->nr_sibling_dies = nr;
size += sizeof(u32);
for (i = 0; i < nr; i++) {
str = do_read_string(ff); if (!str) goto error;
/* include a NULL character at the end */ if (strbuf_add(&sb, str, strlen(str) + 1) < 0) goto error;
size += string_size(str);
zfree(&str);
}
env->sibling_dies = strbuf_detach(&sb, NULL);
for (i = 0; i < (u32)cpu_nr; i++) { if (do_read_u32(ff, &nr)) goto free_cpu;
if (!pmu_num) {
pr_debug("pmu mappings not available\n"); return 0;
}
env->nr_pmu_mappings = pmu_num; if (strbuf_init(&sb, 128) < 0) return -1;
while (pmu_num) { if (do_read_u32(ff, &type)) goto error;
name = do_read_string(ff); if (!name) goto error;
if (strbuf_addf(&sb, "%u:%s", type, name) < 0) goto error; /* include a NULL character at the end */ if (strbuf_add(&sb, "", 1) < 0) goto error;
if (!strcmp(name, "msr"))
env->msr_pmu_type = type;
free(name);
pmu_num--;
} /* AMD may set it by evlist__has_amd_ibs() from perf_session__new() */
free(env->pmu_mappings);
env->pmu_mappings = strbuf_detach(&sb, NULL); return 0;
if (!nr_pmu) {
pr_debug("pmu capabilities not available\n"); return 0;
}
pmu_caps = zalloc(sizeof(*pmu_caps) * nr_pmu); if (!pmu_caps) return -ENOMEM;
for (i = 0; i < nr_pmu; i++) {
ret = __process_pmu_caps(ff, &pmu_caps[i].nr_caps,
&pmu_caps[i].caps,
&pmu_caps[i].max_branches,
&pmu_caps[i].br_cntr_nr,
&pmu_caps[i].br_cntr_width); if (ret) goto err;
pmu_caps[i].pmu_name = do_read_string(ff); if (!pmu_caps[i].pmu_name) {
ret = -1; goto err;
} if (!pmu_caps[i].nr_caps) {
pr_debug("%s pmu capabilities not available\n",
pmu_caps[i].pmu_name);
}
}
if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
pr_debug("Failed to lseek to %" PRIu64 " offset for feature " "%d, continuing...\n", section->offset, feat); return 0;
} if (feat >= HEADER_LAST_FEATURE) {
pr_warning("unknown feature %d\n", feat); return 0;
} if (!feat_ops[feat].print) return 0;
ff = (struct feat_fd) {
.fd = fd,
.ph = ph,
};
if (!feat_ops[feat].full_only || hd->full)
feat_ops[feat].print(&ff, hd->fp); else
fprintf(hd->fp, "# %s info available, use -I to display\n",
feat_ops[feat].name);
return 0;
}
int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full)
{ struct header_print_data hd; struct perf_header *header = &session->header; int fd = perf_data__fd(session->data); struct stat st;
time_t stctime; int ret, bit;
hd.fp = fp;
hd.full = full;
ret = fstat(fd, &st); if (ret == -1) return -1;
stctime = st.st_mtime;
fprintf(fp, "# captured on : %s", ctime(&stctime));
staticint do_write_feat(struct feat_fd *ff, int type, struct perf_file_section **p, struct evlist *evlist, struct feat_copier *fc)
{ int err; int ret = 0;
if (perf_header__has_feat(ff->ph, type)) { if (!feat_ops[type].write) return -1;
if (WARN(ff->buf, "Error: calling %s in pipe-mode.\n", __func__)) return -1;
(*p)->offset = lseek(ff->fd, 0, SEEK_CUR);
/* * Hook to let perf inject copy features sections from the input * file.
*/ if (fc && fc->copy) { struct header_fw h = {
.fw.write = feat_writer_cb,
.ff = ff,
};
/* ->copy() returns 0 if the feature was not copied */
err = fc->copy(fc, type, &h.fw);
} else {
err = 0;
} if (!err)
err = feat_ops[type].write(ff, evlist); if (err < 0) {
pr_debug("failed to write feature %s\n", feat_ops[type].name);
/* undo anything written */
lseek(ff->fd, (*p)->offset, SEEK_SET);
lseek(fd, sec_start, SEEK_SET); /* * may write more than needed due to dropped feature, but * this is okay, reader will skip the missing entries
*/
err = do_write(&ff, feat_sec, sec_size); if (err < 0)
pr_debug("failed to write feature section\n");
free(ff.buf); /* TODO: added to silence clang-tidy. */
free(feat_sec); return err;
}
int perf_header__write_pipe(int fd)
{ struct perf_pipe_file_header f_header; struct feat_fd ff = {
.fd = fd,
}; int err;
if (write_attrs_after_data && at_exit) { /* * Write features at the end of the file first so that * attributes may come after them.
*/ if (!header->data_offset && header->data_size) {
pr_err("File contains data but offset unknown\n");
err = -1; goto err_out;
}
header->feat_offset = header->data_offset + header->data_size;
err = perf_header__adds_write(header, evlist, fd, fc); if (err < 0) goto err_out;
attr_offset = lseek(fd, 0, SEEK_CUR);
} else {
lseek(fd, attr_offset, SEEK_SET);
}
evlist__for_each_entry(session->evlist, evsel) {
evsel->id_offset = attr_offset; /* Avoid writing at the end of the file until the session is exiting. */ if (!write_attrs_after_data || at_exit) {
err = do_write(&ff, evsel->core.id, evsel->core.ids * sizeof(u64)); if (err < 0) {
pr_debug("failed to write perf header\n"); goto err_out;
}
}
attr_offset += evsel->core.ids * sizeof(u64);
}
evlist__for_each_entry(evlist, evsel) { if (evsel->core.attr.size < sizeof(evsel->core.attr)) { /* * We are likely in "perf inject" and have read * from an older file. Update attr size so that * reader gets the right offset to the ids.
*/
evsel->core.attr.size = sizeof(evsel->core.attr);
} /* Avoid writing at the end of the file until the session is exiting. */ if (!write_attrs_after_data || at_exit) { struct perf_file_attr f_attr = {
.attr = evsel->core.attr,
.ids = {
.offset = evsel->id_offset,
.size = evsel->core.ids * sizeof(u64),
}
};
err = do_write(&ff, &f_attr, sizeof(f_attr)); if (err < 0) {
pr_debug("failed to write perf header attribute\n"); goto err_out;
}
}
attr_size += sizeof(struct perf_file_attr);
}
if (!header->data_offset) { if (write_attrs_after_data)
header->data_offset = sizeof(f_header); else
header->data_offset = attr_offset + attr_size;
}
header->feat_offset = header->data_offset + header->data_size;
if (!write_attrs_after_data && at_exit) { /* Write features now feat_offset is known. */
err = perf_header__adds_write(header, evlist, fd, fc); if (err < 0) goto err_out;
}
staticint perf_header__getbuffer64(struct perf_header *header, int fd, void *buf, size_t size)
{ if (readn(fd, buf, size) <= 0) return -1;
if (header->needs_swap)
mem_bswap_64(buf, size);
return 0;
}
int perf_header__process_sections(struct perf_header *header, int fd, void *data, int (*process)(struct perf_file_section *section, struct perf_header *ph, int feat, int fd, void *data))
{ struct perf_file_section *feat_sec, *sec; int nr_sections; int sec_size; int feat; int err;
nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS); if (!nr_sections) return 0;
feat_sec = sec = calloc(nr_sections, sizeof(*feat_sec)); if (!feat_sec) return -1;
/* * In the legacy file format, the magic number is not used to encode endianness. * hdr_sz was used to encode endianness. But given that hdr_sz can vary based * on ABI revisions, we need to try all combinations for all endianness to * detect the endianness.
*/ staticint try_all_file_abis(uint64_t hdr_sz, struct perf_header *ph)
{
uint64_t ref_size, attr_size; int i;
for (i = 0 ; attr_file_abi_sizes[i]; i++) {
ref_size = attr_file_abi_sizes[i]
+ sizeof(struct perf_file_section); if (hdr_sz != ref_size) {
attr_size = bswap_64(hdr_sz); if (attr_size != ref_size) continue;
ph->needs_swap = true;
}
pr_debug("ABI%d perf.data file detected, need_swap=%d\n",
i,
ph->needs_swap); return 0;
} /* could not determine endianness */ return -1;
}
/* * In the legacy pipe format, there is an implicit assumption that endianness * between host recording the samples, and host parsing the samples is the * same. This is not always the case given that the pipe output may always be * redirected into a file and analyzed on a different machine with possibly a * different endianness and perf_event ABI revisions in the perf tool itself.
*/ staticint try_all_pipe_abis(uint64_t hdr_sz, struct perf_header *ph)
{
u64 attr_size; int i;
for (i = 0 ; attr_pipe_abi_sizes[i]; i++) { if (hdr_sz != attr_pipe_abi_sizes[i]) {
attr_size = bswap_64(hdr_sz); if (attr_size != hdr_sz) continue;
/* check for legacy format */
ret = memcmp(&magic, __perf_magic1, sizeof(magic)); if (ret == 0) {
ph->version = PERF_HEADER_VERSION_1;
pr_debug("legacy perf.data format\n"); if (is_pipe) return try_all_pipe_abis(hdr_sz, ph);
return try_all_file_abis(hdr_sz, ph);
} /* * the new magic number serves two purposes: * - unique number to identify actual perf.data files * - encode endianness of file
*/
ph->version = PERF_HEADER_VERSION_2;
/* check magic number with one endianness */ if (magic == __perf_magic2) return 0;
/* check magic number with 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)
{
ssize_t ret;
lseek(fd, 0, SEEK_SET);
ret = readn(fd, header, sizeof(*header)); if (ret <= 0) return -1;
if (header->size != sizeof(*header)) { /* Support the previous format */ if (header->size == offsetof(typeof(*header), adds_features))
bitmap_zero(header->adds_features, HEADER_FEAT_BITS); else return -1;
} elseif (ph->needs_swap) { /* * feature bitmap is declared as an array of unsigned longs -- * not good since its size can differ between the host that * generated the data file and the host analyzing the file. * * We need to handle endianness, but we don't know the size of * the unsigned long where the file was generated. Take a best * guess at determining it: try 64-bit swap first (ie., file * created on a 64-bit host), and check if the hostname feature * bit is set (this feature bit is forced on as of fbe96f2). * If the bit is not, undo the 64-bit swap and try a 32-bit * swap. If the hostname bit is still not set (e.g., older data * file), punt and fallback to the original behavior -- * clearing all feature bits and setting buildid.
*/
mem_bswap_64(&header->adds_features,
BITS_TO_U64(HEADER_FEAT_BITS));
if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { /* unswap as u64 */
mem_bswap_64(&header->adds_features,
BITS_TO_U64(HEADER_FEAT_BITS));
/* unswap as u32 */
mem_bswap_32(&header->adds_features,
BITS_TO_U32(HEADER_FEAT_BITS));
}
if (!test_bit(HEADER_HOSTNAME, header->adds_features)) {
bitmap_zero(header->adds_features, HEADER_FEAT_BITS);
__set_bit(HEADER_BUILD_ID, header->adds_features);
}
}
/* read minimal guaranteed structure */
ret = readn(fd, attr, PERF_ATTR_SIZE_VER0); if (ret <= 0) {
pr_debug("cannot read %d bytes of header attr\n",
PERF_ATTR_SIZE_VER0); return -1;
}
/* on file perf_event_attr size */
sz = attr->size;
if (ph->needs_swap)
sz = bswap_32(sz);
if (sz == 0) { /* assume ABI0 */
sz = PERF_ATTR_SIZE_VER0;
} elseif (sz > our_sz) {
pr_debug("file uses a more recent and unsupported ABI" " (%zu bytes extra)\n", sz - our_sz); return -1;
} /* what we have not yet read and that we know about */
left = sz - PERF_ATTR_SIZE_VER0; if (left) { void *ptr = attr;
ptr += PERF_ATTR_SIZE_VER0;
ret = readn(fd, ptr, left);
} /* read perf_file_section, ids are read in caller */
ret = readn(fd, &f_attr->ids, sizeof(f_attr->ids));
/* * We can read 'pipe' data event from regular file, * check for the pipe header regardless of source.
*/
err = perf_header__read_pipe(session); if (!err || perf_data__is_pipe(data)) {
data->is_pipe = true; return err;
}
if (perf_file_header__read(&f_header, header, fd) < 0) return -EINVAL;
if (header->needs_swap && data->in_place_update) {
pr_err("In-place update not supported when byte-swapping is required\n"); return -EINVAL;
}
/* * 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) {
pr_warning("WARNING: The %s file's data size field is 0 which is unexpected.\n" "Was the 'perf record' command properly terminated?\n",
data->file.path);
}
if (f_header.attr_size == 0) {
pr_err("ERROR: The %s file's attr size field is 0 which is unexpected.\n" "Was the 'perf record' command properly terminated?\n",
data->file.path); return -EINVAL;
}
evsel->needs_swap = header->needs_swap; /* * Do it before so that if perf_evsel__alloc_id fails, this * entry gets purged too at evlist__delete().
*/
evlist__add(session->evlist, evsel);
nr_ids = f_attr.ids.size / sizeof(u64); /* * We don't have the cpu and thread maps on the header, so * for allocating the perf_sample_id table we fake 1 cpu and * hattr->ids threads.
*/ if (perf_evsel__alloc_id(&evsel->core, 1, nr_ids)) goto out_delete_evlist;
lseek(fd, f_attr.ids.offset, SEEK_SET);
for (j = 0; j < nr_ids; j++) { if (perf_header__getbuffer64(header, fd, &f_id, sizeof(f_id))) goto out_errno;
int perf_event__process_feature(struct perf_session *session, union perf_event *event)
{ struct feat_fd ff = { .fd = 0 }; struct perf_record_header_feature *fe = (struct perf_record_header_feature *)event; int type = fe->header.type;
u64 feat = fe->feat_id; int ret = 0; bool print = dump_trace;
if (type < 0 || type >= PERF_RECORD_HEADER_MAX) {
pr_warning("invalid record type %d in pipe-mode\n", type); return 0;
} if (feat == HEADER_RESERVED || feat >= HEADER_LAST_FEATURE) {
pr_warning("invalid record type %d in pipe-mode\n", type); return -1;
}
ret = fprintf(fp, "\n... id: %" PRI_lu64 "\n", ev->id);
switch (ev->type) { case PERF_EVENT_UPDATE__SCALE:
ret += fprintf(fp, "... scale: %f\n", ev->scale.scale); break; case PERF_EVENT_UPDATE__UNIT:
ret += fprintf(fp, "... unit: %s\n", ev->unit); break; case PERF_EVENT_UPDATE__NAME:
ret += fprintf(fp, "... name: %s\n", ev->name); break; case PERF_EVENT_UPDATE__CPUS:
ret += fprintf(fp, "... ");
map = cpu_map__new_data(&ev->cpus.cpus); if (map) {
ret += cpu_map__fprintf(map, fp);
perf_cpu_map__put(map);
} else
ret += fprintf(fp, "failed to get cpus\n"); break; default:
ret += fprintf(fp, "... unknown type\n"); break;
}
int perf_event__process_attr(conststruct perf_tool *tool __maybe_unused, union perf_event *event, struct evlist **pevlist)
{
u32 i, n_ids;
u64 *ids; struct evsel *evsel; struct evlist *evlist = *pevlist;
if (dump_trace)
perf_event__fprintf_attr(event, stdout);
if (evlist == NULL) {
*pevlist = evlist = evlist__new(); if (evlist == NULL) return -ENOMEM;
}
evsel = evsel__new(&event->attr.attr); if (evsel == NULL) return -ENOMEM;
evlist__add(evlist, evsel);
n_ids = event->header.size - sizeof(event->header) - event->attr.attr.size;
n_ids = n_ids / sizeof(u64); /* * We don't have the cpu and thread maps on the header, so * for allocating the perf_sample_id table we fake 1 cpu and * hattr->ids threads.
*/ if (perf_evsel__alloc_id(&evsel->core, 1, n_ids)) return -ENOMEM;
ids = perf_record_header_attr_id(event); for (i = 0; i < n_ids; i++) {
perf_evlist__id_add(&evlist->core, &evsel->core, 0, i, ids[i]);
}
if (dump_trace)
perf_event__fprintf_event_update(event, stdout);
if (!pevlist || *pevlist == NULL) return -EINVAL;
evlist = *pevlist;
evsel = evlist__id2evsel(evlist, ev->id); if (evsel == NULL) return -EINVAL;
switch (ev->type) { case PERF_EVENT_UPDATE__UNIT:
free((char *)evsel->unit);
evsel->unit = strdup(ev->unit); break; case PERF_EVENT_UPDATE__NAME:
free(evsel->name);
evsel->name = strdup(ev->name); break; case PERF_EVENT_UPDATE__SCALE:
evsel->scale = ev->scale.scale; break; case PERF_EVENT_UPDATE__CPUS:
map = cpu_map__new_data(&ev->cpus.cpus); if (map) {
perf_cpu_map__put(evsel->core.pmu_cpus);
evsel->core.pmu_cpus = map;
} else
pr_err("failed to get event_update cpus\n"); default: break;
}
return 0;
}
#ifdef HAVE_LIBTRACEEVENT int perf_event__process_tracing_data(struct perf_session *session, union perf_event *event)
{
ssize_t size_read, padding, size = event->tracing_data.size; int fd = perf_data__fd(session->data); char buf[BUFSIZ];
/* * The pipe fd is already in proper place and in any case * we can't move it, and we'd screw the case where we read * 'pipe' data from regular file. The trace_report reads * data from 'fd' so we need to set it directly behind the * event, where the tracing data starts.
*/ if (!perf_data__is_pipe(session->data)) {
off_t offset = lseek(fd, 0, SEEK_CUR);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.