All of lore.kernel.org
 help / color / mirror / Atom feed
From: Bixuan Cui <cuibixuan@huawei.com>
To: <peterz@infradead.org>, <mingo@redhat.com>, <acme@kernel.org>,
	<mark.rutland@arm.com>, <alexander.shishkin@linux.intel.com>,
	<jolsa@redhat.com>, <namhyung@kernel.org>,
	<linux-kernel@vger.kernel.org>
Cc: <hagen@jauu.net>, <john.wanghui@huawei.com>,
	<abudankov@huawei.com>, <yz.yuzhou@huawei.com>
Subject: [PATCH v5 1/2] perf tools: add 'perf irq' to measure the hardware interrupts
Date: Thu, 25 Feb 2021 09:30:15 +0800	[thread overview]
Message-ID: <20210225013016.67100-2-cuibixuan@huawei.com> (raw)
In-Reply-To: <20210225013016.67100-1-cuibixuan@huawei.com>

Add 'perf irq' to trace/measure the hardware interrupts.

Now three functions are provided:
  1. 'perf irq record <command>' to record the irq handler events.
  2. 'perf irq script' to see a detailed trace of the workload that
   was recorded.
  3. 'perf irq report' to calculate the time consumed by each
   hardware interrupt processing function.

Signed-off-by: Bixuan Cui <cuibixuan@huawei.com>
---
 tools/perf/Build         |   1 +
 tools/perf/builtin-irq.c | 260 +++++++++++++++++++++++++++++++++++++++
 tools/perf/builtin.h     |   1 +
 tools/perf/perf.c        |   1 +
 4 files changed, 263 insertions(+)
 create mode 100644 tools/perf/builtin-irq.c

diff --git a/tools/perf/Build b/tools/perf/Build
index db61dbe2b543..ad8e3c19bb03 100644
--- a/tools/perf/Build
+++ b/tools/perf/Build
@@ -25,6 +25,7 @@ perf-y += builtin-data.o
 perf-y += builtin-version.o
 perf-y += builtin-c2c.o
 perf-y += builtin-daemon.o
+perf-y += builtin-irq.o
 
 perf-$(CONFIG_TRACE) += builtin-trace.o
 perf-$(CONFIG_LIBELF) += builtin-probe.o
diff --git a/tools/perf/builtin-irq.c b/tools/perf/builtin-irq.c
new file mode 100644
index 000000000000..bf1e6efd85f8
--- /dev/null
+++ b/tools/perf/builtin-irq.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "builtin.h"
+#include "perf.h"
+#include "perf-sys.h"
+
+#include "util/cpumap.h"
+#include "util/evlist.h"
+#include "util/evsel.h"
+#include "util/evsel_fprintf.h"
+#include "util/symbol.h"
+#include "util/thread.h"
+#include "util/header.h"
+#include "util/session.h"
+#include "util/tool.h"
+#include "util/cloexec.h"
+#include "util/thread_map.h"
+#include "util/color.h"
+#include "util/stat.h"
+#include "util/string2.h"
+#include "util/callchain.h"
+#include "util/time-utils.h"
+
+#include <subcmd/pager.h>
+#include <subcmd/parse-options.h>
+#include "util/trace-event.h"
+
+#include "util/debug.h"
+#include "util/event.h"
+
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/zalloc.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <inttypes.h>
+
+#include <errno.h>
+#include <semaphore.h>
+#include <pthread.h>
+#include <math.h>
+#include <api/fs/fs.h>
+#include <perf/cpumap.h>
+#include <linux/time64.h>
+#include <linux/err.h>
+
+#include <linux/ctype.h>
+
+#define IRQ_NAME_LEN		24
+#define MAX_CPUS		4096
+
+static const char *cpu_list;
+static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
+
+struct perf_irq {
+	struct perf_tool tool;
+	bool force;
+
+	char irq_name[MAX_CPUS][IRQ_NAME_LEN];
+	u32 irq_num[MAX_CPUS];
+	u64 irq_entry_time[MAX_CPUS];
+	u64 irq_exit_time[MAX_CPUS];
+};
+
+typedef int (*irq_handler)(struct perf_tool *tool,
+			  struct evsel *evsel,
+			  struct perf_sample *sample);
+
+static int perf_report_process_sample(struct perf_tool *tool,
+					 union perf_event *event __maybe_unused,
+					 struct perf_sample *sample,
+					 struct evsel *evsel,
+					 struct machine *machine __maybe_unused)
+{
+	int err = 0;
+
+	if (evsel->handler != NULL) {
+		irq_handler f = evsel->handler;
+		err = f(tool, evsel, sample);
+	}
+
+	return err;
+}
+
+static void output_report(struct perf_irq *irq, u32 cpu)
+{
+	int ret, i;
+	char irq_entry_time[30], irq_exit_time[30], irq_diff[30];
+
+	timestamp__scnprintf_usec(irq->irq_entry_time[cpu],
+				  irq_entry_time, sizeof(irq_entry_time));
+	timestamp__scnprintf_usec(irq->irq_exit_time[cpu],
+				  irq_exit_time, sizeof(irq_exit_time));
+	timestamp__scnprintf_usec(irq->irq_exit_time[cpu] - irq->irq_entry_time[cpu],
+				  irq_diff, sizeof(irq_diff));
+
+	ret = printf("   %s ", irq->irq_name[cpu]);
+	for (i = 0; i < IRQ_NAME_LEN - ret; i++)
+		printf(" ");
+
+	printf("| [%04d] | %13s s | %16s s | %16s s\n",
+		cpu, irq_diff, irq_entry_time, irq_exit_time);
+}
+
+static int report_irq_handler_entry_event(struct perf_tool *tool,
+				      struct evsel *evsel,
+				      struct perf_sample *sample)
+{
+	int this_cpu = sample->cpu, err = 0;
+	struct perf_irq *irq = container_of(tool, struct perf_irq, tool);
+	const char *name = evsel__strval(evsel, sample, "name");
+
+	irq->irq_entry_time[this_cpu] = sample->time;
+
+	strncpy(irq->irq_name[this_cpu], name, IRQ_NAME_LEN - 1);
+	irq->irq_name[this_cpu][IRQ_NAME_LEN - 1] = '\0';
+
+	return err;
+}
+
+static int report_irq_handler_exit_event(struct perf_tool *tool,
+				      struct evsel *evsel __maybe_unused,
+				      struct perf_sample *sample)
+{
+	int this_cpu = sample->cpu, err = 0;
+	struct perf_irq *irq = container_of(tool, struct perf_irq, tool);
+
+	irq->irq_exit_time[this_cpu] = sample->time;
+
+	if (cpu_list && !test_bit(this_cpu, cpu_bitmap))
+		return err;
+
+	output_report(irq, this_cpu);
+
+	return err;
+}
+
+static int perf_irq__read_events(struct perf_irq *irq)
+{
+	struct evsel_str_handler handlers[] = {
+		{ "irq:irq_handler_entry",	report_irq_handler_entry_event, },
+		{ "irq:irq_handler_exit",	report_irq_handler_exit_event, },
+	};
+	struct perf_session *session;
+	struct perf_data data = {
+		.path  = input_name,
+		.mode  = PERF_DATA_MODE_READ,
+		.force = irq->force,
+	};
+	int rc = -1;
+
+	irq->tool.sample = perf_report_process_sample;
+
+	session = perf_session__new(&data, false, &irq->tool);
+	if (IS_ERR(session)) {
+		pr_debug("Error creating perf session");
+		return PTR_ERR(session);
+	}
+
+	if (cpu_list) {
+		rc = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap);
+		if (rc < 0)
+			goto out_delete;
+	}
+
+	if (perf_session__set_tracepoints_handlers(session, handlers))
+		goto out_delete;
+
+	rc = perf_session__process_events(session);
+	if (rc) {
+		pr_err("Failed to process events, error %d", rc);
+		goto out_delete;
+	}
+
+out_delete:
+	perf_session__delete(session);
+	return rc;
+}
+
+static int irq_report(struct perf_irq *irq)
+{
+	printf("  -------------------------------------------------------------------------------------------------------------------------------------------\n");
+	printf("   Irq name             |  CPU   | Time consume us | Handler entry time | Handler exit time \n");
+	printf("  -------------------------------------------------------------------------------------------------------------------------------------------\n");
+
+	if (perf_irq__read_events(irq))
+		return -1;
+
+	return 0;
+}
+
+static int __cmd_record(int argc, const char **argv)
+{
+	unsigned int rec_argc, i, j;
+	const char **rec_argv;
+	const char * const record_args[] = {
+		"record",
+		"-a",
+		"-R",
+		"-c", "1",
+		"-e", "irq:irq_handler_entry",
+		"-e", "irq:irq_handler_exit",
+	};
+
+	rec_argc = ARRAY_SIZE(record_args) + argc - 1;
+	rec_argv = calloc(rec_argc + 1, sizeof(char *));
+	if (rec_argv == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(record_args); i++)
+		rec_argv[i] = strdup(record_args[i]);
+
+	for (j = 1; j < (unsigned int)argc; j++, i++)
+		rec_argv[i] = argv[j];
+
+	BUG_ON(i != rec_argc);
+
+	return cmd_record(i, rec_argv);
+}
+
+int cmd_irq(int argc, const char **argv)
+{
+	struct perf_irq irq = {
+		.force	= false,
+	};
+
+	const struct option irq_options[] = {
+	OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
+	OPT_END()
+	};
+	const struct option report_options[] = {
+	OPT_PARENT(irq_options)
+	};
+
+	const char * const report_usage[] = {
+		"perf irq report [<options>]",
+		NULL
+	};
+	const char *const irq_subcommands[] = { "record", "report", NULL };
+	const char *irq_usage[] = {
+		NULL,
+		NULL
+	};
+
+	argc = parse_options_subcommand(argc, argv, irq_options, irq_subcommands,
+					irq_usage, PARSE_OPT_STOP_AT_NON_OPTION);
+	if (!argc)
+		usage_with_options(irq_usage, irq_options);
+
+	if (!strncmp(argv[0], "record", 6)) {
+		return __cmd_record(argc, argv);
+	} else if (!strncmp(argv[0], "report", 6)) {
+		if (argc > 1) {
+			argc = parse_options(argc, argv, report_options, report_usage, 0);
+			if (argc)
+				usage_with_options(report_usage, report_options);
+		}
+		return irq_report(&irq);
+	}
+	return 0;
+}
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
index 7303e80a639c..fab1483ad98d 100644
--- a/tools/perf/builtin.h
+++ b/tools/perf/builtin.h
@@ -38,6 +38,7 @@ int cmd_mem(int argc, const char **argv);
 int cmd_data(int argc, const char **argv);
 int cmd_ftrace(int argc, const char **argv);
 int cmd_daemon(int argc, const char **argv);
+int cmd_irq(int argc, const char **argv);
 
 int find_scripts(char **scripts_array, char **scripts_path_array, int num,
 		 int pathlen);
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 20cb91ef06ff..347d2f55e59a 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -89,6 +89,7 @@ static struct cmd_struct commands[] = {
 	{ "data",	cmd_data,	0 },
 	{ "ftrace",	cmd_ftrace,	0 },
 	{ "daemon",	cmd_daemon,	0 },
+	{ "irq",	cmd_irq,	0 },
 };
 
 struct pager_config {
-- 
2.17.1


  reply	other threads:[~2021-02-25  1:32 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-02-25  1:30 [PATCH v5 0/2] perf tools: add 'perf irq' to measure the hardware interrupts Bixuan Cui
2021-02-25  1:30 ` Bixuan Cui [this message]
2021-02-25  1:30 ` [PATCH v5 2/2] perf tools: Add documentation for 'perf irq' command Bixuan Cui
2021-02-25 16:19 ` [PATCH v5 0/2] perf tools: add 'perf irq' to measure the hardware interrupts Jiri Olsa

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210225013016.67100-2-cuibixuan@huawei.com \
    --to=cuibixuan@huawei.com \
    --cc=abudankov@huawei.com \
    --cc=acme@kernel.org \
    --cc=alexander.shishkin@linux.intel.com \
    --cc=hagen@jauu.net \
    --cc=john.wanghui@huawei.com \
    --cc=jolsa@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=mingo@redhat.com \
    --cc=namhyung@kernel.org \
    --cc=peterz@infradead.org \
    --cc=yz.yuzhou@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.