2024-04-11 12:11:17

by Bibo Mao

[permalink] [raw]
Subject: [PATCH v3] perf kvm: Add kvm-stat for loongarch64

Add support for 'perf kvm stat' on loongarch64 platform, now only
kvm exit event is supported.

Here is example output about "perf kvm --host stat report" command

Event name Samples Sample% Time (ns) Time% Mean Time (ns)
Mem store 83969 51.00% 625697070 8.00% 7451
Mem read 37641 22.00% 112485730 1.00% 2988
Interrupt 15542 9.00% 20620190 0.00% 1326
Iocsr 15207 9.00% 94296190 1.00% 6200
Hypercall 4873 2.00% 12265280 0.00% 2516
Idle 3713 2.00% 6322055860 87.00% 1702681
FPU 1819 1.00% 2750300 0.00% 1511
Ifecth 502 0.00% 1341740 0.00% 2672
Mem modify 324 0.00% 602240 0.00% 1858
Cpucfg 55 0.00% 77610 0.00% 1411
Csr 12 0.00% 19690 0.00% 1640
LASX 3 0.00% 4870 0.00% 1623
LSX 2 0.00% 2100 0.00% 1050

Signed-off-by: Bibo Mao <[email protected]>
---
v2 --- v3:
1. Add NULL check with cpuid in function get_cpuid()
2. Add example output from /proc/cpuinfo before function get_cpuid()
v1 --- v2:
1. Add child_ops for kvm exit event, split kvm:kvm_exit_gspr events
into cpucfg/csr/iocsr/idle child events by decoding detailed gspr
instruction.
2. Remove some exception code type which does not happen in current
kvm implementation, such as meomry NR/NX/priviledge exception.
---
tools/perf/arch/loongarch/Makefile | 1 +
tools/perf/arch/loongarch/util/Build | 2 +
tools/perf/arch/loongarch/util/header.c | 88 ++++++++++++++
tools/perf/arch/loongarch/util/kvm-stat.c | 135 ++++++++++++++++++++++
4 files changed, 226 insertions(+)
create mode 100644 tools/perf/arch/loongarch/util/header.c
create mode 100644 tools/perf/arch/loongarch/util/kvm-stat.c

diff --git a/tools/perf/arch/loongarch/Makefile b/tools/perf/arch/loongarch/Makefile
index 3992a67a87d9..c89d6bb6b184 100644
--- a/tools/perf/arch/loongarch/Makefile
+++ b/tools/perf/arch/loongarch/Makefile
@@ -4,6 +4,7 @@ PERF_HAVE_DWARF_REGS := 1
endif
PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
PERF_HAVE_JITDUMP := 1
+HAVE_KVM_STAT_SUPPORT := 1

#
# Syscall table generation for perf
diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build
index d776125a2d06..b12d374d7096 100644
--- a/tools/perf/arch/loongarch/util/Build
+++ b/tools/perf/arch/loongarch/util/Build
@@ -1,5 +1,7 @@
+perf-y += header.o
perf-y += perf_regs.o

perf-$(CONFIG_DWARF) += dwarf-regs.o
perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
+perf-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
diff --git a/tools/perf/arch/loongarch/util/header.c b/tools/perf/arch/loongarch/util/header.c
new file mode 100644
index 000000000000..a4ed732b49c6
--- /dev/null
+++ b/tools/perf/arch/loongarch/util/header.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Implementation of get_cpuid().
+ *
+ * Author: Nikita Shubin <[email protected]>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <api/fs/fs.h>
+#include <errno.h>
+#include "util/debug.h"
+#include "util/header.h"
+
+/*
+ * Output example from /proc/cpuinfo
+ * CPU Family : Loongson-64bit
+ * Model Name : Loongson-3C5000
+ * CPU Revision : 0x11
+ */
+#define CPUINFO_MODEL "Model Name"
+#define CPUINFO "/proc/cpuinfo"
+static char *_get_field(const char *line)
+{
+ char *line2, *nl;
+
+ line2 = strrchr(line, ' ');
+ if (!line2)
+ return NULL;
+
+ line2++;
+ nl = strrchr(line, '\n');
+ if (!nl)
+ return NULL;
+
+ return strndup(line2, nl - line2);
+}
+
+static char *__get_cpuid(void)
+{
+ char *line, *model, *cpuid;
+ unsigned long line_sz;
+ FILE *file;
+
+ file = fopen(CPUINFO, "r");
+ if (file == NULL)
+ return cpuid;
+
+ line = model = cpuid = NULL;
+ while (getline(&line, &line_sz, file) != -1) {
+ if (strncmp(line, CPUINFO_MODEL, strlen(CPUINFO_MODEL)))
+ continue;
+
+ model = _get_field(line);
+ if (!model)
+ goto free;
+ break;
+ }
+
+ if (model && (asprintf(&cpuid, "%s", model) < 0))
+ cpuid = NULL;
+
+free:
+ fclose(file);
+ free(model);
+ return cpuid;
+}
+
+int get_cpuid(char *buffer, size_t sz)
+{
+ char *cpuid = __get_cpuid();
+ int ret = 0;
+
+ if (!cpuid)
+ return EINVAL;
+
+ if (sz >= strlen(cpuid))
+ scnprintf(buffer, sz, "%s", cpuid);
+ else
+ ret = ENOBUFS;
+ free(cpuid);
+ return ret;
+}
+
+char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
+{
+ return __get_cpuid();
+}
diff --git a/tools/perf/arch/loongarch/util/kvm-stat.c b/tools/perf/arch/loongarch/util/kvm-stat.c
new file mode 100644
index 000000000000..cc50adb0835a
--- /dev/null
+++ b/tools/perf/arch/loongarch/util/kvm-stat.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <memory.h>
+#include <errno.h>
+#include "util/kvm-stat.h"
+#include "util/parse-events.h"
+#include "util/debug.h"
+#include "util/evsel.h"
+#include "util/evlist.h"
+#include "util/pmus.h"
+
+#define LOONGARCH_EXCEPTION_INT 0
+#define LOONGARCH_EXCEPTION_PIL 1
+#define LOONGARCH_EXCEPTION_PIS 2
+#define LOONGARCH_EXCEPTION_PIF 3
+#define LOONGARCH_EXCEPTION_PME 4
+#define LOONGARCH_EXCEPTION_FPD 15
+#define LOONGARCH_EXCEPTION_SXD 16
+#define LOONGARCH_EXCEPTION_ASXD 17
+#define LOONGARCH_EXCEPTION_GSPR 22
+#define LOONGARCH_EXCEPTION_CPUCFG 100
+#define LOONGARCH_EXCEPTION_CSR 101
+#define LOONGARCH_EXCEPTION_IOCSR 102
+#define LOONGARCH_EXCEPTION_IDLE 103
+#define LOONGARCH_EXCEPTION_OTHERS 104
+#define LOONGARCH_EXCEPTION_HVC 23
+
+#define loongarch_exception_type \
+ {LOONGARCH_EXCEPTION_INT, "Interrupt" }, \
+ {LOONGARCH_EXCEPTION_PIL, "Mem read" }, \
+ {LOONGARCH_EXCEPTION_PIS, "Mem store" }, \
+ {LOONGARCH_EXCEPTION_PIF, "Ifecth" }, \
+ {LOONGARCH_EXCEPTION_PME, "Mem modify" }, \
+ {LOONGARCH_EXCEPTION_FPD, "FPU" }, \
+ {LOONGARCH_EXCEPTION_SXD, "LSX" }, \
+ {LOONGARCH_EXCEPTION_ASXD, "LASX" }, \
+ {LOONGARCH_EXCEPTION_GSPR, "Privilege Error" }, \
+ {LOONGARCH_EXCEPTION_HVC, "Hypercall" }, \
+ {LOONGARCH_EXCEPTION_CPUCFG, "Cpucfg" }, \
+ {LOONGARCH_EXCEPTION_CSR, "Csr" }, \
+ {LOONGARCH_EXCEPTION_IOCSR, "Iocsr" }, \
+ {LOONGARCH_EXCEPTION_IDLE, "Idle" }, \
+ {LOONGARCH_EXCEPTION_OTHERS, "Others" }
+
+define_exit_reasons_table(loongarch_exit_reasons, loongarch_exception_type);
+
+const char *vcpu_id_str = "vcpu_id";
+const char *kvm_exit_reason = "reason";
+const char *kvm_entry_trace = "kvm:kvm_enter";
+const char *kvm_reenter_trace = "kvm:kvm_reenter";
+const char *kvm_exit_trace = "kvm:kvm_exit";
+const char *kvm_events_tp[] = {
+ "kvm:kvm_enter",
+ "kvm:kvm_reenter",
+ "kvm:kvm_exit",
+ "kvm:kvm_exit_gspr",
+ NULL,
+};
+
+static bool event_end(struct evsel *evsel,
+ struct perf_sample *sample __maybe_unused,
+ struct event_key *key __maybe_unused)
+{
+ /*
+ * LoongArch kvm is different with other architectures
+ *
+ * There is kvm:kvm_reenter or kvm:kvm_enter event adjacent with
+ * kvm:kvm_exit event.
+ * kvm:kvm_reenter means returning to guest immediately
+ * kvm:kvm_enter means returning to vmm and then to guest
+ */
+ return evsel__name_is(evsel, kvm_entry_trace) ||
+ evsel__name_is(evsel, kvm_reenter_trace);
+}
+
+static void event_gspr_get_key(struct evsel *evsel,
+ struct perf_sample *sample,
+ struct event_key *key)
+{
+ unsigned int insn;
+
+ key->key = LOONGARCH_EXCEPTION_OTHERS;
+ insn = evsel__intval(evsel, sample, "inst_word");
+ switch (insn >> 24) {
+ case 0:
+ /* cpucfg inst trap */
+ if ((insn >> 10) == 0x1b)
+ key->key = LOONGARCH_EXCEPTION_CPUCFG;
+ break;
+ case 4:
+ /* csr inst trap */
+ key->key = LOONGARCH_EXCEPTION_CSR;
+ break;
+ case 6:
+ /* iocsr inst trap */
+ if ((insn >> 15) == 0xc90)
+ key->key = LOONGARCH_EXCEPTION_IOCSR;
+ else if ((insn >> 15) == 0xc91)
+ /* idle inst trap */
+ key->key = LOONGARCH_EXCEPTION_IDLE;
+ break;
+ default:
+ key->key = LOONGARCH_EXCEPTION_OTHERS;
+ break;
+ }
+}
+
+static struct child_event_ops child_events[] = {
+ { .name = "kvm:kvm_exit_gspr", .get_key = event_gspr_get_key },
+ { NULL, NULL },
+};
+
+static struct kvm_events_ops exit_events = {
+ .is_begin_event = exit_event_begin,
+ .is_end_event = event_end,
+ .child_ops = child_events,
+ .decode_key = exit_event_decode_key,
+ .name = "VM-EXIT"
+};
+
+struct kvm_reg_events_ops kvm_reg_events_ops[] = {
+ { .name = "vmexit", .ops = &exit_events, },
+ { NULL, NULL },
+};
+
+const char * const kvm_skip_events[] = {
+ NULL,
+};
+
+int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid __maybe_unused)
+{
+ kvm->exit_reasons_isa = "loongarch64";
+ kvm->exit_reasons = loongarch_exit_reasons;
+ return 0;
+}

base-commit: 2c71fdf02a95b3dd425b42f28fd47fb2b1d22702
--
2.39.3



2024-04-19 02:35:09

by Bibo Mao

[permalink] [raw]
Subject: Re: [PATCH v3] perf kvm: Add kvm-stat for loongarch64

slightly ping...

On 2024/4/11 下午8:10, Bibo Mao wrote:
> Add support for 'perf kvm stat' on loongarch64 platform, now only
> kvm exit event is supported.
>
> Here is example output about "perf kvm --host stat report" command
>
> Event name Samples Sample% Time (ns) Time% Mean Time (ns)
> Mem store 83969 51.00% 625697070 8.00% 7451
> Mem read 37641 22.00% 112485730 1.00% 2988
> Interrupt 15542 9.00% 20620190 0.00% 1326
> Iocsr 15207 9.00% 94296190 1.00% 6200
> Hypercall 4873 2.00% 12265280 0.00% 2516
> Idle 3713 2.00% 6322055860 87.00% 1702681
> FPU 1819 1.00% 2750300 0.00% 1511
> Ifecth 502 0.00% 1341740 0.00% 2672
> Mem modify 324 0.00% 602240 0.00% 1858
> Cpucfg 55 0.00% 77610 0.00% 1411
> Csr 12 0.00% 19690 0.00% 1640
> LASX 3 0.00% 4870 0.00% 1623
> LSX 2 0.00% 2100 0.00% 1050
>
> Signed-off-by: Bibo Mao <[email protected]>
> ---
> v2 --- v3:
> 1. Add NULL check with cpuid in function get_cpuid()
> 2. Add example output from /proc/cpuinfo before function get_cpuid()
> v1 --- v2:
> 1. Add child_ops for kvm exit event, split kvm:kvm_exit_gspr events
> into cpucfg/csr/iocsr/idle child events by decoding detailed gspr
> instruction.
> 2. Remove some exception code type which does not happen in current
> kvm implementation, such as meomry NR/NX/priviledge exception.
> ---
> tools/perf/arch/loongarch/Makefile | 1 +
> tools/perf/arch/loongarch/util/Build | 2 +
> tools/perf/arch/loongarch/util/header.c | 88 ++++++++++++++
> tools/perf/arch/loongarch/util/kvm-stat.c | 135 ++++++++++++++++++++++
> 4 files changed, 226 insertions(+)
> create mode 100644 tools/perf/arch/loongarch/util/header.c
> create mode 100644 tools/perf/arch/loongarch/util/kvm-stat.c
>
> diff --git a/tools/perf/arch/loongarch/Makefile b/tools/perf/arch/loongarch/Makefile
> index 3992a67a87d9..c89d6bb6b184 100644
> --- a/tools/perf/arch/loongarch/Makefile
> +++ b/tools/perf/arch/loongarch/Makefile
> @@ -4,6 +4,7 @@ PERF_HAVE_DWARF_REGS := 1
> endif
> PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
> PERF_HAVE_JITDUMP := 1
> +HAVE_KVM_STAT_SUPPORT := 1
>
> #
> # Syscall table generation for perf
> diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build
> index d776125a2d06..b12d374d7096 100644
> --- a/tools/perf/arch/loongarch/util/Build
> +++ b/tools/perf/arch/loongarch/util/Build
> @@ -1,5 +1,7 @@
> +perf-y += header.o
> perf-y += perf_regs.o
>
> perf-$(CONFIG_DWARF) += dwarf-regs.o
> perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
> perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> +perf-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
> diff --git a/tools/perf/arch/loongarch/util/header.c b/tools/perf/arch/loongarch/util/header.c
> new file mode 100644
> index 000000000000..a4ed732b49c6
> --- /dev/null
> +++ b/tools/perf/arch/loongarch/util/header.c
> @@ -0,0 +1,88 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Implementation of get_cpuid().
> + *
> + * Author: Nikita Shubin <[email protected]>
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <api/fs/fs.h>
> +#include <errno.h>
> +#include "util/debug.h"
> +#include "util/header.h"
> +
> +/*
> + * Output example from /proc/cpuinfo
> + * CPU Family : Loongson-64bit
> + * Model Name : Loongson-3C5000
> + * CPU Revision : 0x11
> + */
> +#define CPUINFO_MODEL "Model Name"
> +#define CPUINFO "/proc/cpuinfo"
> +static char *_get_field(const char *line)
> +{
> + char *line2, *nl;
> +
> + line2 = strrchr(line, ' ');
> + if (!line2)
> + return NULL;
> +
> + line2++;
> + nl = strrchr(line, '\n');
> + if (!nl)
> + return NULL;
> +
> + return strndup(line2, nl - line2);
> +}
> +
> +static char *__get_cpuid(void)
> +{
> + char *line, *model, *cpuid;
> + unsigned long line_sz;
> + FILE *file;
> +
> + file = fopen(CPUINFO, "r");
> + if (file == NULL)
> + return cpuid;
> +
> + line = model = cpuid = NULL;
> + while (getline(&line, &line_sz, file) != -1) {
> + if (strncmp(line, CPUINFO_MODEL, strlen(CPUINFO_MODEL)))
> + continue;
> +
> + model = _get_field(line);
> + if (!model)
> + goto free;
> + break;
> + }
> +
> + if (model && (asprintf(&cpuid, "%s", model) < 0))
> + cpuid = NULL;
> +
> +free:
> + fclose(file);
> + free(model);
> + return cpuid;
> +}
> +
> +int get_cpuid(char *buffer, size_t sz)
> +{
> + char *cpuid = __get_cpuid();
> + int ret = 0;
> +
> + if (!cpuid)
> + return EINVAL;
> +
> + if (sz >= strlen(cpuid))
> + scnprintf(buffer, sz, "%s", cpuid);
> + else
> + ret = ENOBUFS;
> + free(cpuid);
> + return ret;
> +}
> +
> +char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
> +{
> + return __get_cpuid();
> +}
> diff --git a/tools/perf/arch/loongarch/util/kvm-stat.c b/tools/perf/arch/loongarch/util/kvm-stat.c
> new file mode 100644
> index 000000000000..cc50adb0835a
> --- /dev/null
> +++ b/tools/perf/arch/loongarch/util/kvm-stat.c
> @@ -0,0 +1,135 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <errno.h>
> +#include <memory.h>
> +#include <errno.h>
> +#include "util/kvm-stat.h"
> +#include "util/parse-events.h"
> +#include "util/debug.h"
> +#include "util/evsel.h"
> +#include "util/evlist.h"
> +#include "util/pmus.h"
> +
> +#define LOONGARCH_EXCEPTION_INT 0
> +#define LOONGARCH_EXCEPTION_PIL 1
> +#define LOONGARCH_EXCEPTION_PIS 2
> +#define LOONGARCH_EXCEPTION_PIF 3
> +#define LOONGARCH_EXCEPTION_PME 4
> +#define LOONGARCH_EXCEPTION_FPD 15
> +#define LOONGARCH_EXCEPTION_SXD 16
> +#define LOONGARCH_EXCEPTION_ASXD 17
> +#define LOONGARCH_EXCEPTION_GSPR 22
> +#define LOONGARCH_EXCEPTION_CPUCFG 100
> +#define LOONGARCH_EXCEPTION_CSR 101
> +#define LOONGARCH_EXCEPTION_IOCSR 102
> +#define LOONGARCH_EXCEPTION_IDLE 103
> +#define LOONGARCH_EXCEPTION_OTHERS 104
> +#define LOONGARCH_EXCEPTION_HVC 23
> +
> +#define loongarch_exception_type \
> + {LOONGARCH_EXCEPTION_INT, "Interrupt" }, \
> + {LOONGARCH_EXCEPTION_PIL, "Mem read" }, \
> + {LOONGARCH_EXCEPTION_PIS, "Mem store" }, \
> + {LOONGARCH_EXCEPTION_PIF, "Ifecth" }, \
> + {LOONGARCH_EXCEPTION_PME, "Mem modify" }, \
> + {LOONGARCH_EXCEPTION_FPD, "FPU" }, \
> + {LOONGARCH_EXCEPTION_SXD, "LSX" }, \
> + {LOONGARCH_EXCEPTION_ASXD, "LASX" }, \
> + {LOONGARCH_EXCEPTION_GSPR, "Privilege Error" }, \
> + {LOONGARCH_EXCEPTION_HVC, "Hypercall" }, \
> + {LOONGARCH_EXCEPTION_CPUCFG, "Cpucfg" }, \
> + {LOONGARCH_EXCEPTION_CSR, "Csr" }, \
> + {LOONGARCH_EXCEPTION_IOCSR, "Iocsr" }, \
> + {LOONGARCH_EXCEPTION_IDLE, "Idle" }, \
> + {LOONGARCH_EXCEPTION_OTHERS, "Others" }
> +
> +define_exit_reasons_table(loongarch_exit_reasons, loongarch_exception_type);
> +
> +const char *vcpu_id_str = "vcpu_id";
> +const char *kvm_exit_reason = "reason";
> +const char *kvm_entry_trace = "kvm:kvm_enter";
> +const char *kvm_reenter_trace = "kvm:kvm_reenter";
> +const char *kvm_exit_trace = "kvm:kvm_exit";
> +const char *kvm_events_tp[] = {
> + "kvm:kvm_enter",
> + "kvm:kvm_reenter",
> + "kvm:kvm_exit",
> + "kvm:kvm_exit_gspr",
> + NULL,
> +};
> +
> +static bool event_end(struct evsel *evsel,
> + struct perf_sample *sample __maybe_unused,
> + struct event_key *key __maybe_unused)
> +{
> + /*
> + * LoongArch kvm is different with other architectures
> + *
> + * There is kvm:kvm_reenter or kvm:kvm_enter event adjacent with
> + * kvm:kvm_exit event.
> + * kvm:kvm_reenter means returning to guest immediately
> + * kvm:kvm_enter means returning to vmm and then to guest
> + */
> + return evsel__name_is(evsel, kvm_entry_trace) ||
> + evsel__name_is(evsel, kvm_reenter_trace);
> +}
> +
> +static void event_gspr_get_key(struct evsel *evsel,
> + struct perf_sample *sample,
> + struct event_key *key)
> +{
> + unsigned int insn;
> +
> + key->key = LOONGARCH_EXCEPTION_OTHERS;
> + insn = evsel__intval(evsel, sample, "inst_word");
> + switch (insn >> 24) {
> + case 0:
> + /* cpucfg inst trap */
> + if ((insn >> 10) == 0x1b)
> + key->key = LOONGARCH_EXCEPTION_CPUCFG;
> + break;
> + case 4:
> + /* csr inst trap */
> + key->key = LOONGARCH_EXCEPTION_CSR;
> + break;
> + case 6:
> + /* iocsr inst trap */
> + if ((insn >> 15) == 0xc90)
> + key->key = LOONGARCH_EXCEPTION_IOCSR;
> + else if ((insn >> 15) == 0xc91)
> + /* idle inst trap */
> + key->key = LOONGARCH_EXCEPTION_IDLE;
> + break;
> + default:
> + key->key = LOONGARCH_EXCEPTION_OTHERS;
> + break;
> + }
> +}
> +
> +static struct child_event_ops child_events[] = {
> + { .name = "kvm:kvm_exit_gspr", .get_key = event_gspr_get_key },
> + { NULL, NULL },
> +};
> +
> +static struct kvm_events_ops exit_events = {
> + .is_begin_event = exit_event_begin,
> + .is_end_event = event_end,
> + .child_ops = child_events,
> + .decode_key = exit_event_decode_key,
> + .name = "VM-EXIT"
> +};
> +
> +struct kvm_reg_events_ops kvm_reg_events_ops[] = {
> + { .name = "vmexit", .ops = &exit_events, },
> + { NULL, NULL },
> +};
> +
> +const char * const kvm_skip_events[] = {
> + NULL,
> +};
> +
> +int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid __maybe_unused)
> +{
> + kvm->exit_reasons_isa = "loongarch64";
> + kvm->exit_reasons = loongarch_exit_reasons;
> + return 0;
> +}
>
> base-commit: 2c71fdf02a95b3dd425b42f28fd47fb2b1d22702
>


2024-05-07 06:17:34

by Bibo Mao

[permalink] [raw]
Subject: Re: [PATCH v3] perf kvm: Add kvm-stat for loongarch64

Hi Peter/Ian/Huacai,

This patch stays one month without response, I know you are busy and
look through thousands of patch every day.

Just one question, who has permission to merge perf kvm patch for
LoongArch. Is it LoongArch maintainer or perf kvm maintainer?

Regards
Bibo Mao


On 2024/4/11 下午8:10, Bibo Mao wrote:
> Add support for 'perf kvm stat' on loongarch64 platform, now only
> kvm exit event is supported.
>
> Here is example output about "perf kvm --host stat report" command
>
> Event name Samples Sample% Time (ns) Time% Mean Time (ns)
> Mem store 83969 51.00% 625697070 8.00% 7451
> Mem read 37641 22.00% 112485730 1.00% 2988
> Interrupt 15542 9.00% 20620190 0.00% 1326
> Iocsr 15207 9.00% 94296190 1.00% 6200
> Hypercall 4873 2.00% 12265280 0.00% 2516
> Idle 3713 2.00% 6322055860 87.00% 1702681
> FPU 1819 1.00% 2750300 0.00% 1511
> Ifecth 502 0.00% 1341740 0.00% 2672
> Mem modify 324 0.00% 602240 0.00% 1858
> Cpucfg 55 0.00% 77610 0.00% 1411
> Csr 12 0.00% 19690 0.00% 1640
> LASX 3 0.00% 4870 0.00% 1623
> LSX 2 0.00% 2100 0.00% 1050
>
> Signed-off-by: Bibo Mao <[email protected]>
> ---
> v2 --- v3:
> 1. Add NULL check with cpuid in function get_cpuid()
> 2. Add example output from /proc/cpuinfo before function get_cpuid()
> v1 --- v2:
> 1. Add child_ops for kvm exit event, split kvm:kvm_exit_gspr events
> into cpucfg/csr/iocsr/idle child events by decoding detailed gspr
> instruction.
> 2. Remove some exception code type which does not happen in current
> kvm implementation, such as meomry NR/NX/priviledge exception.
> ---
> tools/perf/arch/loongarch/Makefile | 1 +
> tools/perf/arch/loongarch/util/Build | 2 +
> tools/perf/arch/loongarch/util/header.c | 88 ++++++++++++++
> tools/perf/arch/loongarch/util/kvm-stat.c | 135 ++++++++++++++++++++++
> 4 files changed, 226 insertions(+)
> create mode 100644 tools/perf/arch/loongarch/util/header.c
> create mode 100644 tools/perf/arch/loongarch/util/kvm-stat.c
>
> diff --git a/tools/perf/arch/loongarch/Makefile b/tools/perf/arch/loongarch/Makefile
> index 3992a67a87d9..c89d6bb6b184 100644
> --- a/tools/perf/arch/loongarch/Makefile
> +++ b/tools/perf/arch/loongarch/Makefile
> @@ -4,6 +4,7 @@ PERF_HAVE_DWARF_REGS := 1
> endif
> PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
> PERF_HAVE_JITDUMP := 1
> +HAVE_KVM_STAT_SUPPORT := 1
>
> #
> # Syscall table generation for perf
> diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build
> index d776125a2d06..b12d374d7096 100644
> --- a/tools/perf/arch/loongarch/util/Build
> +++ b/tools/perf/arch/loongarch/util/Build
> @@ -1,5 +1,7 @@
> +perf-y += header.o
> perf-y += perf_regs.o
>
> perf-$(CONFIG_DWARF) += dwarf-regs.o
> perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
> perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> +perf-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
> diff --git a/tools/perf/arch/loongarch/util/header.c b/tools/perf/arch/loongarch/util/header.c
> new file mode 100644
> index 000000000000..a4ed732b49c6
> --- /dev/null
> +++ b/tools/perf/arch/loongarch/util/header.c
> @@ -0,0 +1,88 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Implementation of get_cpuid().
> + *
> + * Author: Nikita Shubin <[email protected]>
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <api/fs/fs.h>
> +#include <errno.h>
> +#include "util/debug.h"
> +#include "util/header.h"
> +
> +/*
> + * Output example from /proc/cpuinfo
> + * CPU Family : Loongson-64bit
> + * Model Name : Loongson-3C5000
> + * CPU Revision : 0x11
> + */
> +#define CPUINFO_MODEL "Model Name"
> +#define CPUINFO "/proc/cpuinfo"
> +static char *_get_field(const char *line)
> +{
> + char *line2, *nl;
> +
> + line2 = strrchr(line, ' ');
> + if (!line2)
> + return NULL;
> +
> + line2++;
> + nl = strrchr(line, '\n');
> + if (!nl)
> + return NULL;
> +
> + return strndup(line2, nl - line2);
> +}
> +
> +static char *__get_cpuid(void)
> +{
> + char *line, *model, *cpuid;
> + unsigned long line_sz;
> + FILE *file;
> +
> + file = fopen(CPUINFO, "r");
> + if (file == NULL)
> + return cpuid;
> +
> + line = model = cpuid = NULL;
> + while (getline(&line, &line_sz, file) != -1) {
> + if (strncmp(line, CPUINFO_MODEL, strlen(CPUINFO_MODEL)))
> + continue;
> +
> + model = _get_field(line);
> + if (!model)
> + goto free;
> + break;
> + }
> +
> + if (model && (asprintf(&cpuid, "%s", model) < 0))
> + cpuid = NULL;
> +
> +free:
> + fclose(file);
> + free(model);
> + return cpuid;
> +}
> +
> +int get_cpuid(char *buffer, size_t sz)
> +{
> + char *cpuid = __get_cpuid();
> + int ret = 0;
> +
> + if (!cpuid)
> + return EINVAL;
> +
> + if (sz >= strlen(cpuid))
> + scnprintf(buffer, sz, "%s", cpuid);
> + else
> + ret = ENOBUFS;
> + free(cpuid);
> + return ret;
> +}
> +
> +char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
> +{
> + return __get_cpuid();
> +}
> diff --git a/tools/perf/arch/loongarch/util/kvm-stat.c b/tools/perf/arch/loongarch/util/kvm-stat.c
> new file mode 100644
> index 000000000000..cc50adb0835a
> --- /dev/null
> +++ b/tools/perf/arch/loongarch/util/kvm-stat.c
> @@ -0,0 +1,135 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <errno.h>
> +#include <memory.h>
> +#include <errno.h>
> +#include "util/kvm-stat.h"
> +#include "util/parse-events.h"
> +#include "util/debug.h"
> +#include "util/evsel.h"
> +#include "util/evlist.h"
> +#include "util/pmus.h"
> +
> +#define LOONGARCH_EXCEPTION_INT 0
> +#define LOONGARCH_EXCEPTION_PIL 1
> +#define LOONGARCH_EXCEPTION_PIS 2
> +#define LOONGARCH_EXCEPTION_PIF 3
> +#define LOONGARCH_EXCEPTION_PME 4
> +#define LOONGARCH_EXCEPTION_FPD 15
> +#define LOONGARCH_EXCEPTION_SXD 16
> +#define LOONGARCH_EXCEPTION_ASXD 17
> +#define LOONGARCH_EXCEPTION_GSPR 22
> +#define LOONGARCH_EXCEPTION_CPUCFG 100
> +#define LOONGARCH_EXCEPTION_CSR 101
> +#define LOONGARCH_EXCEPTION_IOCSR 102
> +#define LOONGARCH_EXCEPTION_IDLE 103
> +#define LOONGARCH_EXCEPTION_OTHERS 104
> +#define LOONGARCH_EXCEPTION_HVC 23
> +
> +#define loongarch_exception_type \
> + {LOONGARCH_EXCEPTION_INT, "Interrupt" }, \
> + {LOONGARCH_EXCEPTION_PIL, "Mem read" }, \
> + {LOONGARCH_EXCEPTION_PIS, "Mem store" }, \
> + {LOONGARCH_EXCEPTION_PIF, "Ifecth" }, \
> + {LOONGARCH_EXCEPTION_PME, "Mem modify" }, \
> + {LOONGARCH_EXCEPTION_FPD, "FPU" }, \
> + {LOONGARCH_EXCEPTION_SXD, "LSX" }, \
> + {LOONGARCH_EXCEPTION_ASXD, "LASX" }, \
> + {LOONGARCH_EXCEPTION_GSPR, "Privilege Error" }, \
> + {LOONGARCH_EXCEPTION_HVC, "Hypercall" }, \
> + {LOONGARCH_EXCEPTION_CPUCFG, "Cpucfg" }, \
> + {LOONGARCH_EXCEPTION_CSR, "Csr" }, \
> + {LOONGARCH_EXCEPTION_IOCSR, "Iocsr" }, \
> + {LOONGARCH_EXCEPTION_IDLE, "Idle" }, \
> + {LOONGARCH_EXCEPTION_OTHERS, "Others" }
> +
> +define_exit_reasons_table(loongarch_exit_reasons, loongarch_exception_type);
> +
> +const char *vcpu_id_str = "vcpu_id";
> +const char *kvm_exit_reason = "reason";
> +const char *kvm_entry_trace = "kvm:kvm_enter";
> +const char *kvm_reenter_trace = "kvm:kvm_reenter";
> +const char *kvm_exit_trace = "kvm:kvm_exit";
> +const char *kvm_events_tp[] = {
> + "kvm:kvm_enter",
> + "kvm:kvm_reenter",
> + "kvm:kvm_exit",
> + "kvm:kvm_exit_gspr",
> + NULL,
> +};
> +
> +static bool event_end(struct evsel *evsel,
> + struct perf_sample *sample __maybe_unused,
> + struct event_key *key __maybe_unused)
> +{
> + /*
> + * LoongArch kvm is different with other architectures
> + *
> + * There is kvm:kvm_reenter or kvm:kvm_enter event adjacent with
> + * kvm:kvm_exit event.
> + * kvm:kvm_reenter means returning to guest immediately
> + * kvm:kvm_enter means returning to vmm and then to guest
> + */
> + return evsel__name_is(evsel, kvm_entry_trace) ||
> + evsel__name_is(evsel, kvm_reenter_trace);
> +}
> +
> +static void event_gspr_get_key(struct evsel *evsel,
> + struct perf_sample *sample,
> + struct event_key *key)
> +{
> + unsigned int insn;
> +
> + key->key = LOONGARCH_EXCEPTION_OTHERS;
> + insn = evsel__intval(evsel, sample, "inst_word");
> + switch (insn >> 24) {
> + case 0:
> + /* cpucfg inst trap */
> + if ((insn >> 10) == 0x1b)
> + key->key = LOONGARCH_EXCEPTION_CPUCFG;
> + break;
> + case 4:
> + /* csr inst trap */
> + key->key = LOONGARCH_EXCEPTION_CSR;
> + break;
> + case 6:
> + /* iocsr inst trap */
> + if ((insn >> 15) == 0xc90)
> + key->key = LOONGARCH_EXCEPTION_IOCSR;
> + else if ((insn >> 15) == 0xc91)
> + /* idle inst trap */
> + key->key = LOONGARCH_EXCEPTION_IDLE;
> + break;
> + default:
> + key->key = LOONGARCH_EXCEPTION_OTHERS;
> + break;
> + }
> +}
> +
> +static struct child_event_ops child_events[] = {
> + { .name = "kvm:kvm_exit_gspr", .get_key = event_gspr_get_key },
> + { NULL, NULL },
> +};
> +
> +static struct kvm_events_ops exit_events = {
> + .is_begin_event = exit_event_begin,
> + .is_end_event = event_end,
> + .child_ops = child_events,
> + .decode_key = exit_event_decode_key,
> + .name = "VM-EXIT"
> +};
> +
> +struct kvm_reg_events_ops kvm_reg_events_ops[] = {
> + { .name = "vmexit", .ops = &exit_events, },
> + { NULL, NULL },
> +};
> +
> +const char * const kvm_skip_events[] = {
> + NULL,
> +};
> +
> +int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid __maybe_unused)
> +{
> + kvm->exit_reasons_isa = "loongarch64";
> + kvm->exit_reasons = loongarch_exit_reasons;
> + return 0;
> +}
>
> base-commit: 2c71fdf02a95b3dd425b42f28fd47fb2b1d22702
>


2024-05-08 13:07:14

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH v3] perf kvm: Add kvm-stat for loongarch64

On Tue, May 7, 2024 at 2:11 PM maobibo <[email protected]> wrote:
>
> Hi Peter/Ian/Huacai,
>
> This patch stays one month without response, I know you are busy and
> look through thousands of patch every day.
>
> Just one question, who has permission to merge perf kvm patch for
> LoongArch. Is it LoongArch maintainer or perf kvm maintainer?
It should probably go to perf tree, but if it is allowed, maybe I can
try to merge into LoongArch tree.


Huacai
>
> Regards
> Bibo Mao
>
>
> On 2024/4/11 下午8:10, Bibo Mao wrote:
> > Add support for 'perf kvm stat' on loongarch64 platform, now only
> > kvm exit event is supported.
> >
> > Here is example output about "perf kvm --host stat report" command
> >
> > Event name Samples Sample% Time (ns) Time% Mean Time (ns)
> > Mem store 83969 51.00% 625697070 8.00% 7451
> > Mem read 37641 22.00% 112485730 1.00% 2988
> > Interrupt 15542 9.00% 20620190 0.00% 1326
> > Iocsr 15207 9.00% 94296190 1.00% 6200
> > Hypercall 4873 2.00% 12265280 0.00% 2516
> > Idle 3713 2.00% 6322055860 87.00% 1702681
> > FPU 1819 1.00% 2750300 0.00% 1511
> > Ifecth 502 0.00% 1341740 0.00% 2672
> > Mem modify 324 0.00% 602240 0.00% 1858
> > Cpucfg 55 0.00% 77610 0.00% 1411
> > Csr 12 0.00% 19690 0.00% 1640
> > LASX 3 0.00% 4870 0.00% 1623
> > LSX 2 0.00% 2100 0.00% 1050
> >
> > Signed-off-by: Bibo Mao <[email protected]>
> > ---
> > v2 --- v3:
> > 1. Add NULL check with cpuid in function get_cpuid()
> > 2. Add example output from /proc/cpuinfo before function get_cpuid()
> > v1 --- v2:
> > 1. Add child_ops for kvm exit event, split kvm:kvm_exit_gspr events
> > into cpucfg/csr/iocsr/idle child events by decoding detailed gspr
> > instruction.
> > 2. Remove some exception code type which does not happen in current
> > kvm implementation, such as meomry NR/NX/priviledge exception.
> > ---
> > tools/perf/arch/loongarch/Makefile | 1 +
> > tools/perf/arch/loongarch/util/Build | 2 +
> > tools/perf/arch/loongarch/util/header.c | 88 ++++++++++++++
> > tools/perf/arch/loongarch/util/kvm-stat.c | 135 ++++++++++++++++++++++
> > 4 files changed, 226 insertions(+)
> > create mode 100644 tools/perf/arch/loongarch/util/header.c
> > create mode 100644 tools/perf/arch/loongarch/util/kvm-stat.c
> >
> > diff --git a/tools/perf/arch/loongarch/Makefile b/tools/perf/arch/loongarch/Makefile
> > index 3992a67a87d9..c89d6bb6b184 100644
> > --- a/tools/perf/arch/loongarch/Makefile
> > +++ b/tools/perf/arch/loongarch/Makefile
> > @@ -4,6 +4,7 @@ PERF_HAVE_DWARF_REGS := 1
> > endif
> > PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
> > PERF_HAVE_JITDUMP := 1
> > +HAVE_KVM_STAT_SUPPORT := 1
> >
> > #
> > # Syscall table generation for perf
> > diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build
> > index d776125a2d06..b12d374d7096 100644
> > --- a/tools/perf/arch/loongarch/util/Build
> > +++ b/tools/perf/arch/loongarch/util/Build
> > @@ -1,5 +1,7 @@
> > +perf-y += header.o
> > perf-y += perf_regs.o
> >
> > perf-$(CONFIG_DWARF) += dwarf-regs.o
> > perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
> > perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> > +perf-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
> > diff --git a/tools/perf/arch/loongarch/util/header.c b/tools/perf/arch/loongarch/util/header.c
> > new file mode 100644
> > index 000000000000..a4ed732b49c6
> > --- /dev/null
> > +++ b/tools/perf/arch/loongarch/util/header.c
> > @@ -0,0 +1,88 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Implementation of get_cpuid().
> > + *
> > + * Author: Nikita Shubin <[email protected]>
> > + */
> > +
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <api/fs/fs.h>
> > +#include <errno.h>
> > +#include "util/debug.h"
> > +#include "util/header.h"
> > +
> > +/*
> > + * Output example from /proc/cpuinfo
> > + * CPU Family : Loongson-64bit
> > + * Model Name : Loongson-3C5000
> > + * CPU Revision : 0x11
> > + */
> > +#define CPUINFO_MODEL "Model Name"
> > +#define CPUINFO "/proc/cpuinfo"
> > +static char *_get_field(const char *line)
> > +{
> > + char *line2, *nl;
> > +
> > + line2 = strrchr(line, ' ');
> > + if (!line2)
> > + return NULL;
> > +
> > + line2++;
> > + nl = strrchr(line, '\n');
> > + if (!nl)
> > + return NULL;
> > +
> > + return strndup(line2, nl - line2);
> > +}
> > +
> > +static char *__get_cpuid(void)
> > +{
> > + char *line, *model, *cpuid;
> > + unsigned long line_sz;
> > + FILE *file;
> > +
> > + file = fopen(CPUINFO, "r");
> > + if (file == NULL)
> > + return cpuid;
> > +
> > + line = model = cpuid = NULL;
> > + while (getline(&line, &line_sz, file) != -1) {
> > + if (strncmp(line, CPUINFO_MODEL, strlen(CPUINFO_MODEL)))
> > + continue;
> > +
> > + model = _get_field(line);
> > + if (!model)
> > + goto free;
> > + break;
> > + }
> > +
> > + if (model && (asprintf(&cpuid, "%s", model) < 0))
> > + cpuid = NULL;
> > +
> > +free:
> > + fclose(file);
> > + free(model);
> > + return cpuid;
> > +}
> > +
> > +int get_cpuid(char *buffer, size_t sz)
> > +{
> > + char *cpuid = __get_cpuid();
> > + int ret = 0;
> > +
> > + if (!cpuid)
> > + return EINVAL;
> > +
> > + if (sz >= strlen(cpuid))
> > + scnprintf(buffer, sz, "%s", cpuid);
> > + else
> > + ret = ENOBUFS;
> > + free(cpuid);
> > + return ret;
> > +}
> > +
> > +char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
> > +{
> > + return __get_cpuid();
> > +}
> > diff --git a/tools/perf/arch/loongarch/util/kvm-stat.c b/tools/perf/arch/loongarch/util/kvm-stat.c
> > new file mode 100644
> > index 000000000000..cc50adb0835a
> > --- /dev/null
> > +++ b/tools/perf/arch/loongarch/util/kvm-stat.c
> > @@ -0,0 +1,135 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +#include <errno.h>
> > +#include <memory.h>
> > +#include <errno.h>
> > +#include "util/kvm-stat.h"
> > +#include "util/parse-events.h"
> > +#include "util/debug.h"
> > +#include "util/evsel.h"
> > +#include "util/evlist.h"
> > +#include "util/pmus.h"
> > +
> > +#define LOONGARCH_EXCEPTION_INT 0
> > +#define LOONGARCH_EXCEPTION_PIL 1
> > +#define LOONGARCH_EXCEPTION_PIS 2
> > +#define LOONGARCH_EXCEPTION_PIF 3
> > +#define LOONGARCH_EXCEPTION_PME 4
> > +#define LOONGARCH_EXCEPTION_FPD 15
> > +#define LOONGARCH_EXCEPTION_SXD 16
> > +#define LOONGARCH_EXCEPTION_ASXD 17
> > +#define LOONGARCH_EXCEPTION_GSPR 22
> > +#define LOONGARCH_EXCEPTION_CPUCFG 100
> > +#define LOONGARCH_EXCEPTION_CSR 101
> > +#define LOONGARCH_EXCEPTION_IOCSR 102
> > +#define LOONGARCH_EXCEPTION_IDLE 103
> > +#define LOONGARCH_EXCEPTION_OTHERS 104
> > +#define LOONGARCH_EXCEPTION_HVC 23
> > +
> > +#define loongarch_exception_type \
> > + {LOONGARCH_EXCEPTION_INT, "Interrupt" }, \
> > + {LOONGARCH_EXCEPTION_PIL, "Mem read" }, \
> > + {LOONGARCH_EXCEPTION_PIS, "Mem store" }, \
> > + {LOONGARCH_EXCEPTION_PIF, "Ifecth" }, \
> > + {LOONGARCH_EXCEPTION_PME, "Mem modify" }, \
> > + {LOONGARCH_EXCEPTION_FPD, "FPU" }, \
> > + {LOONGARCH_EXCEPTION_SXD, "LSX" }, \
> > + {LOONGARCH_EXCEPTION_ASXD, "LASX" }, \
> > + {LOONGARCH_EXCEPTION_GSPR, "Privilege Error" }, \
> > + {LOONGARCH_EXCEPTION_HVC, "Hypercall" }, \
> > + {LOONGARCH_EXCEPTION_CPUCFG, "Cpucfg" }, \
> > + {LOONGARCH_EXCEPTION_CSR, "Csr" }, \
> > + {LOONGARCH_EXCEPTION_IOCSR, "Iocsr" }, \
> > + {LOONGARCH_EXCEPTION_IDLE, "Idle" }, \
> > + {LOONGARCH_EXCEPTION_OTHERS, "Others" }
> > +
> > +define_exit_reasons_table(loongarch_exit_reasons, loongarch_exception_type);
> > +
> > +const char *vcpu_id_str = "vcpu_id";
> > +const char *kvm_exit_reason = "reason";
> > +const char *kvm_entry_trace = "kvm:kvm_enter";
> > +const char *kvm_reenter_trace = "kvm:kvm_reenter";
> > +const char *kvm_exit_trace = "kvm:kvm_exit";
> > +const char *kvm_events_tp[] = {
> > + "kvm:kvm_enter",
> > + "kvm:kvm_reenter",
> > + "kvm:kvm_exit",
> > + "kvm:kvm_exit_gspr",
> > + NULL,
> > +};
> > +
> > +static bool event_end(struct evsel *evsel,
> > + struct perf_sample *sample __maybe_unused,
> > + struct event_key *key __maybe_unused)
> > +{
> > + /*
> > + * LoongArch kvm is different with other architectures
> > + *
> > + * There is kvm:kvm_reenter or kvm:kvm_enter event adjacent with
> > + * kvm:kvm_exit event.
> > + * kvm:kvm_reenter means returning to guest immediately
> > + * kvm:kvm_enter means returning to vmm and then to guest
> > + */
> > + return evsel__name_is(evsel, kvm_entry_trace) ||
> > + evsel__name_is(evsel, kvm_reenter_trace);
> > +}
> > +
> > +static void event_gspr_get_key(struct evsel *evsel,
> > + struct perf_sample *sample,
> > + struct event_key *key)
> > +{
> > + unsigned int insn;
> > +
> > + key->key = LOONGARCH_EXCEPTION_OTHERS;
> > + insn = evsel__intval(evsel, sample, "inst_word");
> > + switch (insn >> 24) {
> > + case 0:
> > + /* cpucfg inst trap */
> > + if ((insn >> 10) == 0x1b)
> > + key->key = LOONGARCH_EXCEPTION_CPUCFG;
> > + break;
> > + case 4:
> > + /* csr inst trap */
> > + key->key = LOONGARCH_EXCEPTION_CSR;
> > + break;
> > + case 6:
> > + /* iocsr inst trap */
> > + if ((insn >> 15) == 0xc90)
> > + key->key = LOONGARCH_EXCEPTION_IOCSR;
> > + else if ((insn >> 15) == 0xc91)
> > + /* idle inst trap */
> > + key->key = LOONGARCH_EXCEPTION_IDLE;
> > + break;
> > + default:
> > + key->key = LOONGARCH_EXCEPTION_OTHERS;
> > + break;
> > + }
> > +}
> > +
> > +static struct child_event_ops child_events[] = {
> > + { .name = "kvm:kvm_exit_gspr", .get_key = event_gspr_get_key },
> > + { NULL, NULL },
> > +};
> > +
> > +static struct kvm_events_ops exit_events = {
> > + .is_begin_event = exit_event_begin,
> > + .is_end_event = event_end,
> > + .child_ops = child_events,
> > + .decode_key = exit_event_decode_key,
> > + .name = "VM-EXIT"
> > +};
> > +
> > +struct kvm_reg_events_ops kvm_reg_events_ops[] = {
> > + { .name = "vmexit", .ops = &exit_events, },
> > + { NULL, NULL },
> > +};
> > +
> > +const char * const kvm_skip_events[] = {
> > + NULL,
> > +};
> > +
> > +int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid __maybe_unused)
> > +{
> > + kvm->exit_reasons_isa = "loongarch64";
> > + kvm->exit_reasons = loongarch_exit_reasons;
> > + return 0;
> > +}
> >
> > base-commit: 2c71fdf02a95b3dd425b42f28fd47fb2b1d22702
> >
>

2024-05-10 01:57:16

by Bibo Mao

[permalink] [raw]
Subject: Re: [PATCH v3] perf kvm: Add kvm-stat for loongarch64



On 2024/5/8 下午9:02, Huacai Chen wrote:
> On Tue, May 7, 2024 at 2:11 PM maobibo <[email protected]> wrote:
>>
>> Hi Peter/Ian/Huacai,
>>
>> This patch stays one month without response, I know you are busy and
>> look through thousands of patch every day.
>>
>> Just one question, who has permission to merge perf kvm patch for
>> LoongArch. Is it LoongArch maintainer or perf kvm maintainer?
> It should probably go to perf tree, but if it is allowed, maybe I can
> try to merge into LoongArch tree.

tools/perf/arch/loongarch/Makefile | 1 +
tools/perf/arch/loongarch/util/Build | 2 +
tools/perf/arch/loongarch/util/header.c | 88 ++++++++++++++
tools/perf/arch/loongarch/util/kvm-stat.c | 135 ++++++++++++++++++++++

All the modification is LoongArch specific, there will be much
appreciated if it can reviewed and merged.

Regards
Bibo Mao

>
>
> Huacai
>>
>> Regards
>> Bibo Mao
>>
>>
>> On 2024/4/11 下午8:10, Bibo Mao wrote:
>>> Add support for 'perf kvm stat' on loongarch64 platform, now only
>>> kvm exit event is supported.
>>>
>>> Here is example output about "perf kvm --host stat report" command
>>>
>>> Event name Samples Sample% Time (ns) Time% Mean Time (ns)
>>> Mem store 83969 51.00% 625697070 8.00% 7451
>>> Mem read 37641 22.00% 112485730 1.00% 2988
>>> Interrupt 15542 9.00% 20620190 0.00% 1326
>>> Iocsr 15207 9.00% 94296190 1.00% 6200
>>> Hypercall 4873 2.00% 12265280 0.00% 2516
>>> Idle 3713 2.00% 6322055860 87.00% 1702681
>>> FPU 1819 1.00% 2750300 0.00% 1511
>>> Ifecth 502 0.00% 1341740 0.00% 2672
>>> Mem modify 324 0.00% 602240 0.00% 1858
>>> Cpucfg 55 0.00% 77610 0.00% 1411
>>> Csr 12 0.00% 19690 0.00% 1640
>>> LASX 3 0.00% 4870 0.00% 1623
>>> LSX 2 0.00% 2100 0.00% 1050
>>>
>>> Signed-off-by: Bibo Mao <[email protected]>
>>> ---
>>> v2 --- v3:
>>> 1. Add NULL check with cpuid in function get_cpuid()
>>> 2. Add example output from /proc/cpuinfo before function get_cpuid()
>>> v1 --- v2:
>>> 1. Add child_ops for kvm exit event, split kvm:kvm_exit_gspr events
>>> into cpucfg/csr/iocsr/idle child events by decoding detailed gspr
>>> instruction.
>>> 2. Remove some exception code type which does not happen in current
>>> kvm implementation, such as meomry NR/NX/priviledge exception.
>>> ---
>>> tools/perf/arch/loongarch/Makefile | 1 +
>>> tools/perf/arch/loongarch/util/Build | 2 +
>>> tools/perf/arch/loongarch/util/header.c | 88 ++++++++++++++
>>> tools/perf/arch/loongarch/util/kvm-stat.c | 135 ++++++++++++++++++++++
>>> 4 files changed, 226 insertions(+)
>>> create mode 100644 tools/perf/arch/loongarch/util/header.c
>>> create mode 100644 tools/perf/arch/loongarch/util/kvm-stat.c
>>>
>>> diff --git a/tools/perf/arch/loongarch/Makefile b/tools/perf/arch/loongarch/Makefile
>>> index 3992a67a87d9..c89d6bb6b184 100644
>>> --- a/tools/perf/arch/loongarch/Makefile
>>> +++ b/tools/perf/arch/loongarch/Makefile
>>> @@ -4,6 +4,7 @@ PERF_HAVE_DWARF_REGS := 1
>>> endif
>>> PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
>>> PERF_HAVE_JITDUMP := 1
>>> +HAVE_KVM_STAT_SUPPORT := 1
>>>
>>> #
>>> # Syscall table generation for perf
>>> diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build
>>> index d776125a2d06..b12d374d7096 100644
>>> --- a/tools/perf/arch/loongarch/util/Build
>>> +++ b/tools/perf/arch/loongarch/util/Build
>>> @@ -1,5 +1,7 @@
>>> +perf-y += header.o
>>> perf-y += perf_regs.o
>>>
>>> perf-$(CONFIG_DWARF) += dwarf-regs.o
>>> perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
>>> perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
>>> +perf-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
>>> diff --git a/tools/perf/arch/loongarch/util/header.c b/tools/perf/arch/loongarch/util/header.c
>>> new file mode 100644
>>> index 000000000000..a4ed732b49c6
>>> --- /dev/null
>>> +++ b/tools/perf/arch/loongarch/util/header.c
>>> @@ -0,0 +1,88 @@
>>> +// SPDX-License-Identifier: GPL-2.0-only
>>> +/*
>>> + * Implementation of get_cpuid().
>>> + *
>>> + * Author: Nikita Shubin <[email protected]>
>>> + */
>>> +
>>> +#include <stdio.h>
>>> +#include <stdlib.h>
>>> +#include <api/fs/fs.h>
>>> +#include <errno.h>
>>> +#include "util/debug.h"
>>> +#include "util/header.h"
>>> +
>>> +/*
>>> + * Output example from /proc/cpuinfo
>>> + * CPU Family : Loongson-64bit
>>> + * Model Name : Loongson-3C5000
>>> + * CPU Revision : 0x11
>>> + */
>>> +#define CPUINFO_MODEL "Model Name"
>>> +#define CPUINFO "/proc/cpuinfo"
>>> +static char *_get_field(const char *line)
>>> +{
>>> + char *line2, *nl;
>>> +
>>> + line2 = strrchr(line, ' ');
>>> + if (!line2)
>>> + return NULL;
>>> +
>>> + line2++;
>>> + nl = strrchr(line, '\n');
>>> + if (!nl)
>>> + return NULL;
>>> +
>>> + return strndup(line2, nl - line2);
>>> +}
>>> +
>>> +static char *__get_cpuid(void)
>>> +{
>>> + char *line, *model, *cpuid;
>>> + unsigned long line_sz;
>>> + FILE *file;
>>> +
>>> + file = fopen(CPUINFO, "r");
>>> + if (file == NULL)
>>> + return cpuid;
>>> +
>>> + line = model = cpuid = NULL;
>>> + while (getline(&line, &line_sz, file) != -1) {
>>> + if (strncmp(line, CPUINFO_MODEL, strlen(CPUINFO_MODEL)))
>>> + continue;
>>> +
>>> + model = _get_field(line);
>>> + if (!model)
>>> + goto free;
>>> + break;
>>> + }
>>> +
>>> + if (model && (asprintf(&cpuid, "%s", model) < 0))
>>> + cpuid = NULL;
>>> +
>>> +free:
>>> + fclose(file);
>>> + free(model);
>>> + return cpuid;
>>> +}
>>> +
>>> +int get_cpuid(char *buffer, size_t sz)
>>> +{
>>> + char *cpuid = __get_cpuid();
>>> + int ret = 0;
>>> +
>>> + if (!cpuid)
>>> + return EINVAL;
>>> +
>>> + if (sz >= strlen(cpuid))
>>> + scnprintf(buffer, sz, "%s", cpuid);
>>> + else
>>> + ret = ENOBUFS;
>>> + free(cpuid);
>>> + return ret;
>>> +}
>>> +
>>> +char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
>>> +{
>>> + return __get_cpuid();
>>> +}
>>> diff --git a/tools/perf/arch/loongarch/util/kvm-stat.c b/tools/perf/arch/loongarch/util/kvm-stat.c
>>> new file mode 100644
>>> index 000000000000..cc50adb0835a
>>> --- /dev/null
>>> +++ b/tools/perf/arch/loongarch/util/kvm-stat.c
>>> @@ -0,0 +1,135 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +#include <errno.h>
>>> +#include <memory.h>
>>> +#include <errno.h>
>>> +#include "util/kvm-stat.h"
>>> +#include "util/parse-events.h"
>>> +#include "util/debug.h"
>>> +#include "util/evsel.h"
>>> +#include "util/evlist.h"
>>> +#include "util/pmus.h"
>>> +
>>> +#define LOONGARCH_EXCEPTION_INT 0
>>> +#define LOONGARCH_EXCEPTION_PIL 1
>>> +#define LOONGARCH_EXCEPTION_PIS 2
>>> +#define LOONGARCH_EXCEPTION_PIF 3
>>> +#define LOONGARCH_EXCEPTION_PME 4
>>> +#define LOONGARCH_EXCEPTION_FPD 15
>>> +#define LOONGARCH_EXCEPTION_SXD 16
>>> +#define LOONGARCH_EXCEPTION_ASXD 17
>>> +#define LOONGARCH_EXCEPTION_GSPR 22
>>> +#define LOONGARCH_EXCEPTION_CPUCFG 100
>>> +#define LOONGARCH_EXCEPTION_CSR 101
>>> +#define LOONGARCH_EXCEPTION_IOCSR 102
>>> +#define LOONGARCH_EXCEPTION_IDLE 103
>>> +#define LOONGARCH_EXCEPTION_OTHERS 104
>>> +#define LOONGARCH_EXCEPTION_HVC 23
>>> +
>>> +#define loongarch_exception_type \
>>> + {LOONGARCH_EXCEPTION_INT, "Interrupt" }, \
>>> + {LOONGARCH_EXCEPTION_PIL, "Mem read" }, \
>>> + {LOONGARCH_EXCEPTION_PIS, "Mem store" }, \
>>> + {LOONGARCH_EXCEPTION_PIF, "Ifecth" }, \
>>> + {LOONGARCH_EXCEPTION_PME, "Mem modify" }, \
>>> + {LOONGARCH_EXCEPTION_FPD, "FPU" }, \
>>> + {LOONGARCH_EXCEPTION_SXD, "LSX" }, \
>>> + {LOONGARCH_EXCEPTION_ASXD, "LASX" }, \
>>> + {LOONGARCH_EXCEPTION_GSPR, "Privilege Error" }, \
>>> + {LOONGARCH_EXCEPTION_HVC, "Hypercall" }, \
>>> + {LOONGARCH_EXCEPTION_CPUCFG, "Cpucfg" }, \
>>> + {LOONGARCH_EXCEPTION_CSR, "Csr" }, \
>>> + {LOONGARCH_EXCEPTION_IOCSR, "Iocsr" }, \
>>> + {LOONGARCH_EXCEPTION_IDLE, "Idle" }, \
>>> + {LOONGARCH_EXCEPTION_OTHERS, "Others" }
>>> +
>>> +define_exit_reasons_table(loongarch_exit_reasons, loongarch_exception_type);
>>> +
>>> +const char *vcpu_id_str = "vcpu_id";
>>> +const char *kvm_exit_reason = "reason";
>>> +const char *kvm_entry_trace = "kvm:kvm_enter";
>>> +const char *kvm_reenter_trace = "kvm:kvm_reenter";
>>> +const char *kvm_exit_trace = "kvm:kvm_exit";
>>> +const char *kvm_events_tp[] = {
>>> + "kvm:kvm_enter",
>>> + "kvm:kvm_reenter",
>>> + "kvm:kvm_exit",
>>> + "kvm:kvm_exit_gspr",
>>> + NULL,
>>> +};
>>> +
>>> +static bool event_end(struct evsel *evsel,
>>> + struct perf_sample *sample __maybe_unused,
>>> + struct event_key *key __maybe_unused)
>>> +{
>>> + /*
>>> + * LoongArch kvm is different with other architectures
>>> + *
>>> + * There is kvm:kvm_reenter or kvm:kvm_enter event adjacent with
>>> + * kvm:kvm_exit event.
>>> + * kvm:kvm_reenter means returning to guest immediately
>>> + * kvm:kvm_enter means returning to vmm and then to guest
>>> + */
>>> + return evsel__name_is(evsel, kvm_entry_trace) ||
>>> + evsel__name_is(evsel, kvm_reenter_trace);
>>> +}
>>> +
>>> +static void event_gspr_get_key(struct evsel *evsel,
>>> + struct perf_sample *sample,
>>> + struct event_key *key)
>>> +{
>>> + unsigned int insn;
>>> +
>>> + key->key = LOONGARCH_EXCEPTION_OTHERS;
>>> + insn = evsel__intval(evsel, sample, "inst_word");
>>> + switch (insn >> 24) {
>>> + case 0:
>>> + /* cpucfg inst trap */
>>> + if ((insn >> 10) == 0x1b)
>>> + key->key = LOONGARCH_EXCEPTION_CPUCFG;
>>> + break;
>>> + case 4:
>>> + /* csr inst trap */
>>> + key->key = LOONGARCH_EXCEPTION_CSR;
>>> + break;
>>> + case 6:
>>> + /* iocsr inst trap */
>>> + if ((insn >> 15) == 0xc90)
>>> + key->key = LOONGARCH_EXCEPTION_IOCSR;
>>> + else if ((insn >> 15) == 0xc91)
>>> + /* idle inst trap */
>>> + key->key = LOONGARCH_EXCEPTION_IDLE;
>>> + break;
>>> + default:
>>> + key->key = LOONGARCH_EXCEPTION_OTHERS;
>>> + break;
>>> + }
>>> +}
>>> +
>>> +static struct child_event_ops child_events[] = {
>>> + { .name = "kvm:kvm_exit_gspr", .get_key = event_gspr_get_key },
>>> + { NULL, NULL },
>>> +};
>>> +
>>> +static struct kvm_events_ops exit_events = {
>>> + .is_begin_event = exit_event_begin,
>>> + .is_end_event = event_end,
>>> + .child_ops = child_events,
>>> + .decode_key = exit_event_decode_key,
>>> + .name = "VM-EXIT"
>>> +};
>>> +
>>> +struct kvm_reg_events_ops kvm_reg_events_ops[] = {
>>> + { .name = "vmexit", .ops = &exit_events, },
>>> + { NULL, NULL },
>>> +};
>>> +
>>> +const char * const kvm_skip_events[] = {
>>> + NULL,
>>> +};
>>> +
>>> +int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid __maybe_unused)
>>> +{
>>> + kvm->exit_reasons_isa = "loongarch64";
>>> + kvm->exit_reasons = loongarch_exit_reasons;
>>> + return 0;
>>> +}
>>>
>>> base-commit: 2c71fdf02a95b3dd425b42f28fd47fb2b1d22702
>>>
>>