Received: by 2002:a05:6a10:2785:0:0:0:0 with SMTP id ia5csp76491pxb; Wed, 13 Jan 2021 23:51:57 -0800 (PST) X-Google-Smtp-Source: ABdhPJyTjALofdrfwZ26EoVMvHvO8Gmrh1tWJ104trGk8H1KovK48DMhCXCn8pp245h6lT35wxHp X-Received: by 2002:a50:eb96:: with SMTP id y22mr4866237edr.91.1610610716959; Wed, 13 Jan 2021 23:51:56 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1610610716; cv=none; d=google.com; s=arc-20160816; b=ZYS2aZwlogVOzLjCTJPGICiz285hKeP98cUR389U0xTba6NUaAkA3Zz27REhfngFv4 /LWH/JTKav8TOAy/K1frne2wRb2vEjOeXki+nPDhHusOMsJqvM6AKifCLFwKlM0Lyuju 0hjxMPSFKpk3opZSLIfjo2MZJKOiWJyFMgqdYNWdJXm+koQ6nnGsoITIKhYYuDuqpjIu 9x+WpmeioT/imw8Tt7kWojkwRi/sVJAwmf+VlEx1alniCpwUTru/fFTzghIqk+xo1/Xu vd5BP8ExNrLSnlEQDZNU8OI8KCCbFgfeVEL+f0f6EECP7z7aFGtknhfYpwl78lojyUMK a5Pw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:mime-version:references:in-reply-to:message-id :date:subject:cc:to:from; bh=tfKzrNcD6bAlo8hSiZBX10D+ybdm/1uEoYl5WNhUVWM=; b=P12q/l4PHGVJsG02+/PWX1SM0s0fkxCDpcGHSjQx/YLMKAFzV1/iWhTMNS5YWGZ2Sr LEIUbcCNOQCl4DbiIRhyrXPyhasoDRfHmjw44heXIJjTCMb/NEmUy/ubSstZbG8xVJEw SOBfAGKICD5fQhDeP/zYKxJ42pU0hLSMJcRrM3QgFqZTPzuYcqpivS0/GmmrrsE79Lfc b5tjjmSzEr6dJ8Osg7mEBPaotWE7NzWTfaOQMANSANGNjoPRVy9VFH+V4guZKbKxO/w4 44i4v6PJXVpKRtDx3uIyAwrns+0oOON90QjClIcDYJm4u1+xlvL1UNLmRCmp4EgeM16G cWMg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id hs17si1998874ejc.661.2021.01.13.23.51.32; Wed, 13 Jan 2021 23:51:56 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727443AbhANHuk (ORCPT + 99 others); Thu, 14 Jan 2021 02:50:40 -0500 Received: from szxga06-in.huawei.com ([45.249.212.32]:10966 "EHLO szxga06-in.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727319AbhANHuj (ORCPT ); Thu, 14 Jan 2021 02:50:39 -0500 Received: from DGGEMS406-HUB.china.huawei.com (unknown [172.30.72.58]) by szxga06-in.huawei.com (SkyGuard) with ESMTP id 4DGbzk27D0zj5hb; Thu, 14 Jan 2021 15:49:06 +0800 (CST) Received: from huawei.com (10.174.28.241) by DGGEMS406-HUB.china.huawei.com (10.3.19.206) with Microsoft SMTP Server id 14.3.498.0; Thu, 14 Jan 2021 15:49:47 +0800 From: Bixuan Cui To: , , , , , , , CC: Subject: [PATCH v2 1/2] perf tools: add 'perf irq' to measure the hardware interrupts Date: Thu, 14 Jan 2021 15:48:51 +0800 Message-ID: <20210114074852.13231-2-cuibixuan@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20210114074852.13231-1-cuibixuan@huawei.com> References: <20210114074852.13231-1-cuibixuan@huawei.com> MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [10.174.28.241] X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add 'perf irq' to trace/measure the hardware interrupts. Now three functions are provided: 1. 'perf irq record ' 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 --- tools/perf/Build | 1 + tools/perf/builtin-irq.c | 287 +++++++++++++++++++++++++++++++++++++++ tools/perf/builtin.h | 1 + tools/perf/perf.c | 1 + 4 files changed, 290 insertions(+) create mode 100644 tools/perf/builtin-irq.c diff --git a/tools/perf/Build b/tools/perf/Build index 5f392dbb88fc..d52a1e1d6d8a 100644 --- a/tools/perf/Build +++ b/tools/perf/Build @@ -24,6 +24,7 @@ perf-y += builtin-mem.o perf-y += builtin-data.o perf-y += builtin-version.o perf-y += builtin-c2c.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..58dd1a488edf --- /dev/null +++ b/tools/perf/builtin-irq.c @@ -0,0 +1,287 @@ +// 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 +#include +#include "util/trace-event.h" + +#include "util/debug.h" +#include "util/event.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define IRQ_NAME_LEN 20 +#define MAX_CPUS 4096 + +static const char *cpu_list; +static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); + +struct perf_irq; + +struct perf_irq { + struct perf_tool tool; + bool force; + + u32 irq_entry_irq; + char irq_name[IRQ_NAME_LEN]; + u32 cpu; + u64 irq_entry_time; + u32 irq_entry_pid; + u32 irq_exit_irq; + u64 irq_exit_time; + u32 irq_exit_pid; +}; + +typedef int (*irq_handler)(struct perf_tool *tool, + union perf_event *event, + struct evsel *evsel, + struct perf_sample *sample, + struct machine *machine); + +static int perf_report_process_sample(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct evsel *evsel, + struct machine *machine) +{ + int err = 0; + + if (evsel->handler != NULL) { + irq_handler f = evsel->handler; + err = f(tool, event, evsel, sample, machine); + } + + return err; +} + +static void output_report(struct perf_irq *irq) +{ + int ret, i; + char irq_entry_time[30], irq_exit_time[30], irq_diff[30]; + + /* The entry and exit of the hardware irq function + * exist at the same time. Check it by irq and pid. + */ + if (irq->irq_entry_pid != irq->irq_exit_pid || + irq->irq_entry_irq != irq->irq_exit_irq) + return; + + timestamp__scnprintf_usec(irq->irq_entry_time, + irq_entry_time, sizeof(irq_entry_time)); + timestamp__scnprintf_usec(irq->irq_exit_time, + irq_exit_time, sizeof(irq_exit_time)); + timestamp__scnprintf_usec(irq->irq_exit_time - irq->irq_entry_time, + irq_diff, sizeof(irq_diff)); + + printf(" -------------------------------------------------------------------------------------------------------------------------------------------\n"); + printf(" Irq name | CPU | Time consume us | Handler entry time | Handler exit time \n"); + printf(" -------------------------------------------------------------------------------------------------------------------------------------------\n"); + + ret = printf(" %s ", irq->irq_name); + for (i = 0; i < IRQ_NAME_LEN - ret; i++) + printf(" "); + + printf("| [%04d] | %13s s | %16s s | %16s s\n", + irq->cpu, irq_diff, irq_entry_time, irq_exit_time); + printf("\n"); +} + +static int report_irq_handler_entry_event(struct perf_tool *tool, + union perf_event *event __maybe_unused, + struct evsel *evsel, + struct perf_sample *sample, + struct machine *machine __maybe_unused) +{ + int err = 0; + struct perf_irq *irq = container_of(tool, struct perf_irq, tool); + + const char *name = evsel__strval(evsel, sample, "name"); + + irq->irq_entry_pid = evsel__intval(evsel, sample, "pid"); + irq->irq_entry_irq = evsel__intval(evsel, sample, "irq"); + irq->irq_entry_time = sample->time; + strncpy(irq->irq_name, name, IRQ_NAME_LEN); + + return err; +} + +static int report_irq_handler_exit_event(struct perf_tool *tool, + union perf_event *event __maybe_unused, + struct evsel *evsel, + struct perf_sample *sample, + struct machine *machine __maybe_unused) +{ + int err = 0; + struct perf_irq *irq = container_of(tool, struct perf_irq, tool); + + irq->irq_exit_pid = evsel__intval(evsel, sample, "pid"); + irq->irq_exit_irq = evsel__intval(evsel, sample, "irq"); + irq->irq_exit_time = sample->time; + irq->cpu = sample->cpu; + + if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) + return err; + + output_report(irq); + + 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) +{ + 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_END() + }; + const struct option report_options[] = { + OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"), + OPT_PARENT(irq_options) + }; + + const char * const report_usage[] = { + "perf irq report []", + NULL + }; + const char *const irq_subcommands[] = { "record", "report", "script", + 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 (!strcmp(argv[0], "script")) { + return cmd_script(argc, argv); + } else if (!strncmp(argv[0], "record", 6)) { + return __cmd_record(argc, argv); + } else if (!strncmp(argv[0], "report", 11)) { + 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 14a2db622a7b..fd92dfc89370 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -37,6 +37,7 @@ int cmd_inject(int argc, const char **argv); 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_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 27f94b0bb874..df9cb344c2d0 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -88,6 +88,7 @@ static struct cmd_struct commands[] = { { "mem", cmd_mem, 0 }, { "data", cmd_data, 0 }, { "ftrace", cmd_ftrace, 0 }, + { "irq", cmd_irq, 0 }, }; struct pager_config { -- 2.17.1