aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util/header.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--tools/perf/util/header.c352
1 files changed, 238 insertions, 114 deletions
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index e3c1a532d059..98dfaf84bd13 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -472,7 +472,7 @@ static int write_nrcpus(struct feat_fd *ff,
u32 nrc, nra;
int ret;
- nrc = cpu__max_present_cpu();
+ nrc = cpu__max_present_cpu().cpu;
nr = sysconf(_SC_NPROCESSORS_ONLN);
if (nr < 0)
@@ -983,6 +983,57 @@ static int write_dir_format(struct feat_fd *ff,
return do_write(ff, &data->dir.version, sizeof(data->dir.version));
}
+/*
+ * Check whether a CPU is online
+ *
+ * Returns:
+ * 1 -> if CPU is online
+ * 0 -> if CPU is offline
+ * -1 -> error case
+ */
+int is_cpu_online(unsigned int cpu)
+{
+ char *str;
+ size_t strlen;
+ char buf[256];
+ int status = -1;
+ struct stat statbuf;
+
+ snprintf(buf, sizeof(buf),
+ "/sys/devices/system/cpu/cpu%d", cpu);
+ if (stat(buf, &statbuf) != 0)
+ return 0;
+
+ /*
+ * Check if /sys/devices/system/cpu/cpux/online file
+ * exists. Some cases cpu0 won't have online file since
+ * it is not expected to be turned off generally.
+ * In kernels without CONFIG_HOTPLUG_CPU, this
+ * file won't exist
+ */
+ snprintf(buf, sizeof(buf),
+ "/sys/devices/system/cpu/cpu%d/online", cpu);
+ if (stat(buf, &statbuf) != 0)
+ return 1;
+
+ /*
+ * Read online file using sysfs__read_str.
+ * If read or open fails, return -1.
+ * If read succeeds, return value from file
+ * which gets stored in "str"
+ */
+ snprintf(buf, sizeof(buf),
+ "devices/system/cpu/cpu%d/online", cpu);
+
+ if (sysfs__read_str(buf, &str, &strlen) < 0)
+ return status;
+
+ status = atoi(str);
+
+ free(str);
+ return status;
+}
+
#ifdef HAVE_LIBBPF_SUPPORT
static int write_bpf_prog_info(struct feat_fd *ff,
struct evlist *evlist __maybe_unused)
@@ -1163,7 +1214,7 @@ static int build_caches(struct cpu_cache_level caches[], u32 *cntp)
u32 nr, cpu;
u16 level;
- nr = cpu__max_cpu();
+ nr = cpu__max_cpu().cpu;
for (cpu = 0; cpu < nr; cpu++) {
for (level = 0; level < MAX_CACHE_LVL; level++) {
@@ -1195,7 +1246,7 @@ static int build_caches(struct cpu_cache_level caches[], u32 *cntp)
static int write_cache(struct feat_fd *ff,
struct evlist *evlist __maybe_unused)
{
- u32 max_caches = cpu__max_cpu() * MAX_CACHE_LVL;
+ u32 max_caches = cpu__max_cpu().cpu * MAX_CACHE_LVL;
struct cpu_cache_level caches[max_caches];
u32 cnt = 0, i, version = 1;
int ret;
@@ -1335,7 +1386,7 @@ static int build_mem_topology(struct memory_node *nodes, u64 size, u64 *cntp)
dir = opendir(path);
if (!dir) {
- pr_debug2("%s: could't read %s, does this arch have topology information?\n",
+ pr_debug2("%s: couldn't read %s, does this arch have topology information?\n",
__func__, path);
return -1;
}
@@ -1461,18 +1512,13 @@ static int write_compressed(struct feat_fd *ff __maybe_unused,
return do_write(ff, &(ff->ph->env.comp_mmap_len), sizeof(ff->ph->env.comp_mmap_len));
}
-static int write_per_cpu_pmu_caps(struct feat_fd *ff, struct perf_pmu *pmu,
- bool write_pmu)
+static int __write_pmu_caps(struct feat_fd *ff, struct perf_pmu *pmu,
+ bool write_pmu)
{
struct perf_pmu_caps *caps = NULL;
- int nr_caps;
int ret;
- nr_caps = perf_pmu__caps_parse(pmu);
- if (nr_caps < 0)
- return nr_caps;
-
- ret = do_write(ff, &nr_caps, sizeof(nr_caps));
+ ret = do_write(ff, &pmu->nr_caps, sizeof(pmu->nr_caps));
if (ret < 0)
return ret;
@@ -1499,33 +1545,60 @@ static int write_cpu_pmu_caps(struct feat_fd *ff,
struct evlist *evlist __maybe_unused)
{
struct perf_pmu *cpu_pmu = perf_pmu__find("cpu");
+ int ret;
if (!cpu_pmu)
return -ENOENT;
- return write_per_cpu_pmu_caps(ff, cpu_pmu, false);
+ ret = perf_pmu__caps_parse(cpu_pmu);
+ if (ret < 0)
+ return ret;
+
+ return __write_pmu_caps(ff, cpu_pmu, false);
}
-static int write_hybrid_cpu_pmu_caps(struct feat_fd *ff,
- struct evlist *evlist __maybe_unused)
+static int write_pmu_caps(struct feat_fd *ff,
+ struct evlist *evlist __maybe_unused)
{
- struct perf_pmu *pmu;
- u32 nr_pmu = perf_pmu__hybrid_pmu_num();
+ struct perf_pmu *pmu = NULL;
+ int nr_pmu = 0;
int ret;
- if (nr_pmu == 0)
- return -ENOENT;
+ while ((pmu = perf_pmu__scan(pmu))) {
+ if (!pmu->name || !strcmp(pmu->name, "cpu") ||
+ 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;
+
+ /*
+ * Write hybrid pmu caps first to maintain compatibility with
+ * older perf tool.
+ */
+ pmu = NULL;
perf_pmu__for_each_hybrid_pmu(pmu) {
- ret = write_per_cpu_pmu_caps(ff, pmu, true);
+ ret = __write_pmu_caps(ff, pmu, true);
if (ret < 0)
return ret;
}
+ pmu = NULL;
+ while ((pmu = perf_pmu__scan(pmu))) {
+ if (!pmu->name || !strcmp(pmu->name, "cpu") ||
+ !pmu->nr_caps || perf_pmu__is_hybrid(pmu->name))
+ continue;
+
+ ret = __write_pmu_caps(ff, pmu, true);
+ if (ret < 0)
+ return ret;
+ }
return 0;
}
@@ -2000,32 +2073,20 @@ static void print_compressed(struct feat_fd *ff, FILE *fp)
ff->ph->env.comp_level, ff->ph->env.comp_ratio);
}
-static void print_per_cpu_pmu_caps(FILE *fp, int nr_caps, char *cpu_pmu_caps,
- char *pmu_name)
+static void __print_pmu_caps(FILE *fp, int nr_caps, char **caps, char *pmu_name)
{
- const char *delimiter;
- char *str, buf[128];
+ const char *delimiter = "";
+ int i;
if (!nr_caps) {
- if (!pmu_name)
- fprintf(fp, "# cpu pmu capabilities: not available\n");
- else
- fprintf(fp, "# %s pmu capabilities: not available\n", pmu_name);
+ fprintf(fp, "# %s pmu capabilities: not available\n", pmu_name);
return;
}
- if (!pmu_name)
- scnprintf(buf, sizeof(buf), "# cpu pmu capabilities: ");
- else
- scnprintf(buf, sizeof(buf), "# %s pmu capabilities: ", pmu_name);
-
- delimiter = buf;
-
- str = cpu_pmu_caps;
- while (nr_caps--) {
- fprintf(fp, "%s%s", delimiter, str);
+ fprintf(fp, "# %s pmu capabilities: ", pmu_name);
+ for (i = 0; i < nr_caps; i++) {
+ fprintf(fp, "%s%s", delimiter, caps[i]);
delimiter = ", ";
- str += strlen(str) + 1;
}
fprintf(fp, "\n");
@@ -2033,19 +2094,18 @@ static void print_per_cpu_pmu_caps(FILE *fp, int nr_caps, char *cpu_pmu_caps,
static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp)
{
- print_per_cpu_pmu_caps(fp, ff->ph->env.nr_cpu_pmu_caps,
- ff->ph->env.cpu_pmu_caps, NULL);
+ __print_pmu_caps(fp, ff->ph->env.nr_cpu_pmu_caps,
+ ff->ph->env.cpu_pmu_caps, (char *)"cpu");
}
-static void print_hybrid_cpu_pmu_caps(struct feat_fd *ff, FILE *fp)
+static void print_pmu_caps(struct feat_fd *ff, FILE *fp)
{
- struct hybrid_cpc_node *n;
+ struct pmu_caps *pmu_caps;
- for (int i = 0; i < ff->ph->env.nr_hybrid_cpc_nodes; i++) {
- n = &ff->ph->env.hybrid_cpc_nodes[i];
- print_per_cpu_pmu_caps(fp, n->nr_cpu_pmu_caps,
- n->cpu_pmu_caps,
- n->pmu_name);
+ for (int i = 0; i < ff->ph->env.nr_pmus_with_caps; i++) {
+ pmu_caps = &ff->ph->env.pmu_caps[i];
+ __print_pmu_caps(fp, pmu_caps->nr_caps, pmu_caps->caps,
+ pmu_caps->pmu_name);
}
}
@@ -2200,6 +2260,7 @@ static int __event_process_build_id(struct perf_record_header_build_id *bev,
build_id__init(&bid, bev->data, size);
dso__set_build_id(dso, &bid);
+ dso->header_build_id = 1;
if (dso_space != DSO_SPACE__USER) {
struct kmod_path m = { .name = NULL, };
@@ -3155,28 +3216,26 @@ static int process_compressed(struct feat_fd *ff,
return 0;
}
-static int process_per_cpu_pmu_caps(struct feat_fd *ff, int *nr_cpu_pmu_caps,
- char **cpu_pmu_caps,
- unsigned int *max_branches)
+static int __process_pmu_caps(struct feat_fd *ff, int *nr_caps,
+ char ***caps, unsigned int *max_branches)
{
- char *name, *value;
- struct strbuf sb;
- u32 nr_caps;
+ char *name, *value, *ptr;
+ u32 nr_pmu_caps, i;
+
+ *nr_caps = 0;
+ *caps = NULL;
- if (do_read_u32(ff, &nr_caps))
+ if (do_read_u32(ff, &nr_pmu_caps))
return -1;
- if (!nr_caps) {
- pr_debug("cpu pmu capabilities not available\n");
+ if (!nr_pmu_caps)
return 0;
- }
-
- *nr_cpu_pmu_caps = nr_caps;
- if (strbuf_init(&sb, 128) < 0)
+ *caps = zalloc(sizeof(char *) * nr_pmu_caps);
+ if (!*caps)
return -1;
- while (nr_caps--) {
+ for (i = 0; i < nr_pmu_caps; i++) {
name = do_read_string(ff);
if (!name)
goto error;
@@ -3185,12 +3244,10 @@ static int process_per_cpu_pmu_caps(struct feat_fd *ff, int *nr_cpu_pmu_caps,
if (!value)
goto free_name;
- if (strbuf_addf(&sb, "%s=%s", name, value) < 0)
+ if (asprintf(&ptr, "%s=%s", name, value) < 0)
goto free_value;
- /* include a NULL character at the end */
- if (strbuf_add(&sb, "", 1) < 0)
- goto free_value;
+ (*caps)[i] = ptr;
if (!strcmp(name, "branches"))
*max_branches = atoi(value);
@@ -3198,7 +3255,7 @@ static int process_per_cpu_pmu_caps(struct feat_fd *ff, int *nr_cpu_pmu_caps,
free(value);
free(name);
}
- *cpu_pmu_caps = strbuf_detach(&sb, NULL);
+ *nr_caps = nr_pmu_caps;
return 0;
free_value:
@@ -3206,64 +3263,76 @@ free_value:
free_name:
free(name);
error:
- strbuf_release(&sb);
+ for (; i > 0; i--)
+ free((*caps)[i - 1]);
+ free(*caps);
+ *caps = NULL;
+ *nr_caps = 0;
return -1;
}
static int process_cpu_pmu_caps(struct feat_fd *ff,
void *data __maybe_unused)
{
- return process_per_cpu_pmu_caps(ff, &ff->ph->env.nr_cpu_pmu_caps,
- &ff->ph->env.cpu_pmu_caps,
- &ff->ph->env.max_branches);
+ int ret = __process_pmu_caps(ff, &ff->ph->env.nr_cpu_pmu_caps,
+ &ff->ph->env.cpu_pmu_caps,
+ &ff->ph->env.max_branches);
+
+ if (!ret && !ff->ph->env.cpu_pmu_caps)
+ pr_debug("cpu pmu capabilities not available\n");
+ return ret;
}
-static int process_hybrid_cpu_pmu_caps(struct feat_fd *ff,
- void *data __maybe_unused)
+static int process_pmu_caps(struct feat_fd *ff, void *data __maybe_unused)
{
- struct hybrid_cpc_node *nodes;
+ struct pmu_caps *pmu_caps;
u32 nr_pmu, i;
int ret;
+ int j;
if (do_read_u32(ff, &nr_pmu))
return -1;
if (!nr_pmu) {
- pr_debug("hybrid cpu pmu capabilities not available\n");
+ pr_debug("pmu capabilities not available\n");
return 0;
}
- nodes = zalloc(sizeof(*nodes) * nr_pmu);
- if (!nodes)
+ pmu_caps = zalloc(sizeof(*pmu_caps) * nr_pmu);
+ if (!pmu_caps)
return -ENOMEM;
for (i = 0; i < nr_pmu; i++) {
- struct hybrid_cpc_node *n = &nodes[i];
-
- ret = process_per_cpu_pmu_caps(ff, &n->nr_cpu_pmu_caps,
- &n->cpu_pmu_caps,
- &n->max_branches);
+ ret = __process_pmu_caps(ff, &pmu_caps[i].nr_caps,
+ &pmu_caps[i].caps,
+ &pmu_caps[i].max_branches);
if (ret)
goto err;
- n->pmu_name = do_read_string(ff);
- if (!n->pmu_name) {
+ 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);
+ }
}
- ff->ph->env.nr_hybrid_cpc_nodes = nr_pmu;
- ff->ph->env.hybrid_cpc_nodes = nodes;
+ ff->ph->env.nr_pmus_with_caps = nr_pmu;
+ ff->ph->env.pmu_caps = pmu_caps;
return 0;
err:
for (i = 0; i < nr_pmu; i++) {
- free(nodes[i].cpu_pmu_caps);
- free(nodes[i].pmu_name);
+ for (j = 0; j < pmu_caps[i].nr_caps; j++)
+ free(pmu_caps[i].caps[j]);
+ free(pmu_caps[i].caps);
+ free(pmu_caps[i].pmu_name);
}
- free(nodes);
+ free(pmu_caps);
return ret;
}
@@ -3329,7 +3398,7 @@ const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPR(CPU_PMU_CAPS, cpu_pmu_caps, false),
FEAT_OPR(CLOCK_DATA, clock_data, false),
FEAT_OPN(HYBRID_TOPOLOGY, hybrid_topology, true),
- FEAT_OPR(HYBRID_CPU_PMU_CAPS, hybrid_cpu_pmu_caps, false),
+ FEAT_OPR(PMU_CAPS, pmu_caps, false),
};
struct header_print_data {
@@ -3410,9 +3479,22 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full)
return 0;
}
+struct header_fw {
+ struct feat_writer fw;
+ struct feat_fd *ff;
+};
+
+static int feat_writer_cb(struct feat_writer *fw, void *buf, size_t sz)
+{
+ struct header_fw *h = container_of(fw, struct header_fw, fw);
+
+ return do_write(h->ff, buf, sz);
+}
+
static int do_write_feat(struct feat_fd *ff, int type,
struct perf_file_section **p,
- struct evlist *evlist)
+ struct evlist *evlist,
+ struct feat_copier *fc)
{
int err;
int ret = 0;
@@ -3426,7 +3508,23 @@ static int do_write_feat(struct feat_fd *ff, int type,
(*p)->offset = lseek(ff->fd, 0, SEEK_CUR);
- err = feat_ops[type].write(ff, evlist);
+ /*
+ * 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);
@@ -3442,7 +3540,8 @@ static int do_write_feat(struct feat_fd *ff, int type,
}
static int perf_header__adds_write(struct perf_header *header,
- struct evlist *evlist, int fd)
+ struct evlist *evlist, int fd,
+ struct feat_copier *fc)
{
int nr_sections;
struct feat_fd ff;
@@ -3471,7 +3570,7 @@ static int perf_header__adds_write(struct perf_header *header,
lseek(fd, sec_start + sec_size, SEEK_SET);
for_each_set_bit(feat, header->adds_features, HEADER_FEAT_BITS) {
- if (do_write_feat(&ff, feat, &p, evlist))
+ if (do_write_feat(&ff, feat, &p, evlist, fc))
perf_header__clear_feat(header, feat);
}
@@ -3509,9 +3608,10 @@ int perf_header__write_pipe(int fd)
return 0;
}
-int perf_session__write_header(struct perf_session *session,
- struct evlist *evlist,
- int fd, bool at_exit)
+static int perf_session__do_write_header(struct perf_session *session,
+ struct evlist *evlist,
+ int fd, bool at_exit,
+ struct feat_copier *fc)
{
struct perf_file_header f_header;
struct perf_file_attr f_attr;
@@ -3563,7 +3663,7 @@ int perf_session__write_header(struct perf_session *session,
header->feat_offset = header->data_offset + header->data_size;
if (at_exit) {
- err = perf_header__adds_write(header, evlist, fd);
+ err = perf_header__adds_write(header, evlist, fd, fc);
if (err < 0)
return err;
}
@@ -3596,6 +3696,35 @@ int perf_session__write_header(struct perf_session *session,
return 0;
}
+int perf_session__write_header(struct perf_session *session,
+ struct evlist *evlist,
+ int fd, bool at_exit)
+{
+ return perf_session__do_write_header(session, evlist, fd, at_exit, NULL);
+}
+
+size_t perf_session__data_offset(const struct evlist *evlist)
+{
+ struct evsel *evsel;
+ size_t data_offset;
+
+ data_offset = sizeof(struct perf_file_header);
+ evlist__for_each_entry(evlist, evsel) {
+ data_offset += evsel->core.ids * sizeof(u64);
+ }
+ data_offset += evlist->core.nr_entries * sizeof(struct perf_file_attr);
+
+ return data_offset;
+}
+
+int perf_session__inject_header(struct perf_session *session,
+ struct evlist *evlist,
+ int fd,
+ struct feat_copier *fc)
+{
+ return perf_session__do_write_header(session, evlist, fd, true, fc);
+}
+
static int perf_header__getbuffer64(struct perf_header *header,
int fd, void *buf, size_t size)
{
@@ -4166,8 +4295,6 @@ out:
size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp)
{
struct perf_record_event_update *ev = &event->event_update;
- struct perf_record_event_update_scale *ev_scale;
- struct perf_record_event_update_cpus *ev_cpus;
struct perf_cpu_map *map;
size_t ret;
@@ -4175,20 +4302,18 @@ size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp)
switch (ev->type) {
case PERF_EVENT_UPDATE__SCALE:
- ev_scale = (struct perf_record_event_update_scale *)ev->data;
- ret += fprintf(fp, "... scale: %f\n", ev_scale->scale);
+ ret += fprintf(fp, "... scale: %f\n", ev->scale.scale);
break;
case PERF_EVENT_UPDATE__UNIT:
- ret += fprintf(fp, "... unit: %s\n", ev->data);
+ ret += fprintf(fp, "... unit: %s\n", ev->unit);
break;
case PERF_EVENT_UPDATE__NAME:
- ret += fprintf(fp, "... name: %s\n", ev->data);
+ ret += fprintf(fp, "... name: %s\n", ev->name);
break;
case PERF_EVENT_UPDATE__CPUS:
- ev_cpus = (struct perf_record_event_update_cpus *)ev->data;
ret += fprintf(fp, "... ");
- map = cpu_map__new_data(&ev_cpus->cpus);
+ map = cpu_map__new_data(&ev->cpus.cpus);
if (map)
ret += cpu_map__fprintf(map, fp);
else
@@ -4245,12 +4370,13 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused,
struct evlist **pevlist)
{
struct perf_record_event_update *ev = &event->event_update;
- struct perf_record_event_update_scale *ev_scale;
- struct perf_record_event_update_cpus *ev_cpus;
struct evlist *evlist;
struct evsel *evsel;
struct perf_cpu_map *map;
+ if (dump_trace)
+ perf_event__fprintf_event_update(event, stdout);
+
if (!pevlist || *pevlist == NULL)
return -EINVAL;
@@ -4263,19 +4389,17 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused,
switch (ev->type) {
case PERF_EVENT_UPDATE__UNIT:
free((char *)evsel->unit);
- evsel->unit = strdup(ev->data);
+ evsel->unit = strdup(ev->unit);
break;
case PERF_EVENT_UPDATE__NAME:
free(evsel->name);
- evsel->name = strdup(ev->data);
+ evsel->name = strdup(ev->name);
break;
case PERF_EVENT_UPDATE__SCALE:
- ev_scale = (struct perf_record_event_update_scale *)ev->data;
- evsel->scale = ev_scale->scale;
+ evsel->scale = ev->scale.scale;
break;
case PERF_EVENT_UPDATE__CPUS:
- ev_cpus = (struct perf_record_event_update_cpus *)ev->data;
- map = cpu_map__new_data(&ev_cpus->cpus);
+ map = cpu_map__new_data(&ev->cpus.cpus);
if (map) {
perf_cpu_map__put(evsel->core.own_cpus);
evsel->core.own_cpus = map;