* [PATCH V3 00/17] Stitch LBR call stack (Perf Tools)
@ 2020-03-13 18:33 kan.liang
2020-03-13 18:33 ` [PATCH V3 01/17] perf pmu: Add support for PMU capabilities kan.liang
` (16 more replies)
0 siblings, 17 replies; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
Changes since V2:
- Check strdup() in Patch 1
- Split several patches into smaller patches
Changes since V1:
- Rebase on top of commit 5100c2b77049 ("perf header: Add check for
unexpected use of reserved membrs in event attr")
- Fix compling error with GCC9 in patch 1.
The kernel patches have been merged into linux-next.
commit bbfd5e4fab63 ("perf/core: Add new branch sample type for HW
index of raw branch records")
commit db278b90c326 ("perf/x86/intel: Output LBR TOS information
correctly")
Start from Haswell, Linux perf can utilize the existing Last Branch
Record (LBR) facility to record call stack. However, the depth of the
reconstructed LBR call stack limits to the number of LBR registers.
E.g. on skylake, the depth of reconstructed LBR call stack is <= 32
That's because HW will overwrite the oldest LBR registers when it's
full.
However, the overwritten LBRs may still be retrieved from previous
sample. At that moment, HW hasn't overwritten the LBR registers yet.
Perf tools can stitch those overwritten LBRs on current call stacks to
get a more complete call stack.
To determine if LBRs can be stitched, the maximum number of LBRs is
required. Patch 1 - 4 retrieve the capabilities information from sysfs
and save them in perf header.
Patch 5 - 12 implements the LBR stitching approach.
Users can use the options introduced in patch 13-16 to enable the LBR
stitching approach for perf report, script, top and c2c.
Patch 17 adds a fast path for duplicate entries check. It benefits all
call stack parsing, not just for stitch LBR call stack. It can be
merged independently.
The stitching approach base on LBR call stack technology. The known
limitations of LBR call stack technology still apply to the approach,
e.g. Exception handing such as setjmp/longjmp will have calls/returns
not match.
This approach is not full proof. There can be cases where it creates
incorrect call stacks from incorrect matches. There is no attempt
to validate any matches in another way. So it is not enabled by default.
However in many common cases with call stack overflows it can recreate
better call stacks than the default lbr call stack output. So if there
are problems with LBR overflows this is a possible workaround.
Regression:
Users may collect LBR call stack on a machine with new perf tool and
new kernel (support LBR TOS). However, they may parse the perf.data with
old perf tool (not support LBR TOS). The old tool doesn't check
attr.branch_sample_type. Users probably get incorrect information
without any warning.
Performance impact:
The processing time may increase with the LBR stitching approach
enabled. The impact depends on the increased depth of call stacks.
For a simple test case tchain_edit with 43 depth of call stacks.
perf record --call-graph lbr -- ./tchain_edit
perf report --stitch-lbr
Without --stitch-lbr, perf report only display 32 depth of call stacks.
With --stitch-lbr, perf report can display all 43 depth of call stacks.
The depth of call stacks increase 34.3%.
Correspondingly, the processing time of perf report increases 39%,
Without --stitch-lbr: 11.0 sec
With --stitch-lbr: 15.3 sec
The source code of tchain_edit.c is something similar as below.
noinline void f43(void)
{
int i;
for (i = 0; i < 10000;) {
if(i%2)
i++;
else
i++;
}
}
noinline void f42(void)
{
int i;
for (i = 0; i < 100; i++) {
f43();
f43();
f43();
}
}
noinline void f41(void)
{
int i;
for (i = 0; i < 100; i++) {
f42();
f42();
f42();
}
}
noinline void f40(void)
{
f41();
}
... ...
noinline void f32(void)
{
f33();
}
noinline void f31(void)
{
int i;
for (i = 0; i < 10000; i++) {
if(i%2)
i++;
else
i++;
}
f32();
}
noinline void f30(void)
{
f31();
}
... ...
noinline void f1(void)
{
f2();
}
int main()
{
f1();
}
Kan Liang (17):
perf pmu: Add support for PMU capabilities
perf header: Support CPU PMU capabilities
perf record: Clear HEADER_CPU_PMU_CAPS for non LBR call stack mode
perf stat: Clear HEADER_CPU_PMU_CAPS
perf machine: Remove the indent in resolve_lbr_callchain_sample
perf machine: Refine the function for LBR call stack reconstruction
perf machine: Factor out lbr_callchain_add_kernel_ip()
perf machine: Factor out lbr_callchain_add_lbr_ip()
perf thread: Add a knob for LBR stitch approach
perf tools: Save previous sample for LBR stitching approach
perf tools: Save previous cursor nodes for LBR stitching approach
perf tools: Stitch LBR call stack
perf report: Add option to enable the LBR stitching approach
perf script: Add option to enable the LBR stitching approach
perf top: Add option to enable the LBR stitching approach
perf c2c: Add option to enable the LBR stitching approach
perf hist: Add fast path for duplicate entries check approach
tools/perf/Documentation/perf-c2c.txt | 11 +
tools/perf/Documentation/perf-report.txt | 11 +
tools/perf/Documentation/perf-script.txt | 11 +
tools/perf/Documentation/perf-top.txt | 9 +
.../Documentation/perf.data-file-format.txt | 16 +
tools/perf/builtin-c2c.c | 6 +
tools/perf/builtin-record.c | 3 +
tools/perf/builtin-report.c | 6 +
tools/perf/builtin-script.c | 6 +
tools/perf/builtin-stat.c | 1 +
tools/perf/builtin-top.c | 11 +
tools/perf/util/branch.h | 19 +-
tools/perf/util/callchain.h | 8 +
tools/perf/util/env.h | 3 +
tools/perf/util/header.c | 110 +++++
tools/perf/util/header.h | 1 +
tools/perf/util/hist.c | 23 +
tools/perf/util/machine.c | 423 +++++++++++++++---
tools/perf/util/pmu.c | 98 ++++
tools/perf/util/pmu.h | 12 +
tools/perf/util/sort.c | 2 +-
tools/perf/util/sort.h | 2 +
tools/perf/util/thread.c | 2 +
tools/perf/util/thread.h | 35 ++
tools/perf/util/top.h | 1 +
25 files changed, 760 insertions(+), 70 deletions(-)
--
2.17.1
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH V3 01/17] perf pmu: Add support for PMU capabilities
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-18 19:47 ` Arnaldo Carvalho de Melo
2020-03-13 18:33 ` [PATCH V3 02/17] perf header: Support CPU " kan.liang
` (15 subsequent siblings)
16 siblings, 1 reply; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
The PMU capabilities information, which is located at
/sys/bus/event_source/devices/<dev>/caps, is required by perf tool.
For example, the max LBR information is required to stitch LBR call
stack.
Add perf_pmu__caps_parse() to parse the PMU capabilities information.
The information is stored in a list.
Add perf_pmu__scan_caps() to scan the capabilities one by one.
The following patch will store the capabilities information in perf
header.
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/util/pmu.c | 98 +++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/pmu.h | 12 ++++++
2 files changed, 110 insertions(+)
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 8b99fd312aae..4cdfbb669567 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -844,6 +844,7 @@ static struct perf_pmu *pmu_lookup(const char *name)
INIT_LIST_HEAD(&pmu->format);
INIT_LIST_HEAD(&pmu->aliases);
+ INIT_LIST_HEAD(&pmu->caps);
list_splice(&format, &pmu->format);
list_splice(&aliases, &pmu->aliases);
list_add_tail(&pmu->list, &pmus);
@@ -1565,3 +1566,100 @@ int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
va_end(args);
return ret;
}
+
+static int perf_pmu__new_caps(struct list_head *list, char *name, char *value)
+{
+ struct perf_pmu_caps *caps;
+
+ caps = zalloc(sizeof(*caps));
+ if (!caps)
+ return -ENOMEM;
+
+ caps->name = strdup(name);
+ if (!caps->name)
+ goto free_caps;
+ caps->value = strndup(value, strlen(value) - 1);
+ if (!caps->value)
+ goto free_name;
+ list_add_tail(&caps->list, list);
+ return 0;
+
+free_name:
+ free(caps->name);
+free_caps:
+ free(caps);
+
+ return -ENOMEM;
+}
+
+/*
+ * Reading/parsing the given pmu capabilities, which should be located at:
+ * /sys/bus/event_source/devices/<dev>/caps as sysfs group attributes.
+ * Return the number of capabilities
+ */
+int perf_pmu__caps_parse(struct perf_pmu *pmu)
+{
+ struct stat st;
+ char caps_path[PATH_MAX];
+ const char *sysfs = sysfs__mountpoint();
+ DIR *caps_dir;
+ struct dirent *evt_ent;
+ int nr_caps = 0;
+
+ if (!sysfs)
+ return -1;
+
+ snprintf(caps_path, PATH_MAX,
+ "%s" EVENT_SOURCE_DEVICE_PATH "%s/caps", sysfs, pmu->name);
+
+ if (stat(caps_path, &st) < 0)
+ return 0; /* no error if caps does not exist */
+
+ caps_dir = opendir(caps_path);
+ if (!caps_dir)
+ return -EINVAL;
+
+ while ((evt_ent = readdir(caps_dir)) != NULL) {
+ char path[PATH_MAX + NAME_MAX + 1];
+ char *name = evt_ent->d_name;
+ char value[128];
+ FILE *file;
+
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ continue;
+
+ snprintf(path, sizeof(path), "%s/%s", caps_path, name);
+
+ file = fopen(path, "r");
+ if (!file)
+ break;
+
+ if (!fgets(value, sizeof(value), file) ||
+ (perf_pmu__new_caps(&pmu->caps, name, value) < 0)) {
+ fclose(file);
+ break;
+ }
+
+ nr_caps++;
+ fclose(file);
+ }
+
+ closedir(caps_dir);
+
+ return nr_caps;
+}
+
+struct perf_pmu_caps *perf_pmu__scan_caps(struct perf_pmu *pmu,
+ struct perf_pmu_caps *caps)
+{
+ if (!pmu)
+ return NULL;
+
+ if (!caps)
+ caps = list_prepare_entry(caps, &pmu->caps, list);
+
+ list_for_each_entry_continue(caps, &pmu->caps, list)
+ return caps;
+
+ return NULL;
+}
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 6737e3d5d568..a228e27ae462 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -21,6 +21,12 @@ enum {
struct perf_event_attr;
+struct perf_pmu_caps {
+ char *name;
+ char *value;
+ struct list_head list;
+};
+
struct perf_pmu {
char *name;
__u32 type;
@@ -32,6 +38,7 @@ struct perf_pmu {
struct perf_cpu_map *cpus;
struct list_head format; /* HEAD struct perf_pmu_format -> list */
struct list_head aliases; /* HEAD struct perf_pmu_alias -> list */
+ struct list_head caps; /* HEAD struct perf_pmu_caps -> list */
struct list_head list; /* ELEM */
};
@@ -102,4 +109,9 @@ struct pmu_events_map *perf_pmu__find_map(struct perf_pmu *pmu);
int perf_pmu__convert_scale(const char *scale, char **end, double *sval);
+int perf_pmu__caps_parse(struct perf_pmu *pmu);
+
+struct perf_pmu_caps *perf_pmu__scan_caps(struct perf_pmu *pmu,
+ struct perf_pmu_caps *caps);
+
#endif /* __PMU_H */
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 02/17] perf header: Support CPU PMU capabilities
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
2020-03-13 18:33 ` [PATCH V3 01/17] perf pmu: Add support for PMU capabilities kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-18 19:49 ` Arnaldo Carvalho de Melo
2020-03-13 18:33 ` [PATCH V3 03/17] perf record: Clear HEADER_CPU_PMU_CAPS for non LBR call stack mode kan.liang
` (14 subsequent siblings)
16 siblings, 1 reply; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
To stitch LBR call stack, the max LBR information is required. So the
CPU PMU capabilities information has to be stored in perf header.
Add a new feature HEADER_CPU_PMU_CAPS for CPU PMU capabilities.
Retrieve all CPU PMU capabilities, not just max LBR information.
Add variable max_branches to facilitate future usage.
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
.../Documentation/perf.data-file-format.txt | 16 +++
tools/perf/util/env.h | 3 +
| 110 ++++++++++++++++++
| 1 +
4 files changed, 130 insertions(+)
diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt
index b0152e1095c5..b6472e463284 100644
--- a/tools/perf/Documentation/perf.data-file-format.txt
+++ b/tools/perf/Documentation/perf.data-file-format.txt
@@ -373,6 +373,22 @@ struct {
Indicates that trace contains records of PERF_RECORD_COMPRESSED type
that have perf_events records in compressed form.
+ HEADER_CPU_PMU_CAPS = 28,
+
+ A list of cpu PMU capabilities. The format of data is as below.
+
+struct {
+ u32 nr_cpu_pmu_caps;
+ {
+ char name[];
+ char value[];
+ } [nr_cpu_pmu_caps]
+};
+
+
+Example:
+ cpu pmu capabilities: branches=32, max_precise=3, pmu_name=icelake
+
other bits are reserved and should ignored for now
HEADER_FEAT_BITS = 256,
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
index 11d05ae3606a..d286d478b4d8 100644
--- a/tools/perf/util/env.h
+++ b/tools/perf/util/env.h
@@ -48,6 +48,7 @@ struct perf_env {
char *cpuid;
unsigned long long total_mem;
unsigned int msr_pmu_type;
+ unsigned int max_branches;
int nr_cmdline;
int nr_sibling_cores;
@@ -57,12 +58,14 @@ struct perf_env {
int nr_memory_nodes;
int nr_pmu_mappings;
int nr_groups;
+ int nr_cpu_pmu_caps;
char *cmdline;
const char **cmdline_argv;
char *sibling_cores;
char *sibling_dies;
char *sibling_threads;
char *pmu_mappings;
+ char *cpu_pmu_caps;
struct cpu_topology_map *cpu;
struct cpu_cache_level *caches;
int caches_cnt;
--git a/tools/perf/util/header.c b/tools/perf/util/header.c
index acbd046bf95c..ce29321a4e1d 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1395,6 +1395,39 @@ 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_cpu_pmu_caps(struct feat_fd *ff,
+ struct evlist *evlist __maybe_unused)
+{
+ struct perf_pmu_caps *caps = NULL;
+ struct perf_pmu *cpu_pmu;
+ int nr_caps;
+ int ret;
+
+ cpu_pmu = perf_pmu__find("cpu");
+ if (!cpu_pmu)
+ return -ENOENT;
+
+ nr_caps = perf_pmu__caps_parse(cpu_pmu);
+ if (nr_caps < 0)
+ return nr_caps;
+
+ ret = do_write(ff, &nr_caps, sizeof(nr_caps));
+ if (ret < 0)
+ return ret;
+
+ while ((caps = perf_pmu__scan_caps(cpu_pmu, caps))) {
+ ret = do_write_string(ff, caps->name);
+ if (ret < 0)
+ return ret;
+
+ ret = do_write_string(ff, caps->value);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
+
static void print_hostname(struct feat_fd *ff, FILE *fp)
{
fprintf(fp, "# hostname : %s\n", ff->ph->env.hostname);
@@ -1809,6 +1842,28 @@ static void print_compressed(struct feat_fd *ff, FILE *fp)
ff->ph->env.comp_level, ff->ph->env.comp_ratio);
}
+static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp)
+{
+ const char *delimiter = "# cpu pmu capabilities: ";
+ char *str;
+ u32 nr_caps;
+
+ nr_caps = ff->ph->env.nr_cpu_pmu_caps;
+ if (!nr_caps) {
+ fprintf(fp, "# cpu pmu capabilities: not available\n");
+ return;
+ }
+
+ str = ff->ph->env.cpu_pmu_caps;
+ while (nr_caps--) {
+ fprintf(fp, "%s%s", delimiter, str);
+ delimiter = ", ";
+ str += strlen(str) + 1;
+ }
+
+ fprintf(fp, "\n");
+}
+
static void print_pmu_mappings(struct feat_fd *ff, FILE *fp)
{
const char *delimiter = "# pmu mappings: ";
@@ -2846,6 +2901,60 @@ static int process_compressed(struct feat_fd *ff,
return 0;
}
+static int process_cpu_pmu_caps(struct feat_fd *ff,
+ void *data __maybe_unused)
+{
+ char *name, *value;
+ struct strbuf sb;
+ u32 nr_caps;
+
+ if (do_read_u32(ff, &nr_caps))
+ return -1;
+
+ if (!nr_caps) {
+ pr_debug("cpu pmu capabilities not available\n");
+ return 0;
+ }
+
+ ff->ph->env.nr_cpu_pmu_caps = nr_caps;
+
+ if (strbuf_init(&sb, 128) < 0)
+ return -1;
+
+ while (nr_caps--) {
+ name = do_read_string(ff);
+ if (!name)
+ goto error;
+
+ value = do_read_string(ff);
+ if (!value)
+ goto free_name;
+
+ if (strbuf_addf(&sb, "%s=%s", name, value) < 0)
+ goto free_value;
+
+ /* include a NULL character at the end */
+ if (strbuf_add(&sb, "", 1) < 0)
+ goto free_value;
+
+ if (!strcmp(name, "branches"))
+ ff->ph->env.max_branches = atoi(value);
+
+ free(value);
+ free(name);
+ }
+ ff->ph->env.cpu_pmu_caps = strbuf_detach(&sb, NULL);
+ return 0;
+
+free_value:
+ free(value);
+free_name:
+ free(name);
+error:
+ strbuf_release(&sb);
+ return -1;
+}
+
#define FEAT_OPR(n, func, __full_only) \
[HEADER_##n] = { \
.name = __stringify(n), \
@@ -2903,6 +3012,7 @@ const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPR(BPF_PROG_INFO, bpf_prog_info, false),
FEAT_OPR(BPF_BTF, bpf_btf, false),
FEAT_OPR(COMPRESSED, compressed, false),
+ FEAT_OPR(CPU_PMU_CAPS, cpu_pmu_caps, false),
};
struct header_print_data {
--git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 840f95cee349..650bd1c7a99b 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -43,6 +43,7 @@ enum {
HEADER_BPF_PROG_INFO,
HEADER_BPF_BTF,
HEADER_COMPRESSED,
+ HEADER_CPU_PMU_CAPS,
HEADER_LAST_FEATURE,
HEADER_FEAT_BITS = 256,
};
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 03/17] perf record: Clear HEADER_CPU_PMU_CAPS for non LBR call stack mode
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
2020-03-13 18:33 ` [PATCH V3 01/17] perf pmu: Add support for PMU capabilities kan.liang
2020-03-13 18:33 ` [PATCH V3 02/17] perf header: Support CPU " kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-13 18:33 ` [PATCH V3 04/17] perf stat: Clear HEADER_CPU_PMU_CAPS kan.liang
` (13 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
The CPU PMU capabilities information is only useful for LBR call stack.
Clear the feature for other perf record mode.
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/builtin-record.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 4c301466101b..428f7f5b8e48 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1120,6 +1120,9 @@ static void record__init_features(struct record *rec)
if (!record__comp_enabled(rec))
perf_header__clear_feat(&session->header, HEADER_COMPRESSED);
+ if (!callchain_param.enabled || (callchain_param.record_mode != CALLCHAIN_LBR))
+ perf_header__clear_feat(&session->header, HEADER_CPU_PMU_CAPS);
+
perf_header__clear_feat(&session->header, HEADER_STAT);
}
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 04/17] perf stat: Clear HEADER_CPU_PMU_CAPS
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
` (2 preceding siblings ...)
2020-03-13 18:33 ` [PATCH V3 03/17] perf record: Clear HEADER_CPU_PMU_CAPS for non LBR call stack mode kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-13 18:33 ` [PATCH V3 05/17] perf machine: Remove the indent in resolve_lbr_callchain_sample kan.liang
` (12 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
The CPU PMU capabilities information is only useful for perf record with
LBR call stack.
Clear the header for perf stat.
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/builtin-stat.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index ec053dc1e35c..b5c8a5ab5e75 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -1595,6 +1595,7 @@ static void init_features(struct perf_session *session)
perf_header__clear_feat(&session->header, HEADER_TRACING_DATA);
perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK);
perf_header__clear_feat(&session->header, HEADER_AUXTRACE);
+ perf_header__clear_feat(&session->header, HEADER_CPU_PMU_CAPS);
}
static int __cmd_record(int argc, const char **argv)
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 05/17] perf machine: Remove the indent in resolve_lbr_callchain_sample
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
` (3 preceding siblings ...)
2020-03-13 18:33 ` [PATCH V3 04/17] perf stat: Clear HEADER_CPU_PMU_CAPS kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-18 19:50 ` Arnaldo Carvalho de Melo
2020-03-13 18:33 ` [PATCH V3 06/17] perf machine: Refine the function for LBR call stack reconstruction kan.liang
` (11 subsequent siblings)
16 siblings, 1 reply; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
The indent is unnecessary in resolve_lbr_callchain_sample.
Removing it will make the following patch simpler.
Current code path for resolve_lbr_callchain_sample()
/* LBR only affects the user callchain */
if (i != chain_nr) {
body of the function
....
return 1;
}
return 0;
With the patch,
/* LBR only affects the user callchain */
if (i == chain_nr)
return 0;
body of the function
...
return 1;
No functional changes.
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/util/machine.c | 123 +++++++++++++++++++-------------------
1 file changed, 63 insertions(+), 60 deletions(-)
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index fd14f1489802..9021e5b6a2a9 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -2177,6 +2177,12 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
int chain_nr = min(max_stack, (int)chain->nr), i;
u8 cpumode = PERF_RECORD_MISC_USER;
u64 ip, branch_from = 0;
+ struct branch_stack *lbr_stack;
+ struct branch_entry *entries;
+ int lbr_nr, j, k;
+ bool branch;
+ struct branch_flags *flags;
+ int mix_chain_nr;
for (i = 0; i < chain_nr; i++) {
if (chain->ips[i] == PERF_CONTEXT_USER)
@@ -2184,71 +2190,68 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
}
/* LBR only affects the user callchain */
- if (i != chain_nr) {
- struct branch_stack *lbr_stack = sample->branch_stack;
- struct branch_entry *entries = perf_sample__branch_entries(sample);
- int lbr_nr = lbr_stack->nr, j, k;
- bool branch;
- struct branch_flags *flags;
- /*
- * LBR callstack can only get user call chain.
- * The mix_chain_nr is kernel call chain
- * number plus LBR user call chain number.
- * i is kernel call chain number,
- * 1 is PERF_CONTEXT_USER,
- * lbr_nr + 1 is the user call chain number.
- * For details, please refer to the comments
- * in callchain__printf
- */
- int mix_chain_nr = i + 1 + lbr_nr + 1;
-
- for (j = 0; j < mix_chain_nr; j++) {
- int err;
- branch = false;
- flags = NULL;
+ if (i == chain_nr)
+ return 0;
- if (callchain_param.order == ORDER_CALLEE) {
- if (j < i + 1)
- ip = chain->ips[j];
- else if (j > i + 1) {
- k = j - i - 2;
- ip = entries[k].from;
- branch = true;
- flags = &entries[k].flags;
- } else {
- ip = entries[0].to;
- branch = true;
- flags = &entries[0].flags;
- branch_from = entries[0].from;
- }
+ lbr_stack = sample->branch_stack;
+ entries = perf_sample__branch_entries(sample);
+ lbr_nr = lbr_stack->nr;
+ /*
+ * LBR callstack can only get user call chain.
+ * The mix_chain_nr is kernel call chain
+ * number plus LBR user call chain number.
+ * i is kernel call chain number,
+ * 1 is PERF_CONTEXT_USER,
+ * lbr_nr + 1 is the user call chain number.
+ * For details, please refer to the comments
+ * in callchain__printf
+ */
+ mix_chain_nr = i + 1 + lbr_nr + 1;
+
+ for (j = 0; j < mix_chain_nr; j++) {
+ int err;
+
+ branch = false;
+ flags = NULL;
+
+ if (callchain_param.order == ORDER_CALLEE) {
+ if (j < i + 1)
+ ip = chain->ips[j];
+ else if (j > i + 1) {
+ k = j - i - 2;
+ ip = entries[k].from;
+ branch = true;
+ flags = &entries[k].flags;
} else {
- if (j < lbr_nr) {
- k = lbr_nr - j - 1;
- ip = entries[k].from;
- branch = true;
- flags = &entries[k].flags;
- }
- else if (j > lbr_nr)
- ip = chain->ips[i + 1 - (j - lbr_nr)];
- else {
- ip = entries[0].to;
- branch = true;
- flags = &entries[0].flags;
- branch_from = entries[0].from;
- }
+ ip = entries[0].to;
+ branch = true;
+ flags = &entries[0].flags;
+ branch_from = entries[0].from;
+ }
+ } else {
+ if (j < lbr_nr) {
+ k = lbr_nr - j - 1;
+ ip = entries[k].from;
+ branch = true;
+ flags = &entries[k].flags;
+ } else if (j > lbr_nr)
+ ip = chain->ips[i + 1 - (j - lbr_nr)];
+ else {
+ ip = entries[0].to;
+ branch = true;
+ flags = &entries[0].flags;
+ branch_from = entries[0].from;
}
-
- err = add_callchain_ip(thread, cursor, parent,
- root_al, &cpumode, ip,
- branch, flags, NULL,
- branch_from);
- if (err)
- return (err < 0) ? err : 0;
}
- return 1;
- }
- return 0;
+ err = add_callchain_ip(thread, cursor, parent,
+ root_al, &cpumode, ip,
+ branch, flags, NULL,
+ branch_from);
+ if (err)
+ return (err < 0) ? err : 0;
+ }
+ return 1;
}
static int find_prev_cpumode(struct ip_callchain *chain, struct thread *thread,
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 06/17] perf machine: Refine the function for LBR call stack reconstruction
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
` (4 preceding siblings ...)
2020-03-13 18:33 ` [PATCH V3 05/17] perf machine: Remove the indent in resolve_lbr_callchain_sample kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-13 18:33 ` [PATCH V3 07/17] perf machine: Factor out lbr_callchain_add_kernel_ip() kan.liang
` (10 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
LBR only collect the user call stack. To reconstruct a call stack, both
kernel call stack and user call stack are required. The function
resolve_lbr_callchain_sample() mix the kernel call stack and user call
stack.
Now, with the help of HW idx, perf tool can reconstruct a more complete
call stack by adding some user call stack from previous sample. However,
current implementation is hard to be extended to support it.
Current code path for resolve_lbr_callchain_sample()
for (j = 0; j < mix_chain_nr; j++) {
if (ORDER_CALLEE) {
if (kernel callchain)
Fill callchain info
else if (LBR callchain)
Fill callchain info
} else {
if (LBR callchain)
Fill callchain info
else if (kernel callchain)
Fill callchain info
}
add_callchain_ip();
}
With the patch,
if (ORDER_CALLEE) {
for (j = 0; j < NUM of kernel callchain) {
Fill callchain info
add_callchain_ip();
}
for (; j < mix_chain_nr) {
Fill callchain info
add_callchain_ip();
}
} else {
for (; j < NUM of LBR callchain) {
Fill callchain info
add_callchain_ip();
}
for (j = 0; j < mix_chain_nr) {
Fill callchain info
add_callchain_ip();
}
}
No functional changes.
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/util/machine.c | 111 ++++++++++++++++++++++++++------------
1 file changed, 76 insertions(+), 35 deletions(-)
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 9021e5b6a2a9..cf2c97a6ef81 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -2183,6 +2183,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
bool branch;
struct branch_flags *flags;
int mix_chain_nr;
+ int err;
for (i = 0; i < chain_nr; i++) {
if (chain->ips[i] == PERF_CONTEXT_USER)
@@ -2208,50 +2209,90 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
*/
mix_chain_nr = i + 1 + lbr_nr + 1;
- for (j = 0; j < mix_chain_nr; j++) {
- int err;
-
- branch = false;
- flags = NULL;
-
- if (callchain_param.order == ORDER_CALLEE) {
- if (j < i + 1)
- ip = chain->ips[j];
- else if (j > i + 1) {
- k = j - i - 2;
- ip = entries[k].from;
- branch = true;
- flags = &entries[k].flags;
- } else {
- ip = entries[0].to;
- branch = true;
- flags = &entries[0].flags;
- branch_from = entries[0].from;
- }
- } else {
- if (j < lbr_nr) {
- k = lbr_nr - j - 1;
- ip = entries[k].from;
- branch = true;
- flags = &entries[k].flags;
- } else if (j > lbr_nr)
- ip = chain->ips[i + 1 - (j - lbr_nr)];
- else {
- ip = entries[0].to;
- branch = true;
- flags = &entries[0].flags;
- branch_from = entries[0].from;
- }
+ if (callchain_param.order == ORDER_CALLEE) {
+ /* Add kernel ip */
+ for (j = 0; j < i + 1; j++) {
+ ip = chain->ips[j];
+ branch = false;
+ flags = NULL;
+ err = add_callchain_ip(thread, cursor, parent,
+ root_al, &cpumode, ip,
+ branch, flags, NULL,
+ branch_from);
+ if (err)
+ goto error;
}
+ /* Add LBR ip from first entries.to */
+ ip = entries[0].to;
+ branch = true;
+ flags = &entries[0].flags;
+ branch_from = entries[0].from;
+ err = add_callchain_ip(thread, cursor, parent,
+ root_al, &cpumode, ip,
+ branch, flags, NULL,
+ branch_from);
+ if (err)
+ goto error;
+ /* Add LBR ip from entries.from one by one. */
+ for (j = i + 2; j < mix_chain_nr; j++) {
+ k = j - i - 2;
+ ip = entries[k].from;
+ branch = true;
+ flags = &entries[k].flags;
+
+ err = add_callchain_ip(thread, cursor, parent,
+ root_al, &cpumode, ip,
+ branch, flags, NULL,
+ branch_from);
+ if (err)
+ goto error;
+ }
+ } else {
+ /* Add LBR ip from entries.from one by one. */
+ for (j = 0; j < lbr_nr; j++) {
+ k = lbr_nr - j - 1;
+ ip = entries[k].from;
+ branch = true;
+ flags = &entries[k].flags;
+
+ err = add_callchain_ip(thread, cursor, parent,
+ root_al, &cpumode, ip,
+ branch, flags, NULL,
+ branch_from);
+ if (err)
+ goto error;
+ }
+
+ /* Add LBR ip from first entries.to */
+ ip = entries[0].to;
+ branch = true;
+ flags = &entries[0].flags;
+ branch_from = entries[0].from;
err = add_callchain_ip(thread, cursor, parent,
root_al, &cpumode, ip,
branch, flags, NULL,
branch_from);
if (err)
- return (err < 0) ? err : 0;
+ goto error;
+
+ /* Add kernel ip */
+ for (j = lbr_nr + 1; j < mix_chain_nr; j++) {
+ ip = chain->ips[i + 1 - (j - lbr_nr)];
+ branch = false;
+ flags = NULL;
+ err = add_callchain_ip(thread, cursor, parent,
+ root_al, &cpumode, ip,
+ branch, flags, NULL,
+ branch_from);
+ if (err)
+ goto error;
+ }
}
return 1;
+
+error:
+ return (err < 0) ? err : 0;
}
static int find_prev_cpumode(struct ip_callchain *chain, struct thread *thread,
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 07/17] perf machine: Factor out lbr_callchain_add_kernel_ip()
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
` (5 preceding siblings ...)
2020-03-13 18:33 ` [PATCH V3 06/17] perf machine: Refine the function for LBR call stack reconstruction kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-13 18:33 ` [PATCH V3 08/17] perf machine: Factor out lbr_callchain_add_lbr_ip() kan.liang
` (9 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
Both caller and callee needs to add kernel ip to callchain.
Factor out lbr_callchain_add_kernel_ip() to improve code readability.
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/util/machine.c | 67 ++++++++++++++++++++++++++-------------
1 file changed, 45 insertions(+), 22 deletions(-)
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index cf2c97a6ef81..ef26f28eacea 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -2159,6 +2159,40 @@ static int remove_loops(struct branch_entry *l, int nr,
return nr;
}
+static int lbr_callchain_add_kernel_ip(struct thread *thread,
+ struct callchain_cursor *cursor,
+ struct perf_sample *sample,
+ struct symbol **parent,
+ struct addr_location *root_al,
+ u64 branch_from,
+ bool callee, int end)
+{
+ struct ip_callchain *chain = sample->callchain;
+ u8 cpumode = PERF_RECORD_MISC_USER;
+ int err, i;
+
+ if (callee) {
+ for (i = 0; i < end + 1; i++) {
+ err = add_callchain_ip(thread, cursor, parent,
+ root_al, &cpumode, chain->ips[i],
+ false, NULL, NULL, branch_from);
+ if (err)
+ return err;
+ }
+ return 0;
+ }
+
+ for (i = end; i >= 0; i--) {
+ err = add_callchain_ip(thread, cursor, parent,
+ root_al, &cpumode, chain->ips[i],
+ false, NULL, NULL, branch_from);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
/*
* Recolve LBR callstack chain sample
* Return:
@@ -2211,17 +2245,12 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
if (callchain_param.order == ORDER_CALLEE) {
/* Add kernel ip */
- for (j = 0; j < i + 1; j++) {
- ip = chain->ips[j];
- branch = false;
- flags = NULL;
- err = add_callchain_ip(thread, cursor, parent,
- root_al, &cpumode, ip,
- branch, flags, NULL,
- branch_from);
- if (err)
- goto error;
- }
+ err = lbr_callchain_add_kernel_ip(thread, cursor, sample,
+ parent, root_al, branch_from,
+ true, i);
+ if (err)
+ goto error;
+
/* Add LBR ip from first entries.to */
ip = entries[0].to;
branch = true;
@@ -2277,17 +2306,11 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
goto error;
/* Add kernel ip */
- for (j = lbr_nr + 1; j < mix_chain_nr; j++) {
- ip = chain->ips[i + 1 - (j - lbr_nr)];
- branch = false;
- flags = NULL;
- err = add_callchain_ip(thread, cursor, parent,
- root_al, &cpumode, ip,
- branch, flags, NULL,
- branch_from);
- if (err)
- goto error;
- }
+ err = lbr_callchain_add_kernel_ip(thread, cursor, sample,
+ parent, root_al, branch_from,
+ false, i);
+ if (err)
+ goto error;
}
return 1;
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 08/17] perf machine: Factor out lbr_callchain_add_lbr_ip()
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
` (6 preceding siblings ...)
2020-03-13 18:33 ` [PATCH V3 07/17] perf machine: Factor out lbr_callchain_add_kernel_ip() kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-13 18:33 ` [PATCH V3 09/17] perf thread: Add a knob for LBR stitch approach kan.liang
` (8 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
Both caller and callee needs to add ip from LBR to callchain.
Factor out lbr_callchain_add_lbr_ip() to improve code readability.
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/util/machine.c | 143 +++++++++++++++++++-------------------
1 file changed, 73 insertions(+), 70 deletions(-)
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index ef26f28eacea..f1661dd3ca69 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -2193,6 +2193,74 @@ static int lbr_callchain_add_kernel_ip(struct thread *thread,
return 0;
}
+static int lbr_callchain_add_lbr_ip(struct thread *thread,
+ struct callchain_cursor *cursor,
+ struct perf_sample *sample,
+ struct symbol **parent,
+ struct addr_location *root_al,
+ u64 *branch_from,
+ bool callee)
+{
+ struct branch_stack *lbr_stack = sample->branch_stack;
+ struct branch_entry *entries = perf_sample__branch_entries(sample);
+ u8 cpumode = PERF_RECORD_MISC_USER;
+ int lbr_nr = lbr_stack->nr;
+ struct branch_flags *flags;
+ int err, i;
+ u64 ip;
+
+ if (callee) {
+ /* Add LBR ip from first entries.to */
+ ip = entries[0].to;
+ flags = &entries[0].flags;
+ *branch_from = entries[0].from;
+ err = add_callchain_ip(thread, cursor, parent,
+ root_al, &cpumode, ip,
+ true, flags, NULL,
+ *branch_from);
+ if (err)
+ return err;
+
+ /* Add LBR ip from entries.from one by one. */
+ for (i = 0; i < lbr_nr; i++) {
+ ip = entries[i].from;
+ flags = &entries[i].flags;
+ err = add_callchain_ip(thread, cursor, parent,
+ root_al, &cpumode, ip,
+ true, flags, NULL,
+ *branch_from);
+ if (err)
+ return err;
+ }
+ return 0;
+ }
+
+ /* Add LBR ip from entries.from one by one. */
+ for (i = lbr_nr - 1; i >= 0; i--) {
+ ip = entries[i].from;
+ flags = &entries[i].flags;
+ err = add_callchain_ip(thread, cursor, parent,
+ root_al, &cpumode, ip,
+ true, flags, NULL,
+ *branch_from);
+ if (err)
+ return err;
+ }
+
+ /* Add LBR ip from first entries.to */
+ ip = entries[0].to;
+ flags = &entries[0].flags;
+ *branch_from = entries[0].from;
+ err = add_callchain_ip(thread, cursor, parent,
+ root_al, &cpumode, ip,
+ true, flags, NULL,
+ *branch_from);
+ if (err)
+ return err;
+
+ return 0;
+}
+
/*
* Recolve LBR callstack chain sample
* Return:
@@ -2209,14 +2277,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
{
struct ip_callchain *chain = sample->callchain;
int chain_nr = min(max_stack, (int)chain->nr), i;
- u8 cpumode = PERF_RECORD_MISC_USER;
- u64 ip, branch_from = 0;
- struct branch_stack *lbr_stack;
- struct branch_entry *entries;
- int lbr_nr, j, k;
- bool branch;
- struct branch_flags *flags;
- int mix_chain_nr;
+ u64 branch_from = 0;
int err;
for (i = 0; i < chain_nr; i++) {
@@ -2228,21 +2289,6 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
if (i == chain_nr)
return 0;
- lbr_stack = sample->branch_stack;
- entries = perf_sample__branch_entries(sample);
- lbr_nr = lbr_stack->nr;
- /*
- * LBR callstack can only get user call chain.
- * The mix_chain_nr is kernel call chain
- * number plus LBR user call chain number.
- * i is kernel call chain number,
- * 1 is PERF_CONTEXT_USER,
- * lbr_nr + 1 is the user call chain number.
- * For details, please refer to the comments
- * in callchain__printf
- */
- mix_chain_nr = i + 1 + lbr_nr + 1;
-
if (callchain_param.order == ORDER_CALLEE) {
/* Add kernel ip */
err = lbr_callchain_add_kernel_ip(thread, cursor, sample,
@@ -2251,57 +2297,14 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
if (err)
goto error;
- /* Add LBR ip from first entries.to */
- ip = entries[0].to;
- branch = true;
- flags = &entries[0].flags;
- branch_from = entries[0].from;
- err = add_callchain_ip(thread, cursor, parent,
- root_al, &cpumode, ip,
- branch, flags, NULL,
- branch_from);
+ err = lbr_callchain_add_lbr_ip(thread, cursor, sample, parent,
+ root_al, &branch_from, true);
if (err)
goto error;
- /* Add LBR ip from entries.from one by one. */
- for (j = i + 2; j < mix_chain_nr; j++) {
- k = j - i - 2;
- ip = entries[k].from;
- branch = true;
- flags = &entries[k].flags;
-
- err = add_callchain_ip(thread, cursor, parent,
- root_al, &cpumode, ip,
- branch, flags, NULL,
- branch_from);
- if (err)
- goto error;
- }
} else {
- /* Add LBR ip from entries.from one by one. */
- for (j = 0; j < lbr_nr; j++) {
- k = lbr_nr - j - 1;
- ip = entries[k].from;
- branch = true;
- flags = &entries[k].flags;
-
- err = add_callchain_ip(thread, cursor, parent,
- root_al, &cpumode, ip,
- branch, flags, NULL,
- branch_from);
- if (err)
- goto error;
- }
-
- /* Add LBR ip from first entries.to */
- ip = entries[0].to;
- branch = true;
- flags = &entries[0].flags;
- branch_from = entries[0].from;
- err = add_callchain_ip(thread, cursor, parent,
- root_al, &cpumode, ip,
- branch, flags, NULL,
- branch_from);
+ err = lbr_callchain_add_lbr_ip(thread, cursor, sample, parent,
+ root_al, &branch_from, false);
if (err)
goto error;
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 09/17] perf thread: Add a knob for LBR stitch approach
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
` (7 preceding siblings ...)
2020-03-13 18:33 ` [PATCH V3 08/17] perf machine: Factor out lbr_callchain_add_lbr_ip() kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-13 18:33 ` [PATCH V3 10/17] perf tools: Save previous sample for LBR stitching approach kan.liang
` (7 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
The LBR stitch approach should be disabled by default. Because
- The stitching approach base on LBR call stack technology. The known
limitations of LBR call stack technology still apply to the approach,
e.g. Exception handing such as setjmp/longjmp will have calls/returns
not match.
- This approach is not full proof. There can be cases where it creates
incorrect call stacks from incorrect matches. There is no attempt to
validate any matches in another way.
The 'lbr_stitch_enable' is used to indicate whether enable LBR stitch
approach, which is disabled by default. The following patch will
introduce a new option for each tools to enable the LBR stitch
approach.
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/util/thread.c | 1 +
tools/perf/util/thread.h | 3 +++
2 files changed, 4 insertions(+)
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 28b719388028..1f080db23615 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -47,6 +47,7 @@ struct thread *thread__new(pid_t pid, pid_t tid)
thread->tid = tid;
thread->ppid = -1;
thread->cpu = -1;
+ thread->lbr_stitch_enable = false;
INIT_LIST_HEAD(&thread->namespaces_list);
INIT_LIST_HEAD(&thread->comm_list);
init_rwsem(&thread->namespaces_lock);
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 20b96b5d1f15..95294050cff2 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -46,6 +46,9 @@ struct thread {
struct srccode_state srccode_state;
bool filter;
int filter_entry_depth;
+
+ /* LBR call stack stitch */
+ bool lbr_stitch_enable;
};
struct machine;
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 10/17] perf tools: Save previous sample for LBR stitching approach
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
` (8 preceding siblings ...)
2020-03-13 18:33 ` [PATCH V3 09/17] perf thread: Add a knob for LBR stitch approach kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-18 12:14 ` Jiri Olsa
2020-03-13 18:33 ` [PATCH V3 11/17] perf tools: Save previous cursor nodes " kan.liang
` (6 subsequent siblings)
16 siblings, 1 reply; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
To retrieve the overwritten LBRs from previous sample for LBR stitching
approach, perf has to save the previous sample.
Only allocate the struct lbr_stitch once, when LBR stitching approach
is enabled and kernel supports hw_idx.
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/util/machine.c | 23 +++++++++++++++++++++++
tools/perf/util/thread.c | 1 +
tools/perf/util/thread.h | 16 ++++++++++++++++
3 files changed, 40 insertions(+)
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index f1661dd3ca69..d91e11bfc8ca 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -2261,6 +2261,21 @@ static int lbr_callchain_add_lbr_ip(struct thread *thread,
return 0;
}
+static bool alloc_lbr_stitch(struct thread *thread)
+{
+ if (thread->lbr_stitch)
+ return true;
+
+ thread->lbr_stitch = calloc(1, sizeof(struct lbr_stitch));
+ if (!thread->lbr_stitch)
+ goto err;
+
+err:
+ pr_warning("Failed to allocate space for stitched LBRs. Disable LBR stitch\n");
+ thread->lbr_stitch_enable = false;
+ return false;
+}
+
/*
* Recolve LBR callstack chain sample
* Return:
@@ -2277,6 +2292,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
{
struct ip_callchain *chain = sample->callchain;
int chain_nr = min(max_stack, (int)chain->nr), i;
+ struct lbr_stitch *lbr_stitch;
u64 branch_from = 0;
int err;
@@ -2289,6 +2305,13 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
if (i == chain_nr)
return 0;
+ if (thread->lbr_stitch_enable && !sample->no_hw_idx &&
+ alloc_lbr_stitch(thread)) {
+ lbr_stitch = thread->lbr_stitch;
+
+ memcpy(&lbr_stitch->prev_sample, sample, sizeof(*sample));
+ }
+
if (callchain_param.order == ORDER_CALLEE) {
/* Add kernel ip */
err = lbr_callchain_add_kernel_ip(thread, cursor, sample,
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 1f080db23615..8d0da260c84c 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -111,6 +111,7 @@ void thread__delete(struct thread *thread)
exit_rwsem(&thread->namespaces_lock);
exit_rwsem(&thread->comm_lock);
+ thread__free_stitch_list(thread);
free(thread);
}
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 95294050cff2..5fe239bd8f17 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -13,6 +13,7 @@
#include <strlist.h>
#include <intlist.h>
#include "rwsem.h"
+#include "event.h"
struct addr_location;
struct map;
@@ -20,6 +21,10 @@ struct perf_record_namespaces;
struct thread_stack;
struct unwind_libunwind_ops;
+struct lbr_stitch {
+ struct perf_sample prev_sample;
+};
+
struct thread {
union {
struct rb_node rb_node;
@@ -49,6 +54,7 @@ struct thread {
/* LBR call stack stitch */
bool lbr_stitch_enable;
+ struct lbr_stitch *lbr_stitch;
};
struct machine;
@@ -145,4 +151,14 @@ static inline bool thread__is_filtered(struct thread *thread)
return false;
}
+static inline void thread__free_stitch_list(struct thread *thread)
+{
+ struct lbr_stitch *lbr_stitch = thread->lbr_stitch;
+
+ if (!lbr_stitch)
+ return;
+
+ free(thread->lbr_stitch);
+}
+
#endif /* __PERF_THREAD_H */
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 11/17] perf tools: Save previous cursor nodes for LBR stitching approach
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
` (9 preceding siblings ...)
2020-03-13 18:33 ` [PATCH V3 10/17] perf tools: Save previous sample for LBR stitching approach kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-13 18:33 ` [PATCH V3 12/17] perf tools: Stitch LBR call stack kan.liang
` (5 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
The cursor nodes which generates from sample are eventually added into
callchain. To avoid generating cursor nodes from previous samples again,
the previous cursor nodes are also saved for LBR stitching approach.
Some option, e.g. hide-unresolved, may hide some LBRs.
Add a variable 'valid' in struct callchain_cursor_node to indicate this
case. The LBR stitching approach will only append the valid cursor nodes
from previous samples later.
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/util/callchain.h | 3 ++
tools/perf/util/machine.c | 77 +++++++++++++++++++++++++++++++++++--
tools/perf/util/thread.h | 3 ++
3 files changed, 79 insertions(+), 4 deletions(-)
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 706bb7bbe1e1..cb33cd42ff43 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -143,6 +143,9 @@ struct callchain_cursor_node {
u64 ip;
struct map_symbol ms;
const char *srcline;
+ /* Indicate valid cursor node for LBR stitch */
+ bool valid;
+
bool branch;
struct branch_flags branch_flags;
u64 branch_from;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index d91e11bfc8ca..f190265a1f26 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -2193,6 +2193,31 @@ static int lbr_callchain_add_kernel_ip(struct thread *thread,
return 0;
}
+static void save_lbr_cursor_node(struct thread *thread,
+ struct callchain_cursor *cursor,
+ int idx)
+{
+ struct lbr_stitch *lbr_stitch = thread->lbr_stitch;
+
+ if (!lbr_stitch)
+ return;
+
+ if (cursor->pos == cursor->nr) {
+ lbr_stitch->prev_lbr_cursor[idx].valid = false;
+ return;
+ }
+
+ if (!cursor->curr)
+ cursor->curr = cursor->first;
+ else
+ cursor->curr = cursor->curr->next;
+ memcpy(&lbr_stitch->prev_lbr_cursor[idx], cursor->curr,
+ sizeof(struct callchain_cursor_node));
+
+ lbr_stitch->prev_lbr_cursor[idx].valid = true;
+ cursor->pos++;
+}
+
static int lbr_callchain_add_lbr_ip(struct thread *thread,
struct callchain_cursor *cursor,
struct perf_sample *sample,
@@ -2209,6 +2234,21 @@ static int lbr_callchain_add_lbr_ip(struct thread *thread,
int err, i;
u64 ip;
+ /*
+ * The curr and pos are not used in writing session. They are cleared
+ * in callchain_cursor_commit() when the writing session is closed.
+ * Using curr and pos to track the current cursor node.
+ */
+ if (thread->lbr_stitch) {
+ cursor->curr = NULL;
+ cursor->pos = cursor->nr;
+ if (cursor->nr) {
+ cursor->curr = cursor->first;
+ for (i = 0; i < (int)(cursor->nr - 1); i++)
+ cursor->curr = cursor->curr->next;
+ }
+ }
+
if (callee) {
/* Add LBR ip from first entries.to */
ip = entries[0].to;
@@ -2221,6 +2261,20 @@ static int lbr_callchain_add_lbr_ip(struct thread *thread,
if (err)
return err;
+ /*
+ * The number of cursor node increases.
+ * Move the current cursor node.
+ * But does not need to save current cursor node for entry 0.
+ * It's impossible to stitch the whole LBRs of previous sample.
+ */
+ if (thread->lbr_stitch && (cursor->pos != cursor->nr)) {
+ if (!cursor->curr)
+ cursor->curr = cursor->first;
+ else
+ cursor->curr = cursor->curr->next;
+ cursor->pos++;
+ }
+
/* Add LBR ip from entries.from one by one. */
for (i = 0; i < lbr_nr; i++) {
ip = entries[i].from;
@@ -2231,6 +2285,7 @@ static int lbr_callchain_add_lbr_ip(struct thread *thread,
*branch_from);
if (err)
return err;
+ save_lbr_cursor_node(thread, cursor, i);
}
return 0;
}
@@ -2245,6 +2300,7 @@ static int lbr_callchain_add_lbr_ip(struct thread *thread,
*branch_from);
if (err)
return err;
+ save_lbr_cursor_node(thread, cursor, i);
}
/* Add LBR ip from first entries.to */
@@ -2261,7 +2317,7 @@ static int lbr_callchain_add_lbr_ip(struct thread *thread,
return 0;
}
-static bool alloc_lbr_stitch(struct thread *thread)
+static bool alloc_lbr_stitch(struct thread *thread, unsigned int max_lbr)
{
if (thread->lbr_stitch)
return true;
@@ -2270,6 +2326,15 @@ static bool alloc_lbr_stitch(struct thread *thread)
if (!thread->lbr_stitch)
goto err;
+ thread->lbr_stitch->prev_lbr_cursor = calloc(max_lbr + 1, sizeof(struct callchain_cursor_node));
+ if (!thread->lbr_stitch->prev_lbr_cursor)
+ goto free_lbr_stitch;
+
+ return true;
+
+free_lbr_stitch:
+ free(thread->lbr_stitch);
+ thread->lbr_stitch = NULL;
err:
pr_warning("Failed to allocate space for stitched LBRs. Disable LBR stitch\n");
thread->lbr_stitch_enable = false;
@@ -2288,7 +2353,8 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
struct perf_sample *sample,
struct symbol **parent,
struct addr_location *root_al,
- int max_stack)
+ int max_stack,
+ unsigned int max_lbr)
{
struct ip_callchain *chain = sample->callchain;
int chain_nr = min(max_stack, (int)chain->nr), i;
@@ -2306,7 +2372,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
return 0;
if (thread->lbr_stitch_enable && !sample->no_hw_idx &&
- alloc_lbr_stitch(thread)) {
+ (max_lbr > 0) && alloc_lbr_stitch(thread, max_lbr)) {
lbr_stitch = thread->lbr_stitch;
memcpy(&lbr_stitch->prev_sample, sample, sizeof(*sample));
@@ -2386,8 +2452,11 @@ static int thread__resolve_callchain_sample(struct thread *thread,
chain_nr = chain->nr;
if (perf_evsel__has_branch_callstack(evsel)) {
+ struct perf_env *env = perf_evsel__env(evsel);
+
err = resolve_lbr_callchain_sample(thread, cursor, sample, parent,
- root_al, max_stack);
+ root_al, max_stack,
+ !env ? 0 : env->max_branches);
if (err)
return (err < 0) ? err : 0;
}
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 5fe239bd8f17..477c669cdbfa 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -14,6 +14,7 @@
#include <intlist.h>
#include "rwsem.h"
#include "event.h"
+#include "callchain.h"
struct addr_location;
struct map;
@@ -23,6 +24,7 @@ struct unwind_libunwind_ops;
struct lbr_stitch {
struct perf_sample prev_sample;
+ struct callchain_cursor_node *prev_lbr_cursor;
};
struct thread {
@@ -158,6 +160,7 @@ static inline void thread__free_stitch_list(struct thread *thread)
if (!lbr_stitch)
return;
+ free(lbr_stitch->prev_lbr_cursor);
free(thread->lbr_stitch);
}
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 12/17] perf tools: Stitch LBR call stack
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
` (10 preceding siblings ...)
2020-03-13 18:33 ` [PATCH V3 11/17] perf tools: Save previous cursor nodes " kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-13 18:33 ` [PATCH V3 13/17] perf report: Add option to enable the LBR stitching approach kan.liang
` (4 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
In LBR call stack mode, the depth of reconstructed LBR call stack limits
to the number of LBR registers.
For example, on skylake, the depth of reconstructed LBR call stack is
always <= 32.
# To display the perf.data header info, please use
# --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 6K of event 'cycles'
# Event count (approx.): 6487119731
#
# Children Self Command Shared Object Symbol
# ........ ........ ............... ..................
# ................................
99.97% 99.97% tchain_edit tchain_edit [.] f43
|
--99.64%--f11
f12
f13
f14
f15
f16
f17
f18
f19
f20
f21
f22
f23
f24
f25
f26
f27
f28
f29
f30
f31
f32
f33
f34
f35
f36
f37
f38
f39
f40
f41
f42
f43
For a call stack which is deeper than LBR limit, HW will overwrite the
LBR register with oldest branch. Only partial call stacks can be
reconstructed.
However, the overwritten LBRs may still be retrieved from previous
sample. At that moment, HW hasn't overwritten the LBR registers yet.
Perf tools can stitch those overwritten LBRs on current call stacks to
get a more complete call stack.
To determine if LBRs can be stitched, perf tools need to compare current
sample with previous sample.
- They should have identical LBR records (Same from, to and flags
values, and the same physical index of LBR registers).
- The searching starts from the base-of-stack of current sample.
Once perf determines to stitch the previous LBRs, the corresponding LBR
cursor nodes will be copied to 'lists'.
The 'lists' is to track the LBR cursor nodes which are going to be
stitched.
When the stitching is over, the nodes will not be freed immediately.
They will be moved to 'free_lists'. Next stitching may reuse the space.
Both 'lists' and 'free_lists' will be freed when all samples are
processed.
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/util/branch.h | 19 +++--
tools/perf/util/callchain.h | 5 ++
tools/perf/util/machine.c | 139 +++++++++++++++++++++++++++++++++++-
tools/perf/util/thread.h | 13 ++++
4 files changed, 168 insertions(+), 8 deletions(-)
diff --git a/tools/perf/util/branch.h b/tools/perf/util/branch.h
index 154a05cd03af..4d3f02fa223d 100644
--- a/tools/perf/util/branch.h
+++ b/tools/perf/util/branch.h
@@ -15,13 +15,18 @@
#include "event.h"
struct branch_flags {
- u64 mispred:1;
- u64 predicted:1;
- u64 in_tx:1;
- u64 abort:1;
- u64 cycles:16;
- u64 type:4;
- u64 reserved:40;
+ union {
+ u64 value;
+ struct {
+ u64 mispred:1;
+ u64 predicted:1;
+ u64 in_tx:1;
+ u64 abort:1;
+ u64 cycles:16;
+ u64 type:4;
+ u64 reserved:40;
+ };
+ };
};
struct branch_info {
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index cb33cd42ff43..8f668ee29f25 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -154,6 +154,11 @@ struct callchain_cursor_node {
struct callchain_cursor_node *next;
};
+struct stitch_list {
+ struct list_head node;
+ struct callchain_cursor_node cursor;
+};
+
struct callchain_cursor {
u64 nr;
struct callchain_cursor_node *first;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index f190265a1f26..4d4df1f3c066 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -2317,6 +2317,119 @@ static int lbr_callchain_add_lbr_ip(struct thread *thread,
return 0;
}
+static int lbr_callchain_add_stitched_lbr_ip(struct thread *thread,
+ struct callchain_cursor *cursor)
+{
+ struct lbr_stitch *lbr_stitch = thread->lbr_stitch;
+ struct callchain_cursor_node *cnode;
+ struct stitch_list *stitch_node;
+ int err;
+
+ list_for_each_entry(stitch_node, &lbr_stitch->lists, node) {
+ cnode = &stitch_node->cursor;
+
+ err = callchain_cursor_append(cursor, cnode->ip,
+ &cnode->ms,
+ cnode->branch,
+ &cnode->branch_flags,
+ cnode->nr_loop_iter,
+ cnode->iter_cycles,
+ cnode->branch_from,
+ cnode->srcline);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static struct stitch_list *get_stitch_node(struct thread *thread)
+{
+ struct lbr_stitch *lbr_stitch = thread->lbr_stitch;
+ struct stitch_list *stitch_node;
+
+ if (!list_empty(&lbr_stitch->free_lists)) {
+ stitch_node = list_first_entry(&lbr_stitch->free_lists,
+ struct stitch_list, node);
+ list_del(&stitch_node->node);
+
+ return stitch_node;
+ }
+
+ return malloc(sizeof(struct stitch_list));
+}
+
+static bool has_stitched_lbr(struct thread *thread,
+ struct perf_sample *cur,
+ struct perf_sample *prev,
+ unsigned int max_lbr,
+ bool callee)
+{
+ struct branch_stack *cur_stack = cur->branch_stack;
+ struct branch_entry *cur_entries = perf_sample__branch_entries(cur);
+ struct branch_stack *prev_stack = prev->branch_stack;
+ struct branch_entry *prev_entries = perf_sample__branch_entries(prev);
+ struct lbr_stitch *lbr_stitch = thread->lbr_stitch;
+ int i, j, nr_identical_branches = 0;
+ struct stitch_list *stitch_node;
+ u64 cur_base, distance;
+
+ if (!cur_stack || !prev_stack)
+ return false;
+
+ /* Find the physical index of the base-of-stack for current sample. */
+ cur_base = max_lbr - cur_stack->nr + cur_stack->hw_idx + 1;
+
+ distance = (prev_stack->hw_idx > cur_base) ? (prev_stack->hw_idx - cur_base) :
+ (max_lbr + prev_stack->hw_idx - cur_base);
+ /* Previous sample has shorter stack. Nothing can be stitched. */
+ if (distance + 1 > prev_stack->nr)
+ return false;
+
+ /*
+ * Check if there are identical LBRs between two samples.
+ * Identicall LBRs must have same from, to and flags values. Also,
+ * they have to be saved in the same LBR registers (same physical
+ * index).
+ *
+ * Starts from the base-of-stack of current sample.
+ */
+ for (i = distance, j = cur_stack->nr - 1; (i >= 0) && (j >= 0); i--, j--) {
+ if ((prev_entries[i].from != cur_entries[j].from) ||
+ (prev_entries[i].to != cur_entries[j].to) ||
+ (prev_entries[i].flags.value != cur_entries[j].flags.value))
+ break;
+ nr_identical_branches++;
+ }
+
+ if (!nr_identical_branches)
+ return false;
+
+ /*
+ * Save the LBRs between the base-of-stack of previous sample
+ * and the base-of-stack of current sample into lbr_stitch->lists.
+ * These LBRs will be stitched later.
+ */
+ for (i = prev_stack->nr - 1; i > (int)distance; i--) {
+
+ if (!lbr_stitch->prev_lbr_cursor[i].valid)
+ continue;
+
+ stitch_node = get_stitch_node(thread);
+ if (!stitch_node)
+ return false;
+
+ memcpy(&stitch_node->cursor, &lbr_stitch->prev_lbr_cursor[i],
+ sizeof(struct callchain_cursor_node));
+
+ if (callee)
+ list_add(&stitch_node->node, &lbr_stitch->lists);
+ else
+ list_add_tail(&stitch_node->node, &lbr_stitch->lists);
+ }
+
+ return true;
+}
+
static bool alloc_lbr_stitch(struct thread *thread, unsigned int max_lbr)
{
if (thread->lbr_stitch)
@@ -2330,6 +2443,9 @@ static bool alloc_lbr_stitch(struct thread *thread, unsigned int max_lbr)
if (!thread->lbr_stitch->prev_lbr_cursor)
goto free_lbr_stitch;
+ INIT_LIST_HEAD(&thread->lbr_stitch->lists);
+ INIT_LIST_HEAD(&thread->lbr_stitch->free_lists);
+
return true;
free_lbr_stitch:
@@ -2356,9 +2472,11 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
int max_stack,
unsigned int max_lbr)
{
+ bool callee = (callchain_param.order == ORDER_CALLEE);
struct ip_callchain *chain = sample->callchain;
int chain_nr = min(max_stack, (int)chain->nr), i;
struct lbr_stitch *lbr_stitch;
+ bool stitched_lbr = false;
u64 branch_from = 0;
int err;
@@ -2375,10 +2493,18 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
(max_lbr > 0) && alloc_lbr_stitch(thread, max_lbr)) {
lbr_stitch = thread->lbr_stitch;
+ stitched_lbr = has_stitched_lbr(thread, sample,
+ &lbr_stitch->prev_sample,
+ max_lbr, callee);
+
+ if (!stitched_lbr && !list_empty(&lbr_stitch->lists)) {
+ list_replace_init(&lbr_stitch->lists,
+ &lbr_stitch->free_lists);
+ }
memcpy(&lbr_stitch->prev_sample, sample, sizeof(*sample));
}
- if (callchain_param.order == ORDER_CALLEE) {
+ if (callee) {
/* Add kernel ip */
err = lbr_callchain_add_kernel_ip(thread, cursor, sample,
parent, root_al, branch_from,
@@ -2391,7 +2517,18 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
if (err)
goto error;
+ if (stitched_lbr) {
+ err = lbr_callchain_add_stitched_lbr_ip(thread, cursor);
+ if (err)
+ goto error;
+ }
+
} else {
+ if (stitched_lbr) {
+ err = lbr_callchain_add_stitched_lbr_ip(thread, cursor);
+ if (err)
+ goto error;
+ }
err = lbr_callchain_add_lbr_ip(thread, cursor, sample, parent,
root_al, &branch_from, false);
if (err)
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 477c669cdbfa..12dd7288a14d 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -23,6 +23,8 @@ struct thread_stack;
struct unwind_libunwind_ops;
struct lbr_stitch {
+ struct list_head lists;
+ struct list_head free_lists;
struct perf_sample prev_sample;
struct callchain_cursor_node *prev_lbr_cursor;
};
@@ -156,10 +158,21 @@ static inline bool thread__is_filtered(struct thread *thread)
static inline void thread__free_stitch_list(struct thread *thread)
{
struct lbr_stitch *lbr_stitch = thread->lbr_stitch;
+ struct stitch_list *pos, *tmp;
if (!lbr_stitch)
return;
+ list_for_each_entry_safe(pos, tmp, &lbr_stitch->lists, node) {
+ list_del_init(&pos->node);
+ free(pos);
+ }
+
+ list_for_each_entry_safe(pos, tmp, &lbr_stitch->free_lists, node) {
+ list_del_init(&pos->node);
+ free(pos);
+ }
+
free(lbr_stitch->prev_lbr_cursor);
free(thread->lbr_stitch);
}
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 13/17] perf report: Add option to enable the LBR stitching approach
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
` (11 preceding siblings ...)
2020-03-13 18:33 ` [PATCH V3 12/17] perf tools: Stitch LBR call stack kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-13 18:33 ` [PATCH V3 14/17] perf script: " kan.liang
` (3 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
With the LBR stitching approach, the reconstructed LBR call stack
can break the HW limitation. However, it may reconstruct invalid call
stacks in some cases, e.g. exception handing such as setjmp/longjmp.
Also, it may impact the processing time especially when the number of
samples with stitched LBRs are huge.
Add an option to enable the approach.
# To display the perf.data header info, please use
# --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 6K of event 'cycles'
# Event count (approx.): 6492797701
#
# Children Self Command Shared Object Symbol
# ........ ........ ............... ..................
# .................................
#
99.99% 99.99% tchain_edit tchain_edit [.] f43
|
---main
f1
f2
f3
f4
f5
f6
f7
f8
f9
f10
f11
f12
f13
f14
f15
f16
f17
f18
f19
f20
f21
f22
f23
f24
f25
f26
f27
f28
f29
f30
f31
|
--99.65%--f32
f33
f34
f35
f36
f37
f38
f39
f40
f41
f42
f43
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/Documentation/perf-report.txt | 11 +++++++++++
tools/perf/builtin-report.c | 6 ++++++
2 files changed, 17 insertions(+)
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index db61f16ffa56..5e4155d2511c 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -482,6 +482,17 @@ include::itrace.txt[]
This option extends the perf report to show reference callgraphs,
which collected by reference event, in no callgraph event.
+--stitch-lbr::
+ Show callgraph with stitched LBRs, which may have more complete
+ callgraph. The perf.data file must have been obtained using
+ perf record --call-graph lbr.
+ Disabled by default. In common cases with call stack overflows,
+ it can recreate better call stacks than the default lbr call stack
+ output. But this approach is not full proof. There can be cases
+ where it creates incorrect call stacks from incorrect matches.
+ The known limitations include exception handing such as
+ setjmp/longjmp will have calls/returns not match.
+
--socket-filter::
Only report the samples on the processor socket that match with this filter
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 72a12b69f120..e41cedb9256c 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -84,6 +84,7 @@ struct report {
bool header_only;
bool nonany_branch_mode;
bool group_set;
+ bool stitch_lbr;
int max_stack;
struct perf_read_values show_threads_values;
struct annotation_options annotation_opts;
@@ -267,6 +268,9 @@ static int process_sample_event(struct perf_tool *tool,
return -1;
}
+ if (rep->stitch_lbr)
+ al.thread->lbr_stitch_enable = true;
+
if (symbol_conf.hide_unresolved && al.sym == NULL)
goto out_put;
@@ -1241,6 +1245,8 @@ int cmd_report(int argc, const char **argv)
"Show full source file name path for source lines"),
OPT_BOOLEAN(0, "show-ref-call-graph", &symbol_conf.show_ref_callgraph,
"Show callgraph from reference event"),
+ OPT_BOOLEAN(0, "stitch-lbr", &report.stitch_lbr,
+ "Enable LBR callgraph stitching approach"),
OPT_INTEGER(0, "socket-filter", &report.socket_filter,
"only show processor socket that match with this filter"),
OPT_BOOLEAN(0, "raw-trace", &symbol_conf.raw_trace,
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 14/17] perf script: Add option to enable the LBR stitching approach
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
` (12 preceding siblings ...)
2020-03-13 18:33 ` [PATCH V3 13/17] perf report: Add option to enable the LBR stitching approach kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-13 18:33 ` [PATCH V3 15/17] perf top: " kan.liang
` (2 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
With the LBR stitching approach, the reconstructed LBR call stack
can break the HW limitation. However, it may reconstruct invalid call
stacks in some cases, e.g. exception handing such as setjmp/longjmp.
Also, it may impact the processing time especially when the number of
samples with stitched LBRs are huge.
Add an option to enable the approach.
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/Documentation/perf-script.txt | 11 +++++++++++
tools/perf/builtin-script.c | 6 ++++++
2 files changed, 17 insertions(+)
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
index 2599b057e47b..472f20f1e479 100644
--- a/tools/perf/Documentation/perf-script.txt
+++ b/tools/perf/Documentation/perf-script.txt
@@ -426,6 +426,17 @@ include::itrace.txt[]
--show-on-off-events::
Show the --switch-on/off events too.
+--stitch-lbr::
+ Show callgraph with stitched LBRs, which may have more complete
+ callgraph. The perf.data file must have been obtained using
+ perf record --call-graph lbr.
+ Disabled by default. In common cases with call stack overflows,
+ it can recreate better call stacks than the default lbr call stack
+ output. But this approach is not full proof. There can be cases
+ where it creates incorrect call stacks from incorrect matches.
+ The known limitations include exception handing such as
+ setjmp/longjmp will have calls/returns not match.
+
SEE ALSO
--------
linkperf:perf-record[1], linkperf:perf-script-perl[1],
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 656b347f6dd8..f8f50bf95e40 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -1687,6 +1687,7 @@ struct perf_script {
bool show_bpf_events;
bool allocated;
bool per_event_dump;
+ bool stitch_lbr;
struct evswitch evswitch;
struct perf_cpu_map *cpus;
struct perf_thread_map *threads;
@@ -1913,6 +1914,9 @@ static void process_event(struct perf_script *script,
if (PRINT_FIELD(IP)) {
struct callchain_cursor *cursor = NULL;
+ if (script->stitch_lbr)
+ al->thread->lbr_stitch_enable = true;
+
if (symbol_conf.use_callchain && sample->callchain &&
thread__resolve_callchain(al->thread, &callchain_cursor, evsel,
sample, NULL, NULL, scripting_max_stack) == 0)
@@ -3602,6 +3606,8 @@ int cmd_script(int argc, const char **argv)
"file", "file saving guest os /proc/kallsyms"),
OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules,
"file", "file saving guest os /proc/modules"),
+ OPT_BOOLEAN('\0', "stitch-lbr", &script.stitch_lbr,
+ "Enable LBR callgraph stitching approach"),
OPTS_EVSWITCH(&script.evswitch),
OPT_END()
};
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 15/17] perf top: Add option to enable the LBR stitching approach
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
` (13 preceding siblings ...)
2020-03-13 18:33 ` [PATCH V3 14/17] perf script: " kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-18 12:14 ` Jiri Olsa
2020-03-13 18:33 ` [PATCH V3 16/17] perf c2c: " kan.liang
2020-03-13 18:33 ` [PATCH V3 17/17] perf hist: Add fast path for duplicate entries check kan.liang
16 siblings, 1 reply; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
With the LBR stitching approach, the reconstructed LBR call stack
can break the HW limitation. However, it may reconstruct invalid call
stacks in some cases, e.g. exception handing such as setjmp/longjmp.
Also, it may impact the processing time especially when the number of
samples with stitched LBRs are huge.
Add an option to enable the approach.
The option must be used with --call-graph lbr.
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/Documentation/perf-top.txt | 9 +++++++++
tools/perf/builtin-top.c | 11 +++++++++++
tools/perf/util/top.h | 1 +
3 files changed, 21 insertions(+)
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index 324b6b53c86b..0648d96981fe 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -310,6 +310,15 @@ Default is to monitor all CPUS.
go straight to the histogram browser, just like 'perf top' with no events
explicitely specified does.
+--stitch-lbr::
+ Show callgraph with stitched LBRs, which may have more complete
+ callgraph. The option must be used with --call-graph lbr recording.
+ Disabled by default. In common cases with call stack overflows,
+ it can recreate better call stacks than the default lbr call stack
+ output. But this approach is not full proof. There can be cases
+ where it creates incorrect call stacks from incorrect matches.
+ The known limitations include exception handing such as
+ setjmp/longjmp will have calls/returns not match.
INTERACTIVE PROMPTING KEYS
--------------------------
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index f6dd1a63f159..aae8282b1fac 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -33,6 +33,7 @@
#include "util/map.h"
#include "util/mmap.h"
#include "util/session.h"
+#include "util/thread.h"
#include "util/symbol.h"
#include "util/synthetic-events.h"
#include "util/top.h"
@@ -766,6 +767,9 @@ static void perf_event__process_sample(struct perf_tool *tool,
if (machine__resolve(machine, &al, sample) < 0)
return;
+ if (top->stitch_lbr)
+ al.thread->lbr_stitch_enable = true;
+
if (!machine->kptr_restrict_warned &&
symbol_conf.kptr_restrict &&
al.cpumode == PERF_RECORD_MISC_KERNEL) {
@@ -1543,6 +1547,8 @@ int cmd_top(int argc, const char **argv)
"number of thread to run event synthesize"),
OPT_BOOLEAN(0, "namespaces", &opts->record_namespaces,
"Record namespaces events"),
+ OPT_BOOLEAN(0, "stitch-lbr", &top.stitch_lbr,
+ "Enable LBR callgraph stitching approach"),
OPTS_EVSWITCH(&top.evswitch),
OPT_END()
};
@@ -1612,6 +1618,11 @@ int cmd_top(int argc, const char **argv)
}
}
+ if (top.stitch_lbr && !(callchain_param.record_mode == CALLCHAIN_LBR)) {
+ pr_err("Error: --stitch-lbr must be used with --call-graph lbr\n");
+ goto out_delete_evlist;
+ }
+
if (opts->branch_stack && callchain_param.enabled)
symbol_conf.show_branchflag_count = true;
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h
index f117d4f4821e..45dc84ddff37 100644
--- a/tools/perf/util/top.h
+++ b/tools/perf/util/top.h
@@ -36,6 +36,7 @@ struct perf_top {
bool use_tui, use_stdio;
bool vmlinux_warned;
bool dump_symtab;
+ bool stitch_lbr;
struct hist_entry *sym_filter_entry;
struct evsel *sym_evsel;
struct perf_session *session;
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 16/17] perf c2c: Add option to enable the LBR stitching approach
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
` (14 preceding siblings ...)
2020-03-13 18:33 ` [PATCH V3 15/17] perf top: " kan.liang
@ 2020-03-13 18:33 ` kan.liang
2020-03-13 18:33 ` [PATCH V3 17/17] perf hist: Add fast path for duplicate entries check kan.liang
16 siblings, 0 replies; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
With the LBR stitching approach, the reconstructed LBR call stack
can break the HW limitation. However, it may reconstruct invalid call
stacks in some cases, e.g. exception handing such as setjmp/longjmp.
Also, it may impact the processing time especially when the number of
samples with stitched LBRs are huge.
Add an option to enable the approach.
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/Documentation/perf-c2c.txt | 11 +++++++++++
tools/perf/builtin-c2c.c | 6 ++++++
2 files changed, 17 insertions(+)
diff --git a/tools/perf/Documentation/perf-c2c.txt b/tools/perf/Documentation/perf-c2c.txt
index e6150f21267d..2133eb320cb0 100644
--- a/tools/perf/Documentation/perf-c2c.txt
+++ b/tools/perf/Documentation/perf-c2c.txt
@@ -111,6 +111,17 @@ REPORT OPTIONS
--display::
Switch to HITM type (rmt, lcl) to display and sort on. Total HITMs as default.
+--stitch-lbr::
+ Show callgraph with stitched LBRs, which may have more complete
+ callgraph. The perf.data file must have been obtained using
+ perf c2c record --call-graph lbr.
+ Disabled by default. In common cases with call stack overflows,
+ it can recreate better call stacks than the default lbr call stack
+ output. But this approach is not full proof. There can be cases
+ where it creates incorrect call stacks from incorrect matches.
+ The known limitations include exception handing such as
+ setjmp/longjmp will have calls/returns not match.
+
C2C RECORD
----------
The perf c2c record command setup options related to HITM cacheline analysis
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 246ac0b4d54f..c798763f62db 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -95,6 +95,7 @@ struct perf_c2c {
bool use_stdio;
bool stats_only;
bool symbol_full;
+ bool stitch_lbr;
/* HITM shared clines stats */
struct c2c_stats hitm_stats;
@@ -273,6 +274,9 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
return -1;
}
+ if (c2c.stitch_lbr)
+ al.thread->lbr_stitch_enable = true;
+
ret = sample__resolve_callchain(sample, &callchain_cursor, NULL,
evsel, &al, sysctl_perf_event_max_stack);
if (ret)
@@ -2752,6 +2756,8 @@ static int perf_c2c__report(int argc, const char **argv)
OPT_STRING('c', "coalesce", &coalesce, "coalesce fields",
"coalesce fields: pid,tid,iaddr,dso"),
OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
+ OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr,
+ "Enable LBR callgraph stitching approach"),
OPT_PARENT(c2c_options),
OPT_END()
};
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V3 17/17] perf hist: Add fast path for duplicate entries check
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
` (15 preceding siblings ...)
2020-03-13 18:33 ` [PATCH V3 16/17] perf c2c: " kan.liang
@ 2020-03-13 18:33 ` kan.liang
16 siblings, 0 replies; 26+ messages in thread
From: kan.liang @ 2020-03-13 18:33 UTC (permalink / raw)
To: acme, jolsa, peterz, mingo, linux-kernel
Cc: namhyung, adrian.hunter, mathieu.poirier, ravi.bangoria,
alexey.budankov, vitaly.slobodskoy, pavel.gerasimov, mpe,
eranian, ak, Kan Liang
From: Kan Liang <kan.liang@linux.intel.com>
Perf checks the duplicate entries in a callchain before adding an entry.
However the check is very slow especially with deeper call stack.
Almost ~50% elapsed time of perf report is spent on the check when the
call stack is always depth of 32.
The hist_entry__cmp() is used to compare the new entry with the old
entries. It will go through all the available sorts in the sort_list,
and call the specific cmp of each sort, which is very slow.
Actually, for most cases, there are no duplicate entries in callchain.
The symbols are usually different. It's much faster to do a quick check
for symbols first. Only do the full cmp when the symbols are exactly the
same.
The quick check is only to check symbols, not dso. Export
_sort__sym_cmp.
$perf record --call-graph lbr ./tchain_edit_64
Without the patch
$time perf report --stdio
real 0m21.142s
user 0m21.110s
sys 0m0.033s
With the patch
$time perf report --stdio
real 0m10.977s
user 0m10.948s
sys 0m0.027s
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Cc: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/hist.c | 23 +++++++++++++++++++++++
tools/perf/util/sort.c | 2 +-
tools/perf/util/sort.h | 2 ++
3 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index e74a5acf66d9..311d6d119f3c 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -1057,6 +1057,20 @@ iter_next_cumulative_entry(struct hist_entry_iter *iter,
return fill_callchain_info(al, node, iter->hide_unresolved);
}
+static bool
+hist_entry__fast__sym_diff(struct hist_entry *left,
+ struct hist_entry *right)
+{
+ struct symbol *sym_l = left->ms.sym;
+ struct symbol *sym_r = right->ms.sym;
+
+ if (!sym_l && !sym_r)
+ return left->ip != right->ip;
+
+ return !!_sort__sym_cmp(sym_l, sym_r);
+}
+
+
static int
iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
struct addr_location *al)
@@ -1083,6 +1097,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
};
int i;
struct callchain_cursor cursor;
+ bool fast = hists__has(he_tmp.hists, sym);
callchain_cursor_snapshot(&cursor, &callchain_cursor);
@@ -1093,6 +1108,14 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
* It's possible that it has cycles or recursive calls.
*/
for (i = 0; i < iter->curr; i++) {
+ /*
+ * For most cases, there are no duplicate entries in callchain.
+ * The symbols are usually different. Do a quick check for
+ * symbols first.
+ */
+ if (fast && hist_entry__fast__sym_diff(he_cache[i], &he_tmp))
+ continue;
+
if (hist_entry__cmp(he_cache[i], &he_tmp) == 0) {
/* to avoid calling callback function */
iter->he = NULL;
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index ab0cfd790ad0..33e0fa1bc203 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -234,7 +234,7 @@ static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
return (int64_t)(right_ip - left_ip);
}
-static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
+int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
{
if (!sym_l || !sym_r)
return cmp_null(sym_l, sym_r);
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 6c862d62d052..c3c3c68cbfdd 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -309,5 +309,7 @@ int64_t
sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right);
int64_t
sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right);
+int64_t
+_sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r);
char *hist_entry__srcline(struct hist_entry *he);
#endif /* __PERF_SORT_H */
--
2.17.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH V3 15/17] perf top: Add option to enable the LBR stitching approach
2020-03-13 18:33 ` [PATCH V3 15/17] perf top: " kan.liang
@ 2020-03-18 12:14 ` Jiri Olsa
2020-03-18 14:19 ` Liang, Kan
0 siblings, 1 reply; 26+ messages in thread
From: Jiri Olsa @ 2020-03-18 12:14 UTC (permalink / raw)
To: kan.liang
Cc: acme, peterz, mingo, linux-kernel, namhyung, adrian.hunter,
mathieu.poirier, ravi.bangoria, alexey.budankov,
vitaly.slobodskoy, pavel.gerasimov, mpe, eranian, ak
On Fri, Mar 13, 2020 at 11:33:17AM -0700, kan.liang@linux.intel.com wrote:
SNIP
> #include "util/top.h"
> @@ -766,6 +767,9 @@ static void perf_event__process_sample(struct perf_tool *tool,
> if (machine__resolve(machine, &al, sample) < 0)
> return;
>
> + if (top->stitch_lbr)
> + al.thread->lbr_stitch_enable = true;
> +
> if (!machine->kptr_restrict_warned &&
> symbol_conf.kptr_restrict &&
> al.cpumode == PERF_RECORD_MISC_KERNEL) {
> @@ -1543,6 +1547,8 @@ int cmd_top(int argc, const char **argv)
> "number of thread to run event synthesize"),
> OPT_BOOLEAN(0, "namespaces", &opts->record_namespaces,
> "Record namespaces events"),
> + OPT_BOOLEAN(0, "stitch-lbr", &top.stitch_lbr,
> + "Enable LBR callgraph stitching approach"),
> OPTS_EVSWITCH(&top.evswitch),
> OPT_END()
> };
> @@ -1612,6 +1618,11 @@ int cmd_top(int argc, const char **argv)
> }
> }
>
> + if (top.stitch_lbr && !(callchain_param.record_mode == CALLCHAIN_LBR)) {
> + pr_err("Error: --stitch-lbr must be used with --call-graph lbr\n");
> + goto out_delete_evlist;
> + }
why is this check not added for script/report/c2c..?
jirka
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V3 10/17] perf tools: Save previous sample for LBR stitching approach
2020-03-13 18:33 ` [PATCH V3 10/17] perf tools: Save previous sample for LBR stitching approach kan.liang
@ 2020-03-18 12:14 ` Jiri Olsa
2020-03-18 14:20 ` Liang, Kan
0 siblings, 1 reply; 26+ messages in thread
From: Jiri Olsa @ 2020-03-18 12:14 UTC (permalink / raw)
To: kan.liang
Cc: acme, peterz, mingo, linux-kernel, namhyung, adrian.hunter,
mathieu.poirier, ravi.bangoria, alexey.budankov,
vitaly.slobodskoy, pavel.gerasimov, mpe, eranian, ak
On Fri, Mar 13, 2020 at 11:33:12AM -0700, kan.liang@linux.intel.com wrote:
SNIP
> + struct lbr_stitch *lbr_stitch;
> };
>
> struct machine;
> @@ -145,4 +151,14 @@ static inline bool thread__is_filtered(struct thread *thread)
> return false;
> }
>
> +static inline void thread__free_stitch_list(struct thread *thread)
> +{
> + struct lbr_stitch *lbr_stitch = thread->lbr_stitch;
> +
> + if (!lbr_stitch)
> + return;
> +
> + free(thread->lbr_stitch);
> +}
free(thread->lbr_stitch) should be enough
jirka
> +
> #endif /* __PERF_THREAD_H */
> --
> 2.17.1
>
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V3 15/17] perf top: Add option to enable the LBR stitching approach
2020-03-18 12:14 ` Jiri Olsa
@ 2020-03-18 14:19 ` Liang, Kan
0 siblings, 0 replies; 26+ messages in thread
From: Liang, Kan @ 2020-03-18 14:19 UTC (permalink / raw)
To: Jiri Olsa
Cc: acme, peterz, mingo, linux-kernel, namhyung, adrian.hunter,
mathieu.poirier, ravi.bangoria, alexey.budankov,
vitaly.slobodskoy, pavel.gerasimov, mpe, eranian, ak
On 3/18/2020 8:14 AM, Jiri Olsa wrote:
> On Fri, Mar 13, 2020 at 11:33:17AM -0700, kan.liang@linux.intel.com wrote:
>
> SNIP
>
>> #include "util/top.h"
>> @@ -766,6 +767,9 @@ static void perf_event__process_sample(struct perf_tool *tool,
>> if (machine__resolve(machine, &al, sample) < 0)
>> return;
>>
>> + if (top->stitch_lbr)
>> + al.thread->lbr_stitch_enable = true;
>> +
>> if (!machine->kptr_restrict_warned &&
>> symbol_conf.kptr_restrict &&
>> al.cpumode == PERF_RECORD_MISC_KERNEL) {
>> @@ -1543,6 +1547,8 @@ int cmd_top(int argc, const char **argv)
>> "number of thread to run event synthesize"),
>> OPT_BOOLEAN(0, "namespaces", &opts->record_namespaces,
>> "Record namespaces events"),
>> + OPT_BOOLEAN(0, "stitch-lbr", &top.stitch_lbr,
>> + "Enable LBR callgraph stitching approach"),
>> OPTS_EVSWITCH(&top.evswitch),
>> OPT_END()
>> };
>> @@ -1612,6 +1618,11 @@ int cmd_top(int argc, const char **argv)
>> }
>> }
>>
>> + if (top.stitch_lbr && !(callchain_param.record_mode == CALLCHAIN_LBR)) {
>> + pr_err("Error: --stitch-lbr must be used with --call-graph lbr\n");
>> + goto out_delete_evlist;
>> + }
>
> why is this check not added for script/report/c2c..?
>
You are right. We should add a check for other tools as well.
I will print a warning as below for other tools in V3.
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index c798763f62db..0d544c4fb4be 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -2605,6 +2605,12 @@ static int setup_callchain(struct evlist *evlist)
}
}
+ if (c2c.stitch_lbr && (mode != CALLCHAIN_LBR)) {
+ ui__warning("Can't find LBR callchain. Switch off --stitch-lbr.\n"
+ "Please apply --call-graph lbr when recording.\n");
+ c2c.stitch_lbr = false;
+ }
+
callchain_param.record_mode = mode;
callchain_param.min_percent = 0;
return 0;
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index e41cedb9256c..3bdbd3649be7 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -411,6 +411,12 @@ static int report__setup_sample_type(struct report
*rep)
callchain_param.record_mode = CALLCHAIN_FP;
}
+ if (rep->stitch_lbr && (callchain_param.record_mode != CALLCHAIN_LBR)) {
+ ui__warning("Can't find LBR callchain. Switch off --stitch-lbr.\n"
+ "Please apply --call-graph lbr when recording.\n");
+ rep->stitch_lbr = false;
+ }
+
/* ??? handle more cases than just ANY? */
if (!(perf_evlist__combined_branch_type(session->evlist) &
PERF_SAMPLE_BRANCH_ANY))
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index f8f50bf95e40..e0ec4a0e7b35 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -3299,6 +3299,12 @@ static void script__setup_sample_type(struct
perf_script *script)
else
callchain_param.record_mode = CALLCHAIN_FP;
}
+
+ if (script->stitch_lbr && (callchain_param.record_mode !=
CALLCHAIN_LBR)) {
+ pr_warning("Can't find LBR callchain. Switch off --stitch-lbr.\n"
+ "Please apply --call-graph lbr when recording.\n");
+ script->stitch_lbr = false;
+ }
}
static int process_stat_round_event(struct perf_session *session,
Thanks,
Kan
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH V3 10/17] perf tools: Save previous sample for LBR stitching approach
2020-03-18 12:14 ` Jiri Olsa
@ 2020-03-18 14:20 ` Liang, Kan
0 siblings, 0 replies; 26+ messages in thread
From: Liang, Kan @ 2020-03-18 14:20 UTC (permalink / raw)
To: Jiri Olsa
Cc: acme, peterz, mingo, linux-kernel, namhyung, adrian.hunter,
mathieu.poirier, ravi.bangoria, alexey.budankov,
vitaly.slobodskoy, pavel.gerasimov, mpe, eranian, ak
On 3/18/2020 8:14 AM, Jiri Olsa wrote:
> On Fri, Mar 13, 2020 at 11:33:12AM -0700, kan.liang@linux.intel.com wrote:
>
> SNIP
>
>> + struct lbr_stitch *lbr_stitch;
>> };
>>
>> struct machine;
>> @@ -145,4 +151,14 @@ static inline bool thread__is_filtered(struct thread *thread)
>> return false;
>> }
>>
>> +static inline void thread__free_stitch_list(struct thread *thread)
>> +{
>> + struct lbr_stitch *lbr_stitch = thread->lbr_stitch;
>> +
>> + if (!lbr_stitch)
>> + return;
>> +
>> + free(thread->lbr_stitch);
>> +}
>
> free(thread->lbr_stitch) should be enough
>
Sure. Will change it in V4.
Thanks,
Kan
> jirka
>
>> +
>> #endif /* __PERF_THREAD_H */
>> --
>> 2.17.1
>>
>
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V3 01/17] perf pmu: Add support for PMU capabilities
2020-03-13 18:33 ` [PATCH V3 01/17] perf pmu: Add support for PMU capabilities kan.liang
@ 2020-03-18 19:47 ` Arnaldo Carvalho de Melo
2020-03-19 13:07 ` Liang, Kan
0 siblings, 1 reply; 26+ messages in thread
From: Arnaldo Carvalho de Melo @ 2020-03-18 19:47 UTC (permalink / raw)
To: kan.liang
Cc: jolsa, peterz, mingo, linux-kernel, namhyung, adrian.hunter,
mathieu.poirier, ravi.bangoria, alexey.budankov,
vitaly.slobodskoy, pavel.gerasimov, mpe, eranian, ak
Em Fri, Mar 13, 2020 at 11:33:03AM -0700, kan.liang@linux.intel.com escreveu:
> From: Kan Liang <kan.liang@linux.intel.com>
>
> The PMU capabilities information, which is located at
> /sys/bus/event_source/devices/<dev>/caps, is required by perf tool.
> For example, the max LBR information is required to stitch LBR call
> stack.
>
> Add perf_pmu__caps_parse() to parse the PMU capabilities information.
> The information is stored in a list.
>
> Add perf_pmu__scan_caps() to scan the capabilities one by one.
>
> The following patch will store the capabilities information in perf
> header.
>
> Reviewed-by: Andi Kleen <ak@linux.intel.com>
> Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
> ---
> tools/perf/util/pmu.c | 98 +++++++++++++++++++++++++++++++++++++++++++
> tools/perf/util/pmu.h | 12 ++++++
> 2 files changed, 110 insertions(+)
>
> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
> index 8b99fd312aae..4cdfbb669567 100644
> --- a/tools/perf/util/pmu.c
> +++ b/tools/perf/util/pmu.c
> @@ -844,6 +844,7 @@ static struct perf_pmu *pmu_lookup(const char *name)
>
> INIT_LIST_HEAD(&pmu->format);
> INIT_LIST_HEAD(&pmu->aliases);
> + INIT_LIST_HEAD(&pmu->caps);
> list_splice(&format, &pmu->format);
> list_splice(&aliases, &pmu->aliases);
> list_add_tail(&pmu->list, &pmus);
> @@ -1565,3 +1566,100 @@ int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
> va_end(args);
> return ret;
> }
> +
> +static int perf_pmu__new_caps(struct list_head *list, char *name, char *value)
> +{
> + struct perf_pmu_caps *caps;
> +
> + caps = zalloc(sizeof(*caps));
> + if (!caps)
> + return -ENOMEM;
Since there will be a v4 for other reasons, please combine the
declaration plus assignment when possible, like above, i.e. make it:
+ struct perf_pmu_caps *caps = zalloc(sizeof(*caps));
+
+ if (!caps)
+ return -ENOMEM;
> +
> + caps->name = strdup(name);
> + if (!caps->name)
> + goto free_caps;
> + caps->value = strndup(value, strlen(value) - 1);
> + if (!caps->value)
> + goto free_name;
> + list_add_tail(&caps->list, list);
> + return 0;
> +
> +free_name:
> + free(caps->name);
In these cases I think this is preferred:
zfree(&caps->name);
> +free_caps:
> + free(caps);
> +
> + return -ENOMEM;
> +}
> +
> +/*
> + * Reading/parsing the given pmu capabilities, which should be located at:
> + * /sys/bus/event_source/devices/<dev>/caps as sysfs group attributes.
> + * Return the number of capabilities
> + */
> +int perf_pmu__caps_parse(struct perf_pmu *pmu)
> +{
> + struct stat st;
> + char caps_path[PATH_MAX];
> + const char *sysfs = sysfs__mountpoint();
> + DIR *caps_dir;
> + struct dirent *evt_ent;
> + int nr_caps = 0;
> +
> + if (!sysfs)
> + return -1;
> +
> + snprintf(caps_path, PATH_MAX,
> + "%s" EVENT_SOURCE_DEVICE_PATH "%s/caps", sysfs, pmu->name);
> +
> + if (stat(caps_path, &st) < 0)
> + return 0; /* no error if caps does not exist */
> +
> + caps_dir = opendir(caps_path);
> + if (!caps_dir)
> + return -EINVAL;
> +
> + while ((evt_ent = readdir(caps_dir)) != NULL) {
> + char path[PATH_MAX + NAME_MAX + 1];
> + char *name = evt_ent->d_name;
> + char value[128];
> + FILE *file;
> +
> + if (!strcmp(name, ".") || !strcmp(name, ".."))
> + continue;
> +
> + snprintf(path, sizeof(path), "%s/%s", caps_path, name);
> +
> + file = fopen(path, "r");
> + if (!file)
> + break;
I haven't looked at what is in such directories that would justify still
returning whatever was read so far when failing to read one of its
files, is this sensible? Care to explain a bit why you think so? Looks
fishy :-\
> +
> + if (!fgets(value, sizeof(value), file) ||
> + (perf_pmu__new_caps(&pmu->caps, name, value) < 0)) {
> + fclose(file);
> + break;
> + }
> +
> + nr_caps++;
> + fclose(file);
> + }
> +
> + closedir(caps_dir);
> +
> + return nr_caps;
> +}
> +
> +struct perf_pmu_caps *perf_pmu__scan_caps(struct perf_pmu *pmu,
> + struct perf_pmu_caps *caps)
> +{
> + if (!pmu)
> + return NULL;
> +
> + if (!caps)
> + caps = list_prepare_entry(caps, &pmu->caps, list);
> +
> + list_for_each_entry_continue(caps, &pmu->caps, list)
> + return caps;
Why not:
return caps ? list_next_entry(caps) : list_first_entry(&pmu->caps, struct perf_pmu_caps, list);
Nah, it'll return the head after the last entry, not NULL, argh.
Anyway, looks like a confusing API :-\
I'll see how it is used in the following patches...
> +
> + return NULL;
> +}
> diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
> index 6737e3d5d568..a228e27ae462 100644
> --- a/tools/perf/util/pmu.h
> +++ b/tools/perf/util/pmu.h
> @@ -21,6 +21,12 @@ enum {
>
> struct perf_event_attr;
>
> +struct perf_pmu_caps {
> + char *name;
> + char *value;
> + struct list_head list;
> +};
> +
> struct perf_pmu {
> char *name;
> __u32 type;
> @@ -32,6 +38,7 @@ struct perf_pmu {
> struct perf_cpu_map *cpus;
> struct list_head format; /* HEAD struct perf_pmu_format -> list */
> struct list_head aliases; /* HEAD struct perf_pmu_alias -> list */
> + struct list_head caps; /* HEAD struct perf_pmu_caps -> list */
> struct list_head list; /* ELEM */
> };
>
> @@ -102,4 +109,9 @@ struct pmu_events_map *perf_pmu__find_map(struct perf_pmu *pmu);
>
> int perf_pmu__convert_scale(const char *scale, char **end, double *sval);
>
> +int perf_pmu__caps_parse(struct perf_pmu *pmu);
> +
> +struct perf_pmu_caps *perf_pmu__scan_caps(struct perf_pmu *pmu,
> + struct perf_pmu_caps *caps);
> +
> #endif /* __PMU_H */
> --
> 2.17.1
>
--
- Arnaldo
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V3 02/17] perf header: Support CPU PMU capabilities
2020-03-13 18:33 ` [PATCH V3 02/17] perf header: Support CPU " kan.liang
@ 2020-03-18 19:49 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 26+ messages in thread
From: Arnaldo Carvalho de Melo @ 2020-03-18 19:49 UTC (permalink / raw)
To: kan.liang
Cc: jolsa, peterz, mingo, linux-kernel, namhyung, adrian.hunter,
mathieu.poirier, ravi.bangoria, alexey.budankov,
vitaly.slobodskoy, pavel.gerasimov, mpe, eranian, ak
Em Fri, Mar 13, 2020 at 11:33:04AM -0700, kan.liang@linux.intel.com escreveu:
> From: Kan Liang <kan.liang@linux.intel.com>
>
> To stitch LBR call stack, the max LBR information is required. So the
> CPU PMU capabilities information has to be stored in perf header.
>
> Add a new feature HEADER_CPU_PMU_CAPS for CPU PMU capabilities.
> Retrieve all CPU PMU capabilities, not just max LBR information.
>
> Add variable max_branches to facilitate future usage.
>
> Reviewed-by: Andi Kleen <ak@linux.intel.com>
> Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
> ---
> .../Documentation/perf.data-file-format.txt | 16 +++
> tools/perf/util/env.h | 3 +
> tools/perf/util/header.c | 110 ++++++++++++++++++
> tools/perf/util/header.h | 1 +
> 4 files changed, 130 insertions(+)
>
> diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt
> index b0152e1095c5..b6472e463284 100644
> --- a/tools/perf/Documentation/perf.data-file-format.txt
> +++ b/tools/perf/Documentation/perf.data-file-format.txt
> @@ -373,6 +373,22 @@ struct {
> Indicates that trace contains records of PERF_RECORD_COMPRESSED type
> that have perf_events records in compressed form.
>
> + HEADER_CPU_PMU_CAPS = 28,
> +
> + A list of cpu PMU capabilities. The format of data is as below.
> +
> +struct {
> + u32 nr_cpu_pmu_caps;
> + {
> + char name[];
> + char value[];
> + } [nr_cpu_pmu_caps]
> +};
> +
> +
> +Example:
> + cpu pmu capabilities: branches=32, max_precise=3, pmu_name=icelake
> +
> other bits are reserved and should ignored for now
> HEADER_FEAT_BITS = 256,
>
> diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
> index 11d05ae3606a..d286d478b4d8 100644
> --- a/tools/perf/util/env.h
> +++ b/tools/perf/util/env.h
> @@ -48,6 +48,7 @@ struct perf_env {
> char *cpuid;
> unsigned long long total_mem;
> unsigned int msr_pmu_type;
> + unsigned int max_branches;
>
> int nr_cmdline;
> int nr_sibling_cores;
> @@ -57,12 +58,14 @@ struct perf_env {
> int nr_memory_nodes;
> int nr_pmu_mappings;
> int nr_groups;
> + int nr_cpu_pmu_caps;
> char *cmdline;
> const char **cmdline_argv;
> char *sibling_cores;
> char *sibling_dies;
> char *sibling_threads;
> char *pmu_mappings;
> + char *cpu_pmu_caps;
> struct cpu_topology_map *cpu;
> struct cpu_cache_level *caches;
> int caches_cnt;
> diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
> index acbd046bf95c..ce29321a4e1d 100644
> --- a/tools/perf/util/header.c
> +++ b/tools/perf/util/header.c
> @@ -1395,6 +1395,39 @@ 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_cpu_pmu_caps(struct feat_fd *ff,
> + struct evlist *evlist __maybe_unused)
> +{
> + struct perf_pmu_caps *caps = NULL;
> + struct perf_pmu *cpu_pmu;
> + int nr_caps;
> + int ret;
> +
> + cpu_pmu = perf_pmu__find("cpu");
please combine decl + assign:
+ struct perf_pmu *cpu_pmu = perf_pmu__find("cpu");
> + if (!cpu_pmu)
> + return -ENOENT;
> +
> + nr_caps = perf_pmu__caps_parse(cpu_pmu);
> + if (nr_caps < 0)
> + return nr_caps;
> +
> + ret = do_write(ff, &nr_caps, sizeof(nr_caps));
> + if (ret < 0)
> + return ret;
> +
> + while ((caps = perf_pmu__scan_caps(cpu_pmu, caps))) {
Why can't you play use list_for_each_entry() here and remove that perf_pmu__scan_caps()?
> + ret = do_write_string(ff, caps->name);
> + if (ret < 0)
> + return ret;
> +
> + ret = do_write_string(ff, caps->value);
> + if (ret < 0)
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> static void print_hostname(struct feat_fd *ff, FILE *fp)
> {
> fprintf(fp, "# hostname : %s\n", ff->ph->env.hostname);
> @@ -1809,6 +1842,28 @@ static void print_compressed(struct feat_fd *ff, FILE *fp)
> ff->ph->env.comp_level, ff->ph->env.comp_ratio);
> }
>
> +static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp)
> +{
> + const char *delimiter = "# cpu pmu capabilities: ";
> + char *str;
> + u32 nr_caps;
> +
> + nr_caps = ff->ph->env.nr_cpu_pmu_caps;
ditto
> + if (!nr_caps) {
> + fprintf(fp, "# cpu pmu capabilities: not available\n");
> + return;
> + }
> +
> + str = ff->ph->env.cpu_pmu_caps;
> + while (nr_caps--) {
> + fprintf(fp, "%s%s", delimiter, str);
> + delimiter = ", ";
> + str += strlen(str) + 1;
> + }
> +
> + fprintf(fp, "\n");
> +}
> +
> static void print_pmu_mappings(struct feat_fd *ff, FILE *fp)
> {
> const char *delimiter = "# pmu mappings: ";
> @@ -2846,6 +2901,60 @@ static int process_compressed(struct feat_fd *ff,
> return 0;
> }
>
> +static int process_cpu_pmu_caps(struct feat_fd *ff,
> + void *data __maybe_unused)
> +{
> + char *name, *value;
> + struct strbuf sb;
> + u32 nr_caps;
> +
> + if (do_read_u32(ff, &nr_caps))
> + return -1;
> +
> + if (!nr_caps) {
> + pr_debug("cpu pmu capabilities not available\n");
> + return 0;
> + }
> +
> + ff->ph->env.nr_cpu_pmu_caps = nr_caps;
> +
> + if (strbuf_init(&sb, 128) < 0)
> + return -1;
> +
> + while (nr_caps--) {
> + name = do_read_string(ff);
> + if (!name)
> + goto error;
> +
> + value = do_read_string(ff);
> + if (!value)
> + goto free_name;
> +
> + if (strbuf_addf(&sb, "%s=%s", name, value) < 0)
> + goto free_value;
> +
> + /* include a NULL character at the end */
> + if (strbuf_add(&sb, "", 1) < 0)
> + goto free_value;
> +
> + if (!strcmp(name, "branches"))
> + ff->ph->env.max_branches = atoi(value);
> +
> + free(value);
> + free(name);
> + }
> + ff->ph->env.cpu_pmu_caps = strbuf_detach(&sb, NULL);
> + return 0;
> +
> +free_value:
> + free(value);
> +free_name:
> + free(name);
> +error:
> + strbuf_release(&sb);
> + return -1;
> +}
> +
> #define FEAT_OPR(n, func, __full_only) \
> [HEADER_##n] = { \
> .name = __stringify(n), \
> @@ -2903,6 +3012,7 @@ const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = {
> FEAT_OPR(BPF_PROG_INFO, bpf_prog_info, false),
> FEAT_OPR(BPF_BTF, bpf_btf, false),
> FEAT_OPR(COMPRESSED, compressed, false),
> + FEAT_OPR(CPU_PMU_CAPS, cpu_pmu_caps, false),
> };
>
> struct header_print_data {
> diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
> index 840f95cee349..650bd1c7a99b 100644
> --- a/tools/perf/util/header.h
> +++ b/tools/perf/util/header.h
> @@ -43,6 +43,7 @@ enum {
> HEADER_BPF_PROG_INFO,
> HEADER_BPF_BTF,
> HEADER_COMPRESSED,
> + HEADER_CPU_PMU_CAPS,
> HEADER_LAST_FEATURE,
> HEADER_FEAT_BITS = 256,
> };
> --
> 2.17.1
>
--
- Arnaldo
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V3 05/17] perf machine: Remove the indent in resolve_lbr_callchain_sample
2020-03-13 18:33 ` [PATCH V3 05/17] perf machine: Remove the indent in resolve_lbr_callchain_sample kan.liang
@ 2020-03-18 19:50 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 26+ messages in thread
From: Arnaldo Carvalho de Melo @ 2020-03-18 19:50 UTC (permalink / raw)
To: kan.liang
Cc: jolsa, peterz, mingo, linux-kernel, namhyung, adrian.hunter,
mathieu.poirier, ravi.bangoria, alexey.budankov,
vitaly.slobodskoy, pavel.gerasimov, mpe, eranian, ak
Em Fri, Mar 13, 2020 at 11:33:07AM -0700, kan.liang@linux.intel.com escreveu:
> From: Kan Liang <kan.liang@linux.intel.com>
>
> The indent is unnecessary in resolve_lbr_callchain_sample.
> Removing it will make the following patch simpler.
>
> Current code path for resolve_lbr_callchain_sample()
>
> /* LBR only affects the user callchain */
> if (i != chain_nr) {
> body of the function
> ....
> return 1;
> }
>
> return 0;
>
> With the patch,
>
> /* LBR only affects the user callchain */
> if (i == chain_nr)
> return 0;
>
> body of the function
> ...
> return 1;
>
> No functional changes.
Thanks for doing this,
- Arnaldo
> Reviewed-by: Andi Kleen <ak@linux.intel.com>
> Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
> ---
> tools/perf/util/machine.c | 123 +++++++++++++++++++-------------------
> 1 file changed, 63 insertions(+), 60 deletions(-)
>
> diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
> index fd14f1489802..9021e5b6a2a9 100644
> --- a/tools/perf/util/machine.c
> +++ b/tools/perf/util/machine.c
> @@ -2177,6 +2177,12 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
> int chain_nr = min(max_stack, (int)chain->nr), i;
> u8 cpumode = PERF_RECORD_MISC_USER;
> u64 ip, branch_from = 0;
> + struct branch_stack *lbr_stack;
> + struct branch_entry *entries;
> + int lbr_nr, j, k;
> + bool branch;
> + struct branch_flags *flags;
> + int mix_chain_nr;
>
> for (i = 0; i < chain_nr; i++) {
> if (chain->ips[i] == PERF_CONTEXT_USER)
> @@ -2184,71 +2190,68 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
> }
>
> /* LBR only affects the user callchain */
> - if (i != chain_nr) {
> - struct branch_stack *lbr_stack = sample->branch_stack;
> - struct branch_entry *entries = perf_sample__branch_entries(sample);
> - int lbr_nr = lbr_stack->nr, j, k;
> - bool branch;
> - struct branch_flags *flags;
> - /*
> - * LBR callstack can only get user call chain.
> - * The mix_chain_nr is kernel call chain
> - * number plus LBR user call chain number.
> - * i is kernel call chain number,
> - * 1 is PERF_CONTEXT_USER,
> - * lbr_nr + 1 is the user call chain number.
> - * For details, please refer to the comments
> - * in callchain__printf
> - */
> - int mix_chain_nr = i + 1 + lbr_nr + 1;
> -
> - for (j = 0; j < mix_chain_nr; j++) {
> - int err;
> - branch = false;
> - flags = NULL;
> + if (i == chain_nr)
> + return 0;
>
> - if (callchain_param.order == ORDER_CALLEE) {
> - if (j < i + 1)
> - ip = chain->ips[j];
> - else if (j > i + 1) {
> - k = j - i - 2;
> - ip = entries[k].from;
> - branch = true;
> - flags = &entries[k].flags;
> - } else {
> - ip = entries[0].to;
> - branch = true;
> - flags = &entries[0].flags;
> - branch_from = entries[0].from;
> - }
> + lbr_stack = sample->branch_stack;
> + entries = perf_sample__branch_entries(sample);
> + lbr_nr = lbr_stack->nr;
> + /*
> + * LBR callstack can only get user call chain.
> + * The mix_chain_nr is kernel call chain
> + * number plus LBR user call chain number.
> + * i is kernel call chain number,
> + * 1 is PERF_CONTEXT_USER,
> + * lbr_nr + 1 is the user call chain number.
> + * For details, please refer to the comments
> + * in callchain__printf
> + */
> + mix_chain_nr = i + 1 + lbr_nr + 1;
> +
> + for (j = 0; j < mix_chain_nr; j++) {
> + int err;
> +
> + branch = false;
> + flags = NULL;
> +
> + if (callchain_param.order == ORDER_CALLEE) {
> + if (j < i + 1)
> + ip = chain->ips[j];
> + else if (j > i + 1) {
> + k = j - i - 2;
> + ip = entries[k].from;
> + branch = true;
> + flags = &entries[k].flags;
> } else {
> - if (j < lbr_nr) {
> - k = lbr_nr - j - 1;
> - ip = entries[k].from;
> - branch = true;
> - flags = &entries[k].flags;
> - }
> - else if (j > lbr_nr)
> - ip = chain->ips[i + 1 - (j - lbr_nr)];
> - else {
> - ip = entries[0].to;
> - branch = true;
> - flags = &entries[0].flags;
> - branch_from = entries[0].from;
> - }
> + ip = entries[0].to;
> + branch = true;
> + flags = &entries[0].flags;
> + branch_from = entries[0].from;
> + }
> + } else {
> + if (j < lbr_nr) {
> + k = lbr_nr - j - 1;
> + ip = entries[k].from;
> + branch = true;
> + flags = &entries[k].flags;
> + } else if (j > lbr_nr)
> + ip = chain->ips[i + 1 - (j - lbr_nr)];
> + else {
> + ip = entries[0].to;
> + branch = true;
> + flags = &entries[0].flags;
> + branch_from = entries[0].from;
> }
> -
> - err = add_callchain_ip(thread, cursor, parent,
> - root_al, &cpumode, ip,
> - branch, flags, NULL,
> - branch_from);
> - if (err)
> - return (err < 0) ? err : 0;
> }
> - return 1;
> - }
>
> - return 0;
> + err = add_callchain_ip(thread, cursor, parent,
> + root_al, &cpumode, ip,
> + branch, flags, NULL,
> + branch_from);
> + if (err)
> + return (err < 0) ? err : 0;
> + }
> + return 1;
> }
>
> static int find_prev_cpumode(struct ip_callchain *chain, struct thread *thread,
> --
> 2.17.1
>
--
- Arnaldo
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V3 01/17] perf pmu: Add support for PMU capabilities
2020-03-18 19:47 ` Arnaldo Carvalho de Melo
@ 2020-03-19 13:07 ` Liang, Kan
0 siblings, 0 replies; 26+ messages in thread
From: Liang, Kan @ 2020-03-19 13:07 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: jolsa, peterz, mingo, linux-kernel, namhyung, adrian.hunter,
mathieu.poirier, ravi.bangoria, alexey.budankov,
vitaly.slobodskoy, pavel.gerasimov, mpe, eranian, ak
On 3/18/2020 3:47 PM, Arnaldo Carvalho de Melo wrote:
> Em Fri, Mar 13, 2020 at 11:33:03AM -0700, kan.liang@linux.intel.com escreveu:
>> From: Kan Liang <kan.liang@linux.intel.com>
>>
>> The PMU capabilities information, which is located at
>> /sys/bus/event_source/devices/<dev>/caps, is required by perf tool.
>> For example, the max LBR information is required to stitch LBR call
>> stack.
>>
>> Add perf_pmu__caps_parse() to parse the PMU capabilities information.
>> The information is stored in a list.
>>
>> Add perf_pmu__scan_caps() to scan the capabilities one by one.
>>
>> The following patch will store the capabilities information in perf
>> header.
>>
>> Reviewed-by: Andi Kleen <ak@linux.intel.com>
>> Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
>> ---
>> tools/perf/util/pmu.c | 98 +++++++++++++++++++++++++++++++++++++++++++
>> tools/perf/util/pmu.h | 12 ++++++
>> 2 files changed, 110 insertions(+)
>>
>> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
>> index 8b99fd312aae..4cdfbb669567 100644
>> --- a/tools/perf/util/pmu.c
>> +++ b/tools/perf/util/pmu.c
>> @@ -844,6 +844,7 @@ static struct perf_pmu *pmu_lookup(const char *name)
>>
>> INIT_LIST_HEAD(&pmu->format);
>> INIT_LIST_HEAD(&pmu->aliases);
>> + INIT_LIST_HEAD(&pmu->caps);
>> list_splice(&format, &pmu->format);
>> list_splice(&aliases, &pmu->aliases);
>> list_add_tail(&pmu->list, &pmus);
>> @@ -1565,3 +1566,100 @@ int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
>> va_end(args);
>> return ret;
>> }
>> +
>> +static int perf_pmu__new_caps(struct list_head *list, char *name, char *value)
>> +{
>> + struct perf_pmu_caps *caps;
>> +
>> + caps = zalloc(sizeof(*caps));
>> + if (!caps)
>> + return -ENOMEM;
>
> Since there will be a v4 for other reasons, please combine the
> declaration plus assignment when possible, like above, i.e. make it:
>
Sure.
> + struct perf_pmu_caps *caps = zalloc(sizeof(*caps));
> +
> + if (!caps)
> + return -ENOMEM;
>
>> +
>> + caps->name = strdup(name);
>> + if (!caps->name)
>> + goto free_caps;
>> + caps->value = strndup(value, strlen(value) - 1);
>> + if (!caps->value)
>> + goto free_name;
>> + list_add_tail(&caps->list, list);
>> + return 0;
>> +
>> +free_name:
>> + free(caps->name);
>
> In these cases I think this is preferred:
>
> zfree(&caps->name);
Sure.
>
>> +free_caps:
>> + free(caps);
>> +
>> + return -ENOMEM;
>> +}
>> +
>> +/*
>> + * Reading/parsing the given pmu capabilities, which should be located at:
>> + * /sys/bus/event_source/devices/<dev>/caps as sysfs group attributes.
>> + * Return the number of capabilities
>> + */
>> +int perf_pmu__caps_parse(struct perf_pmu *pmu)
>> +{
>> + struct stat st;
>> + char caps_path[PATH_MAX];
>> + const char *sysfs = sysfs__mountpoint();
>> + DIR *caps_dir;
>> + struct dirent *evt_ent;
>> + int nr_caps = 0;
>> +
>> + if (!sysfs)
>> + return -1;
>> +
>> + snprintf(caps_path, PATH_MAX,
>> + "%s" EVENT_SOURCE_DEVICE_PATH "%s/caps", sysfs, pmu->name);
>> +
>> + if (stat(caps_path, &st) < 0)
>> + return 0; /* no error if caps does not exist */
>> +
>> + caps_dir = opendir(caps_path);
>> + if (!caps_dir)
>> + return -EINVAL;
>> +
>> + while ((evt_ent = readdir(caps_dir)) != NULL) {
>> + char path[PATH_MAX + NAME_MAX + 1];
>> + char *name = evt_ent->d_name;
>> + char value[128];
>> + FILE *file;
>> +
>> + if (!strcmp(name, ".") || !strcmp(name, ".."))
>> + continue;
>> +
>> + snprintf(path, sizeof(path), "%s/%s", caps_path, name);
>> +
>> + file = fopen(path, "r");
>> + if (!file)
>> + break;
>
> I haven't looked at what is in such directories that would justify still
> returning whatever was read so far when failing to read one of its
> files, is this sensible? Care to explain a bit why you think so? Looks
> fishy :-\
There is no dependency among the 'capabilities'. If perf fails to read
one, it should not impact others.
Yes, 'continue' looks better here.
>
>> +
>> + if (!fgets(value, sizeof(value), file) ||
>> + (perf_pmu__new_caps(&pmu->caps, name, value) < 0)) {
>> + fclose(file);
>> + break;
>> + }
>> +
>> + nr_caps++;
>> + fclose(file);
>> + }
>> +
>> + closedir(caps_dir);
>> +
>> + return nr_caps;
>> +}
>> +
>> +struct perf_pmu_caps *perf_pmu__scan_caps(struct perf_pmu *pmu,
>> + struct perf_pmu_caps *caps)
>> +{
>> + if (!pmu)
>> + return NULL;
>> +
>> + if (!caps)
>> + caps = list_prepare_entry(caps, &pmu->caps, list);
>> +
>> + list_for_each_entry_continue(caps, &pmu->caps, list)
>> + return caps;
>
> Why not:
>
> return caps ? list_next_entry(caps) : list_first_entry(&pmu->caps, struct perf_pmu_caps, list);
>
> Nah, it'll return the head after the last entry, not NULL, argh.
>
> Anyway, looks like a confusing API :-\
>
> I'll see how it is used in the following patches...
As suggested in other thread, I will use list_for_each_entry() to
replace in v4.
Thanks,
Kan
>
>> +
>> + return NULL;
>> +}
>> diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
>> index 6737e3d5d568..a228e27ae462 100644
>> --- a/tools/perf/util/pmu.h
>> +++ b/tools/perf/util/pmu.h
>> @@ -21,6 +21,12 @@ enum {
>>
>> struct perf_event_attr;
>>
>> +struct perf_pmu_caps {
>> + char *name;
>> + char *value;
>> + struct list_head list;
>> +};
>> +
>> struct perf_pmu {
>> char *name;
>> __u32 type;
>> @@ -32,6 +38,7 @@ struct perf_pmu {
>> struct perf_cpu_map *cpus;
>> struct list_head format; /* HEAD struct perf_pmu_format -> list */
>> struct list_head aliases; /* HEAD struct perf_pmu_alias -> list */
>> + struct list_head caps; /* HEAD struct perf_pmu_caps -> list */
>> struct list_head list; /* ELEM */
>> };
>>
>> @@ -102,4 +109,9 @@ struct pmu_events_map *perf_pmu__find_map(struct perf_pmu *pmu);
>>
>> int perf_pmu__convert_scale(const char *scale, char **end, double *sval);
>>
>> +int perf_pmu__caps_parse(struct perf_pmu *pmu);
>> +
>> +struct perf_pmu_caps *perf_pmu__scan_caps(struct perf_pmu *pmu,
>> + struct perf_pmu_caps *caps);
>> +
>> #endif /* __PMU_H */
>> --
>> 2.17.1
>>
>
^ permalink raw reply [flat|nested] 26+ messages in thread
end of thread, other threads:[~2020-03-19 13:07 UTC | newest]
Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-03-13 18:33 [PATCH V3 00/17] Stitch LBR call stack (Perf Tools) kan.liang
2020-03-13 18:33 ` [PATCH V3 01/17] perf pmu: Add support for PMU capabilities kan.liang
2020-03-18 19:47 ` Arnaldo Carvalho de Melo
2020-03-19 13:07 ` Liang, Kan
2020-03-13 18:33 ` [PATCH V3 02/17] perf header: Support CPU " kan.liang
2020-03-18 19:49 ` Arnaldo Carvalho de Melo
2020-03-13 18:33 ` [PATCH V3 03/17] perf record: Clear HEADER_CPU_PMU_CAPS for non LBR call stack mode kan.liang
2020-03-13 18:33 ` [PATCH V3 04/17] perf stat: Clear HEADER_CPU_PMU_CAPS kan.liang
2020-03-13 18:33 ` [PATCH V3 05/17] perf machine: Remove the indent in resolve_lbr_callchain_sample kan.liang
2020-03-18 19:50 ` Arnaldo Carvalho de Melo
2020-03-13 18:33 ` [PATCH V3 06/17] perf machine: Refine the function for LBR call stack reconstruction kan.liang
2020-03-13 18:33 ` [PATCH V3 07/17] perf machine: Factor out lbr_callchain_add_kernel_ip() kan.liang
2020-03-13 18:33 ` [PATCH V3 08/17] perf machine: Factor out lbr_callchain_add_lbr_ip() kan.liang
2020-03-13 18:33 ` [PATCH V3 09/17] perf thread: Add a knob for LBR stitch approach kan.liang
2020-03-13 18:33 ` [PATCH V3 10/17] perf tools: Save previous sample for LBR stitching approach kan.liang
2020-03-18 12:14 ` Jiri Olsa
2020-03-18 14:20 ` Liang, Kan
2020-03-13 18:33 ` [PATCH V3 11/17] perf tools: Save previous cursor nodes " kan.liang
2020-03-13 18:33 ` [PATCH V3 12/17] perf tools: Stitch LBR call stack kan.liang
2020-03-13 18:33 ` [PATCH V3 13/17] perf report: Add option to enable the LBR stitching approach kan.liang
2020-03-13 18:33 ` [PATCH V3 14/17] perf script: " kan.liang
2020-03-13 18:33 ` [PATCH V3 15/17] perf top: " kan.liang
2020-03-18 12:14 ` Jiri Olsa
2020-03-18 14:19 ` Liang, Kan
2020-03-13 18:33 ` [PATCH V3 16/17] perf c2c: " kan.liang
2020-03-13 18:33 ` [PATCH V3 17/17] perf hist: Add fast path for duplicate entries check kan.liang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).