From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9C9A835F603 for ; Sat, 25 Apr 2026 22:50:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157413; cv=none; b=o0UMIb78nfgrqg2cKzY2Ff94+kKqVgu0whO12uEar6wtWi6LEjtvJDJHCtwonkGZKtKtNItuo7iZ5U6dJuJKYlVc4N0rL4bAMYxZjxOCWVz54yI6leGNYmoozeSiogychKRZAmIb9d7GMCq/SozuGrUSob0/8qEuH0po6B7HYOg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157413; c=relaxed/simple; bh=tQ1Ciq6F5jpsicBpjuYuPQ8Rii/lZLgtKSUmaluqGxc=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=cdviOzhXpfJgyY7b3MXeiNM9MAbPdjM0JScIl+n9Qt5ZGPaeXg9Vfa9oxGv4o665w0KZ5UYnsShSMJqGp4XUGABGWVYC+yFRY9mUShm7KPLvW5mWvYiwJGzLraIQw69rwJEacq9+zego2rJUvM2agmGq8rQ1pOpxuCZKaUhj6XY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=whXQydGR; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="whXQydGR" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2ddd8ef5343so8846681eec.1 for ; Sat, 25 Apr 2026 15:50:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157410; x=1777762210; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=kv6V2dWecTfOpmD/K/CBj33HJCmv3wXwQToI4e7PgkI=; b=whXQydGRax1mEEqwU6E94vYgl4TIEcnlqtVT0BS5CFqHG0g4LB7g6N8lEI9WaOyUAW k/3eW5F8WKyAC0HAfLW6xJ+POJwNQwdJkodCr8Ea+aJJhhSRC3wFm3HQduA5W/YTWRAh tbE85kfaRHIV4Ijcl4/7maM4rt8XDBCwMr1RJHI2MW/SM79ZenIhsnqC64DB/FaYgru3 bkt4C8irc6IIdyLZY8ri/rAcGT43GvnXEnPbJpawnMip3wIO9+b0sfcAi/23FsgUu0kp kXtc5HhW1zlpgWQZJxe8hTwRKj3EV5Qw1I3Rnbqf8OQzncrb7/ceqYxxJXbS2xIuB42w OqQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157410; x=1777762210; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=kv6V2dWecTfOpmD/K/CBj33HJCmv3wXwQToI4e7PgkI=; b=hFNHQAbjBcnUHMS3d7YQWiEb5ZdpxyHAUglcVwjn55aZvj56Mu2PRvHlyRBoWb3nu2 XzPNxU9ZmGdXLUxXn/gWhLIN05m+OGAWHqAAgpN8PrZMYQ3ZmCLVbDrszWMwv5zwp8bP sBzESXzbE9hQvbDHxQLS3CUleRA+4LGHUPkxFIKdeDlIfaba+mG+BmoPeAcavN3y9FGi BMj0TRB9zqcqRSPuOHqE7zK5fIfCgRVQre/8Za+zMP+IgUMkhZ5JYkXQEdWSm3ryFKyw +LF8Oijjvw2656tlEzV6708tfqBu3t6E6uMAXci+d6fKsAbX3heMJHFYwnoAwq/ZzRb9 7uMg== X-Forwarded-Encrypted: i=1; AFNElJ88tDs3qQy6zeUJDX+I1pp18AmgboVLJ9Eh4OcR+4WLZVM6lvcR4q4Ooa2vJV6TuJkBziTpV8McZ4ag0wc=@vger.kernel.org X-Gm-Message-State: AOJu0YzLjrgNEFBeoM428BOAA6XWGgWpGoKkOjY7xutpcC6WT+xFX6qc diwgrVUDan+xbwYS8gTA/B6dHllVA9bOXg1+sGv0aK2WsBGl1iNqGZrs6bkfrFs8p/pUL6ErT0F +7j0gv539Wg== X-Received: from dybtv6.prod.google.com ([2002:a05:7300:f486:b0:2d8:5b05:964b]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:641b:b0:2d1:d434:d022 with SMTP id 5a478bee46e88-2e4521f9d92mr19591953eec.0.1777157409508; Sat, 25 Apr 2026 15:50:09 -0700 (PDT) Date: Sat, 25 Apr 2026 15:48:53 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-2-irogers@google.com> Subject: [PATCH v7 01/59] perf inject: Fix itrace branch stack synthesis From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When using "perf inject --itrace=3DL" to synthesize branch stacks from AUX data, several issues caused failures: 1. The synthesized samples were delivered without the PERF_SAMPLE_BRANCH_STACK flag if it was not in the original event's sample_type. Fixed by using sample_type | evsel->synth_sample_type in intel_pt_deliver_synth_event. 2. The record layout was misaligned because of inconsistent handling of PERF_SAMPLE_BRANCH_HW_INDEX. Fixed by explicitly writing nr and hw_idx in perf_event__synthesize_sample. 3. Modifying evsel->core.attr.sample_type early in __cmd_inject caused parse failures for subsequent records in the input file. Fixed by moving this modification to just before writing the header. 4. perf_event__repipe_sample was narrowed to only synthesize samples when branch stack injection was requested, and restored the use of perf_inject__cut_auxtrace_sample as a fallback to preserve functionality. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- Issues fixed in v2: 1. Potential Heap Overflow in perf_event__repipe_sample : Addressed by adding a check that prints an error and returns -EFAULT if the calculated event size exceeds PERF_SAMPLE_MAX_SIZE , as you requested. 2. Header vs Payload Mismatch in __cmd_inject : Addressed by narrowing the condition so that HEADER_BRANCH_STACK is only set in the file header if add_last_branch was true. 3. NULL Pointer Dereference in intel-pt.c : Addressed by updating the condition in intel_pt_do_synth_pebs_sample to fill sample. branch_stack if it was synthesized, even if not in the original sample_type . 4. Unsafe Reads for events lacking HW_INDEX in synthetic-events.c : Addressed by using the perf_sample__branch_entries() macro and checking sample->no_hw_idx . 5. Size mismatch in perf_event__sample_event_size : Addressed by passing branch_sample_type to it and conditioning the hw_idx size on PERF_SAMPLE_BRANCH_HW_INDEX . --- tools/perf/bench/inject-buildid.c | 9 ++-- tools/perf/builtin-inject.c | 77 ++++++++++++++++++++++++++++-- tools/perf/tests/dlfilter-test.c | 8 +++- tools/perf/tests/sample-parsing.c | 5 +- tools/perf/util/arm-spe.c | 7 ++- tools/perf/util/cs-etm.c | 6 ++- tools/perf/util/intel-bts.c | 3 +- tools/perf/util/intel-pt.c | 13 +++-- tools/perf/util/synthetic-events.c | 25 +++++++--- tools/perf/util/synthetic-events.h | 6 ++- 10 files changed, 129 insertions(+), 30 deletions(-) diff --git a/tools/perf/bench/inject-buildid.c b/tools/perf/bench/inject-bu= ildid.c index aad572a78d7f..bfd2c5ec9488 100644 --- a/tools/perf/bench/inject-buildid.c +++ b/tools/perf/bench/inject-buildid.c @@ -228,9 +228,12 @@ static ssize_t synthesize_sample(struct bench_data *da= ta, struct bench_dso *dso, =20 event.header.type =3D PERF_RECORD_SAMPLE; event.header.misc =3D PERF_RECORD_MISC_USER; - event.header.size =3D perf_event__sample_event_size(&sample, bench_sample= _type, 0); - - perf_event__synthesize_sample(&event, bench_sample_type, 0, &sample); + event.header.size =3D perf_event__sample_event_size(&sample, bench_sample= _type, + /*read_format=3D*/0, + /*branch_sample_type=3D*/0); + perf_event__synthesize_sample(&event, bench_sample_type, + /*read_format=3D*/0, + /*branch_sample_type=3D*/0, &sample); =20 return writen(data->input_pipe[1], &event, event.header.size); } diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index f174bc69cec4..88c0ef4f5ff1 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -375,7 +375,59 @@ static int perf_event__repipe_sample(const struct perf= _tool *tool, =20 build_id__mark_dso_hit(tool, event, sample, evsel, machine); =20 - if (inject->itrace_synth_opts.set && sample->aux_sample.size) { + if (inject->itrace_synth_opts.set && + (inject->itrace_synth_opts.last_branch || + inject->itrace_synth_opts.add_last_branch)) { + union perf_event *event_copy =3D (void *)inject->event_copy; + struct branch_stack dummy_bs =3D { .nr =3D 0 }; + int err; + size_t sz; + u64 orig_type =3D evsel->core.attr.sample_type; + u64 orig_branch_type =3D evsel->core.attr.branch_sample_type; + + if (event_copy =3D=3D NULL) { + inject->event_copy =3D malloc(PERF_SAMPLE_MAX_SIZE); + if (!inject->event_copy) + return -ENOMEM; + + event_copy =3D (void *)inject->event_copy; + } + + if (!sample->branch_stack) + sample->branch_stack =3D &dummy_bs; + + if (inject->itrace_synth_opts.add_last_branch) { + /* Temporarily add in type bits for synthesis. */ + evsel->core.attr.sample_type |=3D PERF_SAMPLE_BRANCH_STACK; + evsel->core.attr.branch_sample_type |=3D PERF_SAMPLE_BRANCH_HW_INDEX; + evsel->core.attr.sample_type &=3D ~PERF_SAMPLE_AUX; + } + + sz =3D perf_event__sample_event_size(sample, evsel->core.attr.sample_typ= e, + evsel->core.attr.read_format, + evsel->core.attr.branch_sample_type); + + if (sz > PERF_SAMPLE_MAX_SIZE) { + pr_err("Sample size %zu exceeds max size %d\n", sz, PERF_SAMPLE_MAX_SIZ= E); + return -EFAULT; + } + + event_copy->header.type =3D PERF_RECORD_SAMPLE; + event_copy->header.size =3D sz; + + err =3D perf_event__synthesize_sample(event_copy, evsel->core.attr.sampl= e_type, + evsel->core.attr.read_format, + evsel->core.attr.branch_sample_type, sample); + + evsel->core.attr.sample_type =3D orig_type; + evsel->core.attr.branch_sample_type =3D orig_branch_type; + + if (err) { + pr_err("Failed to synthesize sample\n"); + return err; + } + event =3D event_copy; + } else if (inject->itrace_synth_opts.set && sample->aux_sample.size) { event =3D perf_inject__cut_auxtrace_sample(inject, event, sample); if (IS_ERR(event)) return PTR_ERR(event); @@ -464,7 +516,8 @@ static int perf_event__convert_sample_callchain(const s= truct perf_tool *tool, sample_type &=3D ~(PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER); =20 perf_event__synthesize_sample(event_copy, sample_type, - evsel->core.attr.read_format, sample); + evsel->core.attr.read_format, + evsel->core.attr.branch_sample_type, sample); return perf_event__repipe_synth(tool, event_copy); } =20 @@ -1100,7 +1153,8 @@ static int perf_inject__sched_stat(const struct perf_= tool *tool, sample_sw.period =3D sample->period; sample_sw.time =3D sample->time; perf_event__synthesize_sample(event_sw, evsel->core.attr.sample_type, - evsel->core.attr.read_format, &sample_sw); + evsel->core.attr.read_format, + evsel->core.attr.branch_sample_type, &sample_sw); build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); ret =3D perf_event__repipe(tool, event_sw, &sample_sw, machine); perf_sample__exit(&sample_sw); @@ -2434,12 +2488,25 @@ static int __cmd_inject(struct perf_inject *inject) * synthesized hardware events, so clear the feature flag. */ if (inject->itrace_synth_opts.set) { + struct evsel *evsel; + perf_header__clear_feat(&session->header, HEADER_AUXTRACE); - if (inject->itrace_synth_opts.last_branch || - inject->itrace_synth_opts.add_last_branch) + + evlist__for_each_entry(session->evlist, evsel) { + evsel->core.attr.sample_type &=3D ~PERF_SAMPLE_AUX; + } + + if (inject->itrace_synth_opts.add_last_branch) { perf_header__set_feat(&session->header, HEADER_BRANCH_STACK); + + evlist__for_each_entry(session->evlist, evsel) { + evsel->core.attr.sample_type |=3D PERF_SAMPLE_BRANCH_STACK; + evsel->core.attr.branch_sample_type |=3D + PERF_SAMPLE_BRANCH_HW_INDEX; + } + } } =20 /* diff --git a/tools/perf/tests/dlfilter-test.c b/tools/perf/tests/dlfilter-t= est.c index e63790c61d53..204663571943 100644 --- a/tools/perf/tests/dlfilter-test.c +++ b/tools/perf/tests/dlfilter-test.c @@ -188,8 +188,12 @@ static int write_sample(struct test_data *td, u64 samp= le_type, u64 id, pid_t pid =20 event->header.type =3D PERF_RECORD_SAMPLE; event->header.misc =3D PERF_RECORD_MISC_USER; - event->header.size =3D perf_event__sample_event_size(&sample, sample_type= , 0); - err =3D perf_event__synthesize_sample(event, sample_type, 0, &sample); + event->header.size =3D perf_event__sample_event_size(&sample, sample_type, + /*read_format=3D*/0, + /*branch_sample_type=3D*/0); + err =3D perf_event__synthesize_sample(event, sample_type, + /*read_format=3D*/0, + /*branch_sample_type=3D*/0, &sample); if (err) return test_result("perf_event__synthesize_sample() failed", TEST_FAIL); =20 diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-pa= rsing.c index a7327c942ca2..55f0b73ca20e 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c @@ -310,7 +310,8 @@ static int do_test(u64 sample_type, u64 sample_regs, u6= 4 read_format) sample.read.one.lost =3D 1; } =20 - sz =3D perf_event__sample_event_size(&sample, sample_type, read_format); + sz =3D perf_event__sample_event_size(&sample, sample_type, read_format, + evsel.core.attr.branch_sample_type); bufsz =3D sz + 4096; /* Add a bit for overrun checking */ event =3D malloc(bufsz); if (!event) { @@ -324,7 +325,7 @@ static int do_test(u64 sample_type, u64 sample_regs, u6= 4 read_format) event->header.size =3D sz; =20 err =3D perf_event__synthesize_sample(event, sample_type, read_format, - &sample); + evsel.core.attr.branch_sample_type, &sample); if (err) { pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", "perf_event__synthesize_sample", sample_type, err); diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c index e5835042acdf..c4ed9f10e731 100644 --- a/tools/perf/util/arm-spe.c +++ b/tools/perf/util/arm-spe.c @@ -484,8 +484,11 @@ static void arm_spe__prep_branch_stack(struct arm_spe_= queue *speq) =20 static int arm_spe__inject_event(union perf_event *event, struct perf_samp= le *sample, u64 type) { - event->header.size =3D perf_event__sample_event_size(sample, type, 0); - return perf_event__synthesize_sample(event, type, 0, sample); + event->header.type =3D PERF_RECORD_SAMPLE; + event->header.size =3D perf_event__sample_event_size(sample, type, /*read= _format=3D*/0, + /*branch_sample_type=3D*/0); + return perf_event__synthesize_sample(event, type, /*read_format=3D*/0, + /*branch_sample_type=3D*/0, sample); } =20 static inline int diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 8a639d2e51a4..1ebc1a6a5e75 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -1425,8 +1425,10 @@ static void cs_etm__update_last_branch_rb(struct cs_= etm_queue *etmq, static int cs_etm__inject_event(union perf_event *event, struct perf_sample *sample, u64 type) { - event->header.size =3D perf_event__sample_event_size(sample, type, 0); - return perf_event__synthesize_sample(event, type, 0, sample); + event->header.size =3D perf_event__sample_event_size(sample, type, /*read= _format=3D*/0, + /*branch_sample_type=3D*/0); + return perf_event__synthesize_sample(event, type, /*read_format=3D*/0, + /*branch_sample_type=3D*/0, sample); } =20 =20 diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c index 382255393fb3..0b18ebd13f7c 100644 --- a/tools/perf/util/intel-bts.c +++ b/tools/perf/util/intel-bts.c @@ -303,7 +303,8 @@ static int intel_bts_synth_branch_sample(struct intel_b= ts_queue *btsq, event.sample.header.size =3D bts->branches_event_size; ret =3D perf_event__synthesize_sample(&event, bts->branches_sample_type, - 0, &sample); + /*read_format=3D*/0, /*branch_sample_type=3D*/0, + &sample); if (ret) return ret; } diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index fc9eec8b54b8..2dce6106c038 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -1731,8 +1731,12 @@ static void intel_pt_prep_b_sample(struct intel_pt *= pt, static int intel_pt_inject_event(union perf_event *event, struct perf_sample *sample, u64 type) { - event->header.size =3D perf_event__sample_event_size(sample, type, 0); - return perf_event__synthesize_sample(event, type, 0, sample); + event->header.type =3D PERF_RECORD_SAMPLE; + event->header.size =3D perf_event__sample_event_size(sample, type, /*read= _format=3D*/0, + /*branch_sample_type=3D*/0); + + return perf_event__synthesize_sample(event, type, /*read_format=3D*/0, + /*branch_sample_type=3D*/0, sample); } =20 static inline int intel_pt_opt_inject(struct intel_pt *pt, @@ -2486,7 +2490,7 @@ static int intel_pt_do_synth_pebs_sample(struct intel= _pt_queue *ptq, struct evse intel_pt_add_xmm(intr_regs, pos, items, regs_mask); } =20 - if (sample_type & PERF_SAMPLE_BRANCH_STACK) { + if ((sample_type | evsel->synth_sample_type) & PERF_SAMPLE_BRANCH_STACK) { if (items->mask[INTEL_PT_LBR_0_POS] || items->mask[INTEL_PT_LBR_1_POS] || items->mask[INTEL_PT_LBR_2_POS]) { @@ -2557,7 +2561,8 @@ static int intel_pt_do_synth_pebs_sample(struct intel= _pt_queue *ptq, struct evse sample.transaction =3D txn; } =20 - ret =3D intel_pt_deliver_synth_event(pt, event, &sample, sample_type); + ret =3D intel_pt_deliver_synth_event(pt, event, &sample, + sample_type | evsel->synth_sample_type); perf_sample__exit(&sample); return ret; } diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic= -events.c index 85bee747f4cd..2461f25a4d7d 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -1455,7 +1455,8 @@ int perf_event__synthesize_stat_round(const struct pe= rf_tool *tool, return process(tool, (union perf_event *) &event, NULL, machine); } =20 -size_t perf_event__sample_event_size(const struct perf_sample *sample, u64= type, u64 read_format) +size_t perf_event__sample_event_size(const struct perf_sample *sample, u64= type, u64 read_format, + u64 branch_sample_type) { size_t sz, result =3D sizeof(struct perf_record_sample); =20 @@ -1515,8 +1516,10 @@ size_t perf_event__sample_event_size(const struct pe= rf_sample *sample, u64 type, =20 if (type & PERF_SAMPLE_BRANCH_STACK) { sz =3D sample->branch_stack->nr * sizeof(struct branch_entry); - /* nr, hw_idx */ - sz +=3D 2 * sizeof(u64); + /* nr */ + sz +=3D sizeof(u64); + if (branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX) + sz +=3D sizeof(u64); result +=3D sz; } =20 @@ -1605,7 +1608,7 @@ static __u64 *copy_read_group_values(__u64 *array, __= u64 read_format, } =20 int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 r= ead_format, - const struct perf_sample *sample) + u64 branch_sample_type, const struct perf_sample *sample) { __u64 *array; size_t sz; @@ -1719,9 +1722,17 @@ int perf_event__synthesize_sample(union perf_event *= event, u64 type, u64 read_fo =20 if (type & PERF_SAMPLE_BRANCH_STACK) { sz =3D sample->branch_stack->nr * sizeof(struct branch_entry); - /* nr, hw_idx */ - sz +=3D 2 * sizeof(u64); - memcpy(array, sample->branch_stack, sz); + + *array++ =3D sample->branch_stack->nr; + + if (branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX) { + if (sample->no_hw_idx) + *array++ =3D 0; + else + *array++ =3D sample->branch_stack->hw_idx; + } + + memcpy(array, perf_sample__branch_entries((struct perf_sample *)sample),= sz); array =3D (void *)array + sz; } =20 diff --git a/tools/perf/util/synthetic-events.h b/tools/perf/util/synthetic= -events.h index b0edad0c3100..8c7f49f9ccf5 100644 --- a/tools/perf/util/synthetic-events.h +++ b/tools/perf/util/synthetic-events.h @@ -81,7 +81,8 @@ int perf_event__synthesize_mmap_events(const struct perf_= tool *tool, union perf_ int perf_event__synthesize_modules(const struct perf_tool *tool, perf_even= t__handler_t process, struct machine *machine); int perf_event__synthesize_namespaces(const struct perf_tool *tool, union = perf_event *event, pid_t pid, pid_t tgid, perf_event__handler_t process, st= ruct machine *machine); int perf_event__synthesize_cgroups(const struct perf_tool *tool, perf_even= t__handler_t process, struct machine *machine); -int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 r= ead_format, const struct perf_sample *sample); +int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 r= ead_format, + u64 branch_sample_type, const struct perf_sample *sample); int perf_event__synthesize_stat_config(const struct perf_tool *tool, struc= t perf_stat_config *config, perf_event__handler_t process, struct machine *= machine); int perf_event__synthesize_stat_events(struct perf_stat_config *config, co= nst struct perf_tool *tool, struct evlist *evlist, perf_event__handler_t pr= ocess, bool attrs); int perf_event__synthesize_stat_round(const struct perf_tool *tool, u64 ti= me, u64 type, perf_event__handler_t process, struct machine *machine); @@ -97,7 +98,8 @@ void perf_event__synthesize_final_bpf_metadata(struct per= f_session *session, =20 int perf_tool__process_synth_event(const struct perf_tool *tool, union per= f_event *event, struct machine *machine, perf_event__handler_t process); =20 -size_t perf_event__sample_event_size(const struct perf_sample *sample, u64= type, u64 read_format); +size_t perf_event__sample_event_size(const struct perf_sample *sample, u64= type, + u64 read_format, u64 branch_sample_type); =20 int __machine__synthesize_threads(struct machine *machine, const struct pe= rf_tool *tool, struct target *target, struct perf_thread_map *threads, --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 937E438C42D for ; Sat, 25 Apr 2026 22:50:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157415; cv=none; b=ScM1UXLGMbZ7rCjEkGbj4CyP9nws5lCglRRekEwJwvCZyBzZPNnAB6yRPxXkgHBTwHTtw/kgiH/hLmT5QiGo0fimygOFkGsev9NUqwAVhpejckDkxWPuLSr6s22dNXCjGaMdyMBr4ETpCAzqlEXpcR57GA07bynpvLRSpkegN0s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157415; c=relaxed/simple; bh=bITPVK77ksbOupCd8yIPNfKlXiQAinH05MI73w7M+5Y=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=u/CHXh1/OnMET8pqa6I6oSYjC8vf/ec9O/EgWTyy7i4n+I7wHZvH1o+xOWBb1jU3qCuembN0xx32Y2/12BgcpaJm+Q9qKBmnbuIBHySYVjVxqoCoAfPQvOjcEj53qwzYdZqDH0zPV/zWyVBXLiJGenVcRDGdZl5Hbp2RoFRROdw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=drZ53eSF; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="drZ53eSF" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2d8a677cdfaso9577369eec.1 for ; Sat, 25 Apr 2026 15:50:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157411; x=1777762211; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=K/xS7q5C5QGMJMPDSriSnCglCHYvD9rGLYFUY/AUodE=; b=drZ53eSFJ0I4lPIKSxseH/uAbCqz68p1WWvgm8J1FgZ3Hh6mpwlEh1GV7ddqEK2J80 v9oxuhqtD2YIFal104s+gUn1IWTby3Fr83vGdReQJoPku7ecgs1l32G5RLd6mDoeHgv/ +JAA2+7jEJktmh9CqP99cPDLFoNs3RbcjBdwc4pxy/H3JAKAT2/UjGqoZtZxHGteDMWO lezq2TR1idSu7rnM9o9JfgOgJQ2NAK5pR4nrT5JPeUJGhu3Qok2mo1cfyT3ViRpt7o1i heU3l9G1/0E9OLk1u61M310pzGM0Z7q9oDWi8hwk+1mO4EHJqo+Ddx5AOukjXA2i4j9s w5vg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157411; x=1777762211; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=K/xS7q5C5QGMJMPDSriSnCglCHYvD9rGLYFUY/AUodE=; b=N3Zks/6skAR+Z+WL5rZkh0zNZJYgDr7JK2yRSgOSvIHdzZ9tKG6pVo/wr1THIdN3u8 jzWSTsWB8XeBrbm5L5jtw9FYSBOkAXiVy4IeRrrzFKalri1PH3Io74x5NywHy6rmlbwm TOYm/2Nn20X95ABY4IZBR8HfvI8e9ZoVXp7E4VUUZGm2dsPMO7v7KudHY/qeYQC1UAcn r4lRMdxhuvE83LjRELduad92f91Qxf4dCTPLH6T8i9cdc9Yb7HRWpbN50WveMF9xtNgF BAmtqe9KmYL2wagrsZRfwlI9YJf6Vb4tAqIOExWdvJ8jNidPWEfnpmOAzO/zCsWCY5Kd 91kA== X-Forwarded-Encrypted: i=1; AFNElJ/NG59d4KrmEpF2FCCkOegrC5Apy9hncTwr29ZhnEiSjOl5cWi8ud96h3mvvIa5cqjZVtA+HLbg1JedWAE=@vger.kernel.org X-Gm-Message-State: AOJu0YwAVmjmcVgIYWv9kucW9tSq0+rd+MhDpcqsIT1l1hnONHSprjAq eDg8PcMnwLU2949yGl7ij+vpc2Lvc2iva3Euk9zzZmgzKSAUOB1kfQjdf9Js2BUNZ9iAN+wHJGp /bxNGg4UfNA== X-Received: from dybnj1.prod.google.com ([2002:a05:7300:d081:b0:2da:5e63:c8e4]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:693c:3108:b0:2d8:df01:d9f6 with SMTP id 5a478bee46e88-2e478c20551mr20278918eec.23.1777157411417; Sat, 25 Apr 2026 15:50:11 -0700 (PDT) Date: Sat, 25 Apr 2026 15:48:54 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-3-irogers@google.com> Subject: [PATCH v7 02/59] perf arch arm: Sort includes and add missed explicit dependencies From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Fix missing #includes found while cleaning the evsel/evlist header files. Sort the remaining header files for consistency with the rest of the code. Signed-off-by: Ian Rogers --- tools/perf/arch/arm/util/cs-etm.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/c= s-etm.c index b7a839de8707..cdf8e3e60606 100644 --- a/tools/perf/arch/arm/util/cs-etm.c +++ b/tools/perf/arch/arm/util/cs-etm.c @@ -3,10 +3,13 @@ * Copyright(C) 2015 Linaro Limited. All rights reserved. * Author: Mathieu Poirier */ +#include "../../../util/cs-etm.h" + +#include +#include =20 -#include -#include #include +#include #include #include #include @@ -14,25 +17,24 @@ #include #include #include +#include + +#include +#include // page_size =20 -#include "cs-etm.h" -#include "../../../util/debug.h" -#include "../../../util/record.h" #include "../../../util/auxtrace.h" #include "../../../util/cpumap.h" +#include "../../../util/debug.h" #include "../../../util/event.h" #include "../../../util/evlist.h" #include "../../../util/evsel.h" -#include "../../../util/perf_api_probe.h" #include "../../../util/evsel_config.h" +#include "../../../util/perf_api_probe.h" +#include "../../../util/pmu.h" #include "../../../util/pmus.h" -#include "../../../util/cs-etm.h" -#include // page_size +#include "../../../util/record.h" #include "../../../util/session.h" - -#include -#include -#include +#include "cs-etm.h" =20 struct cs_etm_recording { struct auxtrace_record itr; --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2F0AF38D018 for ; Sat, 25 Apr 2026 22:50:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157416; cv=none; b=h6ebm7nOzp7QKNxlfsoTir2X4DdYXNVSCjpY8Oa4hSHPS++Jkwk6LMfGwGZqJTv5BvPJIPmBjXVsGhO+UDNJug49CMeL5iT+mUGen5aGjdm2+kQvNpDW7WwKRxi84HZWUEgTuoBvEj5+xk621F7sDVe0VpwD3Q2Rerj0N+casqI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157416; c=relaxed/simple; bh=IzGtiew6lV3UoAkWJeECtCMku/bwu3+F3qCjyvwzJ3A=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=W4/r8N6nO0hZU7/x+yN41I4n27M0iTv86eQTBEuchjRABPIkuJonfwzcn+mYCknCyGOD458VTEcjO5PZhs2odfAEH9egwsUhG91NTuBwQqDqCZjEtrPjgBpvG5wtCYEtVezSBwf7zd8vKC6TBvLTD6fFMPBONiqK5nuuYVjcU88= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=nd8Irtt5; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="nd8Irtt5" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-12dc3d81736so1360794c88.1 for ; Sat, 25 Apr 2026 15:50:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157413; x=1777762213; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=RL6gbA3yHwRjF7ykSQ2dEvMc7Axg6yuH3FzRoVTsCT0=; b=nd8Irtt5IbjVgL8cHI5c5u9VhATuQC+UEbGDKKg6rKK4NJvlZz9STnOhD4kmVtYCM5 JmDkViZEDQjl4OduCr8pAKwORxD1+sF400dRxN64EfD8gJf7Kza3WFGWARIOfES++UQ0 b1G1UKLHVFH8prZrImmoR7FZk1RdDmMS3SjlldIduhrlw1oWbOFY+fsDcnRjs8CRMh8W JlvS6fYCMUwUVpD7NvnHThn1L4FdjXIo4AxLP6P7dcSTJ27Fu1KWq73qUZqhvmKbfbHT 0ebr5ktr7TCjD4Ao3XPQODbw2DPPkM2LYre7YYS+vDZY2Bnz87DoivJDxs87FacjfTsp Iljg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157413; x=1777762213; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=RL6gbA3yHwRjF7ykSQ2dEvMc7Axg6yuH3FzRoVTsCT0=; b=jYohGKyz8hDEFBHaQgxQrJKzVbDSn4mBDvxje+7Jzfsh2c+x3ldIj52CgtBiJVe3mF q4vda80rUK4kpg8AMmFYbWWFdjZC6Yni44F88UCJU5ZoAQZNP3D5FdBfemTdfT5cP//c 0qYL/+j4wGg1D9qODALnbwmIDyxQBoJirDgtgD144r7xuLQssf6ecczhR4lac0ltZZ/0 S6JoWOPgDyrHq6fi6PZGiYUzjF8x48aJYRMeJ3KaDioS5Aqoxd9bJM2Vs8y9PcOm4efm A1pihDLk4AJmMyuay5UD/6FIzl6Hpgb0OJAXPfSR+2eirWugunBbqLYocxZ6mtEyDRlC dHdQ== X-Forwarded-Encrypted: i=1; AFNElJ8NwQlosBTRrfQz9IHyO8kweFG7jxpiFrMYwlR0eOT8Mb31XgFCjNjgejQ8FcvG7OT7tvQ4HXI0/nyJlKo=@vger.kernel.org X-Gm-Message-State: AOJu0YyKcpIoC3hJHR/r/bbz6huHtFvtoQyuVjLfZ0OpOpgLZ+64XCFx S9BEcmmpXI2KRIVRogIkvvGJC/6GVJInvcgD1jdjYJqCVeFpXsIuRSMwScl7O7RC1kgKTG8HFOY 4kaSzIJIn1w== X-Received: from dldnz4.prod.google.com ([2002:a05:701a:ca04:b0:12c:91ef:91f4]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:6981:b0:12c:856:ddcc with SMTP id a92af1059eb24-12c73fa3c9amr19853823c88.27.1777157413166; Sat, 25 Apr 2026 15:50:13 -0700 (PDT) Date: Sat, 25 Apr 2026 15:48:55 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-4-irogers@google.com> Subject: [PATCH v7 03/59] perf arch x86: Sort includes and add missed explicit dependencies From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Fix missing #includes found while cleaning the evsel/evlist header files. Sort the remaining header files for consistency with the rest of the code. Signed-off-by: Ian Rogers --- tools/perf/arch/x86/util/intel-bts.c | 20 +++++++++++-------- tools/perf/arch/x86/util/intel-pt.c | 29 +++++++++++++++------------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/uti= l/intel-bts.c index 85c8186300c8..100a23d27998 100644 --- a/tools/perf/arch/x86/util/intel-bts.c +++ b/tools/perf/arch/x86/util/intel-bts.c @@ -4,26 +4,30 @@ * Copyright (c) 2013-2015, Intel Corporation. */ =20 +#include "../../../util/intel-bts.h" + #include -#include -#include + #include +#include #include +#include #include =20 +#include // page_size + +#include "../../../util/auxtrace.h" #include "../../../util/cpumap.h" +#include "../../../util/debug.h" #include "../../../util/event.h" -#include "../../../util/evsel.h" #include "../../../util/evlist.h" +#include "../../../util/evsel.h" #include "../../../util/mmap.h" -#include "../../../util/session.h" +#include "../../../util/pmu.h" #include "../../../util/pmus.h" -#include "../../../util/debug.h" #include "../../../util/record.h" +#include "../../../util/session.h" #include "../../../util/tsc.h" -#include "../../../util/auxtrace.h" -#include "../../../util/intel-bts.h" -#include // page_size =20 #define KiB(x) ((x) * 1024) #define MiB(x) ((x) * 1024 * 1024) diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util= /intel-pt.c index c131a727774f..0307ff15d9fc 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -3,36 +3,39 @@ * intel_pt.c: Intel Processor Trace support * Copyright (c) 2013-2015, Intel Corporation. */ +#include "../../../util/intel-pt.h" =20 #include #include -#include -#include + #include +#include +#include #include +#include #include -#include =20 -#include "../../../util/session.h" +#include +#include // page_size +#include + +#include "../../../util/auxtrace.h" +#include "../../../util/config.h" +#include "../../../util/cpumap.h" +#include "../../../util/debug.h" #include "../../../util/event.h" #include "../../../util/evlist.h" #include "../../../util/evsel.h" #include "../../../util/evsel_config.h" -#include "../../../util/config.h" -#include "../../../util/cpumap.h" #include "../../../util/mmap.h" -#include #include "../../../util/parse-events.h" -#include "../../../util/pmus.h" -#include "../../../util/debug.h" -#include "../../../util/auxtrace.h" #include "../../../util/perf_api_probe.h" +#include "../../../util/pmu.h" +#include "../../../util/pmus.h" #include "../../../util/record.h" +#include "../../../util/session.h" #include "../../../util/target.h" #include "../../../util/tsc.h" -#include // page_size -#include "../../../util/intel-pt.h" -#include #include "cpuid.h" =20 #define KiB(x) ((x) * 1024) --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 80F9638F237 for ; Sat, 25 Apr 2026 22:50:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157418; cv=none; b=ByLa523h3hRHKdOW2v7SVxQN079kh3MrD3SxRDaBEFQnuYiNvSETwN8BkXjzbpw3IgcFNsnrBU7reC+qrLwoXyUUctqyo1ztghu9hGir2jXn+esDHiRUBk7HJkwWOi2ptwvIJ1w2GX9qUk6Tg2Rixg5tQhrBPKkXEvkyvqwcTWE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157418; c=relaxed/simple; bh=/h8ESLHe5S0bbxRN9MN7l0JrDETJedf6AiFuXhBylRE=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=WoBbGx5kS0LCOwYvUnnHFX+RvdTmkyqO8qwPhH4FiV8xol5MQyEqQ9CEFxAchlC+JeqMiUsMaAeioXhgpyX6h1YWu6VpDmvM9t9EFCkx31i0DcISRaNay7D1o7RuEekv+ib3c8lekBLb8hMyDg1fTY1nAwgLfKPoI9EgJg6xmwM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=DTs4fc5I; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="DTs4fc5I" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2de07c12745so25487367eec.1 for ; Sat, 25 Apr 2026 15:50:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157416; x=1777762216; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=mqXJIn8r4IA9Gva4etcbzr/XlHSVW6Lm8pY9GKRmyzg=; b=DTs4fc5IwqpJTTAlQc91lMtkTr9RsPuUvpB0STRaw2TG3J6lVXRpuEhlO5Xa5dgJFQ ojd995w56/Ig140Tm2zl1SBlE/rgmLmLKKUW63GcAqs+2UyfOOFndz9hRqOdMMCNSgTC MmS0ljymi15gb2IS/w6xNq96af5SQzp+wP2Q+AgLz47Z7138ceqYX6tjSzA/vwLMsLCh 9AyJkQG+sCSlrdmavcCDgrGektTRt3SPoujJywLjPLbaBb1JoCyHc6PLjNI3m08+l8ET mp58Y5hYW3HDMkJ8J8WIRVK2/iZLImuLpZnq8qb/Aen4U9jRKRqEECC5UsIKEK/gnEpD sEGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157416; x=1777762216; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=mqXJIn8r4IA9Gva4etcbzr/XlHSVW6Lm8pY9GKRmyzg=; b=ExdJqmrb919p/Ze8hp/s0jwbzcdpHjLgb9mnbOf/d13iVgVz6B29cMulbaPuADsvsl 7wb79ubN4wIj01AImFJFcrHveew5j6RWvbvygRXlIFo38ldqVLSBEAzEcXEkhi7mFpRq C1nSt7uM/8Emv4A2rmgPxT655RLI1d1CROYdExF2tzPrr9JY9Cj3q4UR5h+3w/bWiWa4 qf6QKDzGJjvnVtvMJVonubh+kfN4aOsTdzjnL2jA5UF7B6yxlNdRkwH953FuoKSAy+jJ NGPXRRM9gECvsc5thMtz6kE7fUSPmmyE5FWCuJeC2ac/4nB75KfsIJuEFkwmhcKyfWME sIpg== X-Forwarded-Encrypted: i=1; AFNElJ8RGF5JDxEQUWLhdkDGt1kJqTrhzDjm7VEohv5qqyf6agLhcRKn5vIivDNqS3142WFPUmlpAlwT1JWJGzc=@vger.kernel.org X-Gm-Message-State: AOJu0Yz1d6RRW6q812lDDWFSuwQyOGVF2Hk7KW1/YxLHBGHXpmXeqFsb 4Y2KdswWOkIgm1WupuVExgMKeAuBf8ypYebt2SApPlfSchB8sbtyVq2YN0kITP4hfKhCghbFDE5 EQAyBDszHdg== X-Received: from dycqb7.prod.google.com ([2002:a05:7300:fe87:b0:2df:9320:a159]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:d517:b0:2e0:4edd:64e8 with SMTP id 5a478bee46e88-2e47a2faf4amr23570921eec.24.1777157415407; Sat, 25 Apr 2026 15:50:15 -0700 (PDT) Date: Sat, 25 Apr 2026 15:48:56 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-5-irogers@google.com> Subject: [PATCH v7 04/59] perf tests: Sort includes and add missed explicit dependencies From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Fix missing #includes found while cleaning the evsel/evlist header files. Sort the remaining header files for consistency with the rest of the code. Signed-off-by: Ian Rogers --- tools/perf/tests/hwmon_pmu.c | 14 +++++++++----- tools/perf/tests/mmap-basic.c | 18 +++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/tools/perf/tests/hwmon_pmu.c b/tools/perf/tests/hwmon_pmu.c index 4aa4aac94f09..ada6e445c4c4 100644 --- a/tools/perf/tests/hwmon_pmu.c +++ b/tools/perf/tests/hwmon_pmu.c @@ -1,15 +1,19 @@ // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -#include "debug.h" -#include "evlist.h" #include "hwmon_pmu.h" -#include "parse-events.h" -#include "tests.h" + #include + #include -#include #include #include #include +#include + +#include "debug.h" +#include "evlist.h" +#include "parse-events.h" +#include "pmus.h" +#include "tests.h" =20 static const struct test_event { const char *name; diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index 3313c236104e..8d04f6edb004 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c @@ -1,25 +1,29 @@ // SPDX-License-Identifier: GPL-2.0 #include -#include #include #include + +#include +#include +#include +#include + #include +#include +#include =20 #include "cpumap.h" #include "debug.h" #include "event.h" #include "evlist.h" #include "evsel.h" -#include "thread_map.h" +#include "pmu.h" +#include "pmus.h" #include "tests.h" +#include "thread_map.h" #include "util/affinity.h" #include "util/mmap.h" #include "util/sample.h" -#include -#include -#include -#include -#include =20 /* * This test will generate random numbers of calls to some getpid syscalls, --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9101139022A for ; Sat, 25 Apr 2026 22:50:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157421; cv=none; b=ORsXuBtJ7De1DPBHCnD6LOGv7KQop8pvuzwMQM2l/v8TNwRmameJqBPZjkVGfwDgiG3TNAp692k5YqztCaQNpQ+z+JfMvm13TYyie7n0iZud9WtgaduRlDxDSmjYmu51pLcdbRB9tL1eoSkCv1bgsEXlkHIAeWRJf+39653CNlU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157421; c=relaxed/simple; bh=QHTZx5MPgaaZQHiX00rro2INAj+zmier+jw6hcgWdKU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Wh+7Kubpq4tOGEQIzfEzdTKUYL56Gg6r3eTG0XCqwwgHlx2sIJt4m0/aFEbSnGkCJbI9TNE9ijEvMu7Fj4EW+bdW4GZ1azlQN88RWm7T0p0vnw6XGY+SDP6G3wocWaq3aakfedZ9kuQ0CAmnnp9pL0o1E1vy5YTz+ZxmEFvHskU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=h2dM6I92; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="h2dM6I92" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2ddd8ef5343so8846740eec.1 for ; Sat, 25 Apr 2026 15:50:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157418; x=1777762218; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=PdL5J++jQFS0WI1Hq1Y43G2dZXRWh1hnOIKdQUKwLiY=; b=h2dM6I92bck/DW8hyRZ8Ffq2YnOk6+1aHodFFKa+5y0ryc4eAMuFF+GMX0Lgqw95qu dgWlLkyf0NQK8jRuSX5SZTB65MK8D2U+G84F5iQdftZopP6lUAXCs6/ihpKyAfVYG7in MvA5JHA4aKYy7zd2aSAGxOlUxF+aC9R56m/Y86AkYfXTOpfC6Ba7XiASxcLl/z+je+F9 yjiEqF21tnxvkr61Dxqj1J6VHZOxmdoUg7936xTKZFtua7hPGsAufOrePxYp26Rxg7cP bQbYgtQVFOJpq8DyW6jF8cws+Ycju+dg/krbOIFgcKbi8O95hEFK6ZBIbspjN/Tylx45 nj4Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157418; x=1777762218; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=PdL5J++jQFS0WI1Hq1Y43G2dZXRWh1hnOIKdQUKwLiY=; b=rsd/YAtxiFM/4LI+GUBMpGhdS6MLSWjy8c/MiQML9Hye49YMcNrDpspJNbsUq3EIU9 VdXoX4ePpFhmr3hCRovgXzfqDOQ+/wxNjq1XlNDnTrcZL6hLbY7NIiGIRIisxVzYLtSI 8KHQYr+JojvzfSDjbOr0mxAN/IeQdVzsaMnuZTVkprGKeyxi+5N8xR962xKI3tvokA6M /NSbk8TOtK6eDNNK3oHdnccfM3WCKU5vVDNxMX5injIMPt3Px/mYoO5NrhHVZPJuqcqL JM7icEUVnxrGRvpdF8soO3jvFDOHEcDXPCvNAwuOcB+y5PyjkQwXvVSkJrA00bv4c9oK jFCw== X-Forwarded-Encrypted: i=1; AFNElJ/bGjDrN4uoxrFH2OyndpggpNGmU/bL0ucRWzN2TVuw+FzBfiljxaQDcDaf2SaQB/BhkWeK6tCOyU0vz0Y=@vger.kernel.org X-Gm-Message-State: AOJu0YwZGqEydtc34j+XmReCsbTQQmkhUMIL3knFIitR9GKeL2YckLc7 Z5bckAKY5c6lkGMZdC0yZu+0itn1M2BWK+zZ3FxkIiakT88ZVmTrmaxeNoJT6djJPNhD6uQqNK2 3GlTsx76MYg== X-Received: from dybzy3.prod.google.com ([2002:a05:7301:e103:b0:2df:af:8fde]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:fd09:b0:2da:d4b4:c85a with SMTP id 5a478bee46e88-2e464dad10cmr17831870eec.11.1777157417586; Sat, 25 Apr 2026 15:50:17 -0700 (PDT) Date: Sat, 25 Apr 2026 15:48:57 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-6-irogers@google.com> Subject: [PATCH v7 05/59] perf script: Sort includes and add missed explicit dependencies From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Fix missing #include of pmu.h found while cleaning the evsel/evlist header files. Sort the remaining header files for consistency with the rest of the code. Doing this exposed a missing forward declaration of addr_location in print_insn.h, add this and sort the forward declarations. Signed-off-by: Ian Rogers --- tools/perf/builtin-script.c | 111 ++++++++++++++++++----------------- tools/perf/util/print_insn.h | 5 +- 2 files changed, 60 insertions(+), 56 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index c8ac9f01a36b..853b141a0d50 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -1,74 +1,77 @@ // SPDX-License-Identifier: GPL-2.0 -#include "builtin.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include =20 +#include +#include +#include +#include + +#include "asm/bug.h" +#include "builtin.h" +#include "perf.h" +#include "print_binary.h" +#include "print_insn.h" +#include "ui/ui.h" +#include "util/annotate.h" +#include "util/auxtrace.h" +#include "util/cgroup.h" +#include "util/color.h" #include "util/counts.h" +#include "util/cpumap.h" +#include "util/data.h" #include "util/debug.h" +#include "util/dlfilter.h" #include "util/dso.h" -#include -#include "util/header.h" -#include -#include "util/perf_regs.h" -#include "util/session.h" -#include "util/tool.h" -#include "util/map.h" -#include "util/srcline.h" -#include "util/symbol.h" -#include "util/thread.h" -#include "util/trace-event.h" +#include "util/dump-insn.h" #include "util/env.h" +#include "util/event.h" #include "util/evlist.h" #include "util/evsel.h" #include "util/evsel_fprintf.h" #include "util/evswitch.h" +#include "util/header.h" +#include "util/map.h" +#include "util/mem-events.h" +#include "util/mem-info.h" +#include "util/metricgroup.h" +#include "util/path.h" +#include "util/perf_regs.h" +#include "util/pmu.h" +#include "util/record.h" +#include "util/session.h" #include "util/sort.h" -#include "util/data.h" -#include "util/auxtrace.h" -#include "util/cpumap.h" -#include "util/thread_map.h" +#include "util/srcline.h" #include "util/stat.h" -#include "util/color.h" #include "util/string2.h" +#include "util/symbol.h" #include "util/thread-stack.h" +#include "util/thread.h" +#include "util/thread_map.h" #include "util/time-utils.h" -#include "util/path.h" -#include "util/event.h" -#include "util/mem-info.h" -#include "util/metricgroup.h" -#include "ui/ui.h" -#include "print_binary.h" -#include "print_insn.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "asm/bug.h" -#include "util/mem-events.h" -#include "util/dump-insn.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "util/dlfilter.h" -#include "util/record.h" +#include "util/tool.h" +#include "util/trace-event.h" #include "util/util.h" -#include "util/cgroup.h" -#include "util/annotate.h" -#include "perf.h" =20 -#include #ifdef HAVE_LIBTRACEEVENT #include #endif diff --git a/tools/perf/util/print_insn.h b/tools/perf/util/print_insn.h index 07d11af3fc1c..a54f7e858e49 100644 --- a/tools/perf/util/print_insn.h +++ b/tools/perf/util/print_insn.h @@ -5,10 +5,11 @@ #include #include =20 -struct perf_sample; -struct thread; +struct addr_location; struct machine; struct perf_insn; +struct perf_sample; +struct thread; =20 #define PRINT_INSN_IMM_HEX (1<<0) =20 --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 099E839022A for ; Sat, 25 Apr 2026 22:50:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157426; cv=none; b=l5zFypqPngS4aSVyYgZcMgNLbVbTTPp8XeFooSjeNhI8tCrczlwmxqrygmGs8reUdNF8uPW1QYX95Ik1mHcokhSV18ASf4fuDE2QKajRa6Nl2MhXzpBWg6makKZttY4C3VZY3Tc3fcw11eN/5/GuyRfPoQ5zlcLMyINCPZBjMTo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157426; c=relaxed/simple; bh=bCaeAHwSbM97Ifxo1Dx//tE+XP8ZfBYmMdOmVKj+DdU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=KQPwJnkURV7deuwBqkSv33pKiumFOmDYRKcZK0ZSmwQ6rFY7eHJaNRdkqF/hYxi5OG3WiK7fNoL4piqlayOM5E5XTBll2JNSegRPVV3sSkuAcU+mm7jIF7pWYY1TLhexG8vOIlc93mrzqEgEhhpJerBh+FEK1sZ/xgqGom3Whrs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=oIDJn6Ty; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="oIDJn6Ty" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2eaed3d96d7so860354eec.0 for ; Sat, 25 Apr 2026 15:50:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157424; x=1777762224; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=viZhgTNNP2buRGy766mhoKyGu3Atkn7DPzIJmPMe5kc=; b=oIDJn6TyV+GOJGsMpHeQ90cC9T+AF2fNemDnVYBSSd6j1GWhK0pWnQZHpA8HUsGnED RQMktH5zQV4qw80Hf3fa/nAlsNDztyrzh2NFXv/ruIYes9B+gc/5GloeedcZWjKcp/jv Kv8RvqKvjbMhVcw2magaFNdIOV5RoBwb7yxbqiVjKqRH2DFokzeH5rZV5s9mad3V8GBh Q6TTjMhqBZcFeuvdm34qBxNSp+Uzj0gCfvd6VEDNGu4ewTrAi/ET5dJPDQFCz3vm8sgc LC89LgWc9eMpZCzIk+JB3AET4dDNs7/jgZkw9oDRK8OsBF3FpQzE3Ixe8+gGBc4yFpci gGRw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157424; x=1777762224; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=viZhgTNNP2buRGy766mhoKyGu3Atkn7DPzIJmPMe5kc=; b=Ezn9y9anZ2WJZG+ToPKhXIPCpI4QG0Tz/Ttf1XZi/i1K3Rk+UpH66mkOaBYyp4qPx5 12JgjHBqTWgQtt7dJMDZzSUAX6NZv3xIvEsk9edEWpPIex5QVaAxwyC48do95SnNjhnZ G+CcuU+mGVzGdLGMW0AZVaVUTiSAxs9Xif46cqXXGBHnMhkrPxYK1zc6bI86KptjapsB TUflv8sZIzjt9nExzjUDmYmjkjsfUWkOoCA4J6bu4CkW4meQeu/J4YBKrQENG+Itonbs Hb4iSAmKiaA6sksjMayseXw1HMGkDAaiKrbX10ugHiN46t0LBKvttZeTUIKyezuEDmJI lOWQ== X-Forwarded-Encrypted: i=1; AFNElJ9pIhwPpjsN6hk9gboVkmweV8NNL438Fwy5XmF2MA1ErUuRWq/wNamgAQUKK/MRFrggjq+ej8KfZdx2DQE=@vger.kernel.org X-Gm-Message-State: AOJu0YwheaHNhqoemhP/GcXkz18/GQKCPhK9egbsev5Gx5rMfy0axWCE ChegM8qTRs4VNSc5oUTTYBrCRc4MZddCDJWCpWM6ISqsrR7A9Pgdcpyu8zpf3Hx3kmsKC9LDUuN 6AZxJoX3HgA== X-Received: from dycol2.prod.google.com ([2002:a05:7301:db82:b0:2dd:8e19:2d13]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:230c:b0:2da:10f0:a8df with SMTP id 5a478bee46e88-2e464dad2a6mr18033073eec.12.1777157423970; Sat, 25 Apr 2026 15:50:23 -0700 (PDT) Date: Sat, 25 Apr 2026 15:48:58 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-7-irogers@google.com> Subject: [PATCH v7 06/59] perf util: Sort includes and add missed explicit dependencies From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Fix missing includes found while cleaning the evsel/evlist header files. Sort the remaining header files for consistency with the rest of the code. Signed-off-by: Ian Rogers --- tools/perf/util/bpf_off_cpu.c | 30 +++++----- tools/perf/util/bpf_trace_augment.c | 8 +-- tools/perf/util/evlist.c | 91 +++++++++++++++-------------- tools/perf/util/evsel.c | 75 ++++++++++++------------ tools/perf/util/map.h | 9 ++- tools/perf/util/perf_api_probe.c | 18 +++--- tools/perf/util/s390-sample-raw.c | 19 +++--- tools/perf/util/stat-shadow.c | 20 ++++--- tools/perf/util/stat.c | 16 +++-- 9 files changed, 152 insertions(+), 134 deletions(-) diff --git a/tools/perf/util/bpf_off_cpu.c b/tools/perf/util/bpf_off_cpu.c index a3b699a5322f..48cb930cdd2e 100644 --- a/tools/perf/util/bpf_off_cpu.c +++ b/tools/perf/util/bpf_off_cpu.c @@ -1,23 +1,25 @@ // SPDX-License-Identifier: GPL-2.0 -#include "util/bpf_counter.h" -#include "util/debug.h" -#include "util/evsel.h" -#include "util/evlist.h" -#include "util/off_cpu.h" -#include "util/perf-hooks.h" -#include "util/record.h" -#include "util/session.h" -#include "util/target.h" -#include "util/cpumap.h" -#include "util/thread_map.h" -#include "util/cgroup.h" -#include "util/strlist.h" +#include + #include #include #include -#include =20 +#include "bpf_counter.h" #include "bpf_skel/off_cpu.skel.h" +#include "cgroup.h" +#include "cpumap.h" +#include "debug.h" +#include "evlist.h" +#include "evsel.h" +#include "off_cpu.h" +#include "parse-events.h" +#include "perf-hooks.h" +#include "record.h" +#include "session.h" +#include "strlist.h" +#include "target.h" +#include "thread_map.h" =20 #define MAX_STACKS 32 #define MAX_PROC 4096 diff --git a/tools/perf/util/bpf_trace_augment.c b/tools/perf/util/bpf_trac= e_augment.c index 9e706f0fa53d..a9cf2a77ded1 100644 --- a/tools/perf/util/bpf_trace_augment.c +++ b/tools/perf/util/bpf_trace_augment.c @@ -1,11 +1,11 @@ #include #include =20 -#include "util/debug.h" -#include "util/evlist.h" -#include "util/trace_augment.h" - #include "bpf_skel/augmented_raw_syscalls.skel.h" +#include "debug.h" +#include "evlist.h" +#include "parse-events.h" +#include "trace_augment.h" =20 static struct augmented_raw_syscalls_bpf *skel; static struct evsel *bpf_output; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index ee971d15b3c6..35d65fe50e06 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -5,67 +5,68 @@ * Parts came from builtin-{top,stat,record}.c, see those files for further * copyright notes. */ -#include +#include "evlist.h" + #include #include -#include -#include "cpumap.h" -#include "util/mmap.h" -#include "thread_map.h" -#include "target.h" -#include "dwarf-regs.h" -#include "evlist.h" -#include "evsel.h" -#include "record.h" -#include "debug.h" -#include "units.h" -#include "bpf_counter.h" -#include // page_size -#include "affinity.h" -#include "../perf.h" -#include "asm/bug.h" -#include "bpf-event.h" -#include "util/event.h" -#include "util/string2.h" -#include "util/perf_api_probe.h" -#include "util/evsel_fprintf.h" -#include "util/pmu.h" -#include "util/sample.h" -#include "util/bpf-filter.h" -#include "util/stat.h" -#include "util/util.h" -#include "util/env.h" -#include "util/intel-tpebs.h" -#include "util/metricgroup.h" -#include "util/strbuf.h" #include -#include -#include #include =20 -#include "parse-events.h" -#include - #include -#include -#include -#include -#include -#include - #include +#include #include #include -#include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // page_size +#include +#include #include #include -#include #include +#include =20 -#include +#include "../perf.h" +#include "affinity.h" +#include "asm/bug.h" +#include "bpf-event.h" +#include "bpf-filter.h" +#include "bpf_counter.h" +#include "cpumap.h" +#include "debug.h" +#include "dwarf-regs.h" +#include "env.h" +#include "event.h" +#include "evsel.h" +#include "evsel_fprintf.h" +#include "intel-tpebs.h" +#include "metricgroup.h" +#include "mmap.h" +#include "parse-events.h" +#include "perf_api_probe.h" +#include "pmu.h" +#include "pmus.h" +#include "record.h" +#include "sample.h" +#include "stat.h" +#include "strbuf.h" +#include "string2.h" +#include "target.h" +#include "thread_map.h" +#include "units.h" +#include "util.h" =20 #ifdef LACKS_SIGQUEUE_PROTOTYPE int sigqueue(pid_t pid, int sig, const union sigval value); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 2ee87fd84d3e..e03727d395e9 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -11,68 +11,71 @@ */ #define __SANE_USERSPACE_TYPES__ =20 -#include +#include "evsel.h" + #include #include +#include + +#include #include -#include -#include -#include -#include #include +#include #include +#include +#include #include #include #include #include #include -#include -#include + +#include +#include +#include +#include +#include +#include +#include #include + +#include "../perf-sys.h" #include "asm/bug.h" +#include "bpf-filter.h" #include "bpf_counter.h" #include "callchain.h" #include "cgroup.h" #include "counts.h" +#include "debug.h" +#include "drm_pmu.h" #include "dwarf-regs.h" +#include "env.h" #include "event.h" -#include "evsel.h" -#include "time-utils.h" -#include "util/env.h" -#include "util/evsel_config.h" -#include "util/evsel_fprintf.h" #include "evlist.h" -#include -#include "thread_map.h" -#include "target.h" +#include "evsel_config.h" +#include "evsel_fprintf.h" +#include "hashmap.h" +#include "hist.h" +#include "hwmon_pmu.h" +#include "intel-tpebs.h" +#include "memswap.h" +#include "off_cpu.h" +#include "parse-branch-options.h" #include "perf_regs.h" +#include "pmu.h" +#include "pmus.h" #include "record.h" -#include "debug.h" -#include "trace-event.h" +#include "rlimit.h" #include "session.h" #include "stat.h" #include "string2.h" -#include "memswap.h" -#include "util.h" -#include "util/hashmap.h" -#include "off_cpu.h" -#include "pmu.h" -#include "pmus.h" -#include "drm_pmu.h" -#include "hwmon_pmu.h" +#include "target.h" +#include "thread_map.h" +#include "time-utils.h" #include "tool_pmu.h" #include "tp_pmu.h" -#include "rlimit.h" -#include "../perf-sys.h" -#include "util/parse-branch-options.h" -#include "util/bpf-filter.h" -#include "util/hist.h" -#include -#include -#include -#include "util/intel-tpebs.h" - -#include +#include "trace-event.h" +#include "util.h" =20 #ifdef HAVE_LIBTRACEEVENT #include diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 979b3e11b9bc..fb0279810ae9 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -2,14 +2,13 @@ #ifndef __PERF_MAP_H #define __PERF_MAP_H =20 -#include -#include -#include -#include +#include #include #include -#include + +#include #include + #include =20 struct dso; diff --git a/tools/perf/util/perf_api_probe.c b/tools/perf/util/perf_api_pr= obe.c index 6ecf38314f01..e1904a330b28 100644 --- a/tools/perf/util/perf_api_probe.c +++ b/tools/perf/util/perf_api_probe.c @@ -1,14 +1,18 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#include "perf_api_probe.h" =20 -#include "perf-sys.h" -#include "util/cloexec.h" -#include "util/evlist.h" -#include "util/evsel.h" -#include "util/parse-events.h" -#include "util/perf_api_probe.h" -#include #include =20 +#include + +#include "cloexec.h" +#include "evlist.h" +#include "evsel.h" +#include "parse-events.h" +#include "perf-sys.h" +#include "pmu.h" +#include "pmus.h" + typedef void (*setup_probe_fn_t)(struct evsel *evsel); =20 static int perf_do_probe_api(setup_probe_fn_t fn, struct perf_cpu cpu, con= st char *str) diff --git a/tools/perf/util/s390-sample-raw.c b/tools/perf/util/s390-sampl= e-raw.c index c6ae0ae8d86a..6bf0edf80d4e 100644 --- a/tools/perf/util/s390-sample-raw.c +++ b/tools/perf/util/s390-sample-raw.c @@ -12,25 +12,26 @@ * sample was taken from. */ =20 -#include +#include #include #include -#include =20 -#include +#include #include #include -#include +#include +#include =20 +#include "color.h" #include "debug.h" -#include "session.h" #include "evlist.h" -#include "color.h" #include "hashmap.h" -#include "sample-raw.h" +#include "pmu.h" +#include "pmus.h" #include "s390-cpumcf-kernel.h" -#include "util/pmu.h" -#include "util/sample.h" +#include "sample-raw.h" +#include "sample.h" +#include "session.h" =20 static size_t ctrset_size(struct cf_ctrset_entry *set) { diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index bc2d44df7baf..48524450326d 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c @@ -2,20 +2,24 @@ #include #include #include -#include "evsel.h" -#include "stat.h" + +#include + +#include "cgroup.h" #include "color.h" #include "debug.h" -#include "pmu.h" -#include "rblist.h" #include "evlist.h" +#include "evsel.h" #include "expr.h" -#include "metricgroup.h" -#include "cgroup.h" -#include "units.h" +#include "hashmap.h" #include "iostat.h" -#include "util/hashmap.h" +#include "metricgroup.h" +#include "pmu.h" +#include "pmus.h" +#include "rblist.h" +#include "stat.h" #include "tool_pmu.h" +#include "units.h" =20 static bool tool_pmu__is_time_event(const struct perf_stat_config *config, const struct evsel *evsel, int *tool_aggr_idx) diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 14d169e22e8f..66eb9a66a4f7 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -1,21 +1,25 @@ // SPDX-License-Identifier: GPL-2.0 +#include "stat.h" + #include -#include #include #include #include + +#include +#include + #include "counts.h" #include "cpumap.h" #include "debug.h" +#include "evlist.h" +#include "evsel.h" +#include "hashmap.h" #include "header.h" -#include "stat.h" +#include "pmu.h" #include "session.h" #include "target.h" -#include "evlist.h" -#include "evsel.h" #include "thread_map.h" -#include "util/hashmap.h" -#include =20 void update_stats(struct stats *stats, u64 val) { --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 112FC38E5DA for ; Sat, 25 Apr 2026 22:50:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157428; cv=none; b=sgx1fe1H0Bc7xydEc3C+5t5q+4kOLbF8XRYmsM5PhUC1YUjeCHS6i3Z2hwuki18+2FzY4u4HcqoSJH+5z3GxqZ3pnzWT/FyAzjdugrqZTtU68BTfOqfhMsm5lMJ0wGugz0kIIP40NTzb5HxlXffLh49NrxayfGVOeQCsC03pIN0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157428; c=relaxed/simple; bh=E1A9rOUkigl47RQgwxPU7BZg+a2AkxhYpc7Hpry26ZU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=uMrJL5ytcMiPS/gbcDzjGl0zAdjbJfAV3Ww4EHTqmPuVzd0BhloBPnc0WMBGvdLZ68P+Woa4VJ5BJek/ACAuz0wb8Shm01ivtbwmIiWVPfIlapNIn/lKIkM62ps7enYeaKgfecAE3WceXSLRKsxEtPBfGzc9ni9gpdmyzj8jcMg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=wgCznQXf; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="wgCznQXf" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-12c8d7d4a79so7470711c88.1 for ; Sat, 25 Apr 2026 15:50:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157426; x=1777762226; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Pih4PvjlzSX23VUBfhxJgrdaziaLdcdiDaL3PE08L8Q=; b=wgCznQXfY6MDRwspWi9+E5grmFcCWBvVx2FAuM8RX554wVHbWbTmXBJfFpdWImUism UfUsxb4MDKEhFBQcIVjjTEXOgj4nerSJHg7sa+XTYpsVEx8bXNN84BJ22n4kHgScbzxP MY2rVNdiVhR5K4SO8MW+etCf7kQHqehgSprYaEVfTWjLKgOuSSKLwGNvaa9xQYtqAv7g egYrEJqNYQkmOVJl8KdHaP6pJmoTy0LNpeTwmIRsm7Vk7Td6AwBB03fs5WlCqgQ5MR+9 o/Wf6/17eliaYkUG1EVJP/RfxTLoygzx4BWT8rfa9eaH3Hwih+HSssGEztltou43jSUa GUYg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157426; x=1777762226; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Pih4PvjlzSX23VUBfhxJgrdaziaLdcdiDaL3PE08L8Q=; b=G+i0uRU6XAb+pp9TH9BPtBIK4+uhPMHMXp8R7WOwuRPJyBveHTjmbXMqsQXI9fsWwL RBd0Z2HAg03FGqLfhoMgoHk5+/JZOjS1M7u6A+mwhtjtOtZ3Vwidc0+wBi6EgUG3ygID F1USF71a5rJ5xPTcXAFiALhR/PD9eJUCWrFta0C2Uy2dW327Q61+lKFxb7wzAHKRb+Lx 4SOypdXtr77BxFpAad5pPMdapH45/NLkG3yQwinYtmBT5qN0PmOSOwvDM/8/D1zv+uhy NIdGPpDZUH0T1U7sqcfh8gH4F7Y0Ml9tU2RGdUi2xqO654lbqlvC9S/cqpDce/+rYi9F +Tdg== X-Forwarded-Encrypted: i=1; AFNElJ+RqUwpgmnzCXLpCbUEooHAl5ZB/rGib36Tx73973iQEOxjpD9vTYqT0/h2rVareeDg9P4FxpS8iYlR8tU=@vger.kernel.org X-Gm-Message-State: AOJu0Yxdkd8t6dRLYvZaW0UhvdZGLSHi4DP6XftNnp0pxdXU5u8Dc2tB vKD0KpzOlo4kAbU9KNGbOIOQ5lwvKTFUuvSiVGmxlR6FTKLAf0MS+n11oj/EufEVCL881NJ93I4 xKpV1yXoJnw== X-Received: from dlec6-n2.prod.google.com ([2002:a05:701b:4286:20b0:12d:bbdf:edcb]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:90a:b0:128:d715:b717 with SMTP id a92af1059eb24-12c73f82878mr18014490c88.13.1777157425996; Sat, 25 Apr 2026 15:50:25 -0700 (PDT) Date: Sat, 25 Apr 2026 15:48:59 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-8-irogers@google.com> Subject: [PATCH v7 07/59] perf python: Add missed explicit dependencies From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Fix missing #include of pmus.h found while cleaning the evsel/evlist header files. Signed-off-by: Ian Rogers --- tools/perf/util/python.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index cc1019d29a5d..1e6c99efff90 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -5,26 +5,29 @@ #include #include #include -#ifdef HAVE_LIBTRACEEVENT -#include -#endif +#include #include + #include "callchain.h" #include "counts.h" +#include "event.h" #include "evlist.h" #include "evsel.h" -#include "event.h" #include "expr.h" +#include "metricgroup.h" +#include "mmap.h" +#include "pmus.h" #include "print_binary.h" #include "record.h" #include "strbuf.h" #include "thread_map.h" #include "tp_pmu.h" #include "trace-event.h" -#include "metricgroup.h" -#include "mmap.h" #include "util/sample.h" -#include + +#ifdef HAVE_LIBTRACEEVENT +#include +#endif =20 PyMODINIT_FUNC PyInit_perf(void); =20 --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2AC36391E45 for ; Sat, 25 Apr 2026 22:50:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157430; cv=none; b=ZZQ1fq5C2YuChSWRN8ShtuQF0XXBybuIDOXv4Ad/Szf0Vyk8Pb+MeBj94cuA+jqxgt2TbaIcxAT0cWWv6O5SJs9Wuv/BRLkWC/MPiSRDDf/ByzVQR+/342YTgYqoRXlde1TSloi7VOW6C4089R3f1sab1eeyfUNW1hnnOuPEhhg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157430; c=relaxed/simple; bh=FP5o9n90ZT5agsSGJdl1masLAbe0pH/IJ+EbWllzEcE=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=dgAYzM0PT1hzui1pEFFCdDFYTuy/Lm40xvLQC1EmXwuZrnm6/YuSqG5xaiSVYJCNbyEFt0akUouQhC2spOIGafGFjIpAER8Yq8J6MfsQ/sTVO+RGC2QRH83mn34qbU6F9bg71P8KCES/doE/oiKwNfJp7+ylnB94AEur7ogLqII= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=SuVDRJQe; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="SuVDRJQe" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2eaf70f3b5fso734208eec.0 for ; Sat, 25 Apr 2026 15:50:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157428; x=1777762228; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=niT+j+ygae4DUDVZvlLCzSKgJX/QJcllkfXgy9y3G00=; b=SuVDRJQeiP5hEHiYJ9oWwgHp6+QSVqJZPsM/h8GIBA+5zsR+j3HecQ2l6kt93iCUx/ 4aPtN42T5Ss5oh1/RY7Vie/9ckmDT0v1K8KmqNp1HQreppf6Tp7qWIGS5CMcUejli+EU ppoJoPUOx9u3MHoDQtfF75pVRoT13xOUbZu5AlfQVTXDvbDw5PHe1zM9zBrd3Y2rxRIE ET5q2rpGUDIGkCqoTCEhPk/ukOpJUdZzQ5LqBXpVZYSIo/YrfGeejh/+excJ3gO0sO07 z+VdHbEkZ3hvHm+yE2rqMLiIBN3Zlo1voqxEdCda/lFq1T9P35CtXkru5eBcreTj5dS+ uIBA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157428; x=1777762228; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=niT+j+ygae4DUDVZvlLCzSKgJX/QJcllkfXgy9y3G00=; b=RGSWMdN58qrk9AmMKWlOWl4ZxR4vmWEXm/oaA6KeYG3My2WkgqH5L38uTBgwgA9oIu MOU/N+lYguoVqasoAxaogH+7TDugZYB1zbrWC/66iJaeJsgCJV1O4VfuN3EfpO9b5iHp WgAAyW8n24pBJrfj6ZpeXYDDSm/VYll5suWMkB4BF+cLGF4shLqWpFgv59Od/VY2ndVu 51G8Ftmb1ZU2sd3M9x5Iq6Smej3Xg+k/47De/piH9Vzwz4Cksv3WbM4K0RZCKZMQk1t4 lJ2vSx7DSJ1cSYz4Yd0N++xdq0YSOJbmRntzgcDX9OsfqvsEfrTK1uQA5IUUnK/xYfvy 5APA== X-Forwarded-Encrypted: i=1; AFNElJ8V31kKeqwXpyBhgS5yjJP5dO8OwGUxby60bXewy3bk4PNxk3KYQAVLskKIoK1IaQkU/K/o5FleSquqNmA=@vger.kernel.org X-Gm-Message-State: AOJu0YzJeLr8NJmlswUcD9IcOXShwmNFJHkgV/Quz2qXb/cMkTtbOlpB zuuKEEoZQqPPtcyyC9p/nG0CuXORJKPQ1FbDADXJU2lNCAdo7QYzrNepBKe2Mp0sQ7+qXWs2abV RwOWdjTs9Yg== X-Received: from dlbpu10.prod.google.com ([2002:a05:7022:e88a:b0:12b:fba9:5eb0]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:4199:b0:12c:2cf8:2f30 with SMTP id a92af1059eb24-12c73f83616mr19958155c88.15.1777157428055; Sat, 25 Apr 2026 15:50:28 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:00 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-9-irogers@google.com> Subject: [PATCH v7 08/59] perf evsel/evlist: Avoid unnecessary #includes From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Use forward declarations and remove unnecessary #includes in evsel.h. Sort the forward declarations in evsel.h and evlist.h. Signed-off-by: Ian Rogers --- tools/perf/util/evlist.h | 15 +++++++++------ tools/perf/util/evsel.h | 20 +++++++++++--------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index e507f5f20ef6..e54761c670b6 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -2,29 +2,32 @@ #ifndef __PERF_EVLIST_H #define __PERF_EVLIST_H 1 =20 +#include + #include #include -#include #include +#include +#include +#include + #include #include #include #include + #include "affinity.h" #include "events_stats.h" #include "evsel.h" #include "rblist.h" -#include -#include -#include =20 -struct pollfd; -struct thread_map; struct perf_cpu_map; struct perf_stat_config; +struct pollfd; struct record_opts; struct strbuf; struct target; +struct thread_map; =20 /* * State machine of bkw_mmap_state: diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 339b5c08a33d..b099c8e5dd86 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -2,28 +2,30 @@ #ifndef __PERF_EVSEL_H #define __PERF_EVSEL_H 1 =20 -#include #include -#include + +#include #include #include +#include + #include #include + #include "symbol_conf.h" -#include "pmus.h" -#include "pmu.h" =20 +struct bperf_follower_bpf; +struct bperf_leader_bpf; +struct bpf_counter_ops; struct bpf_object; struct cgroup; +struct hashmap; struct perf_counts; +struct perf_pmu; struct perf_stat_config; struct perf_stat_evsel; -union perf_event; -struct bpf_counter_ops; struct target; -struct hashmap; -struct bperf_leader_bpf; -struct bperf_follower_bpf; +union perf_event; =20 typedef int (evsel__sb_cb_t)(union perf_event *event, void *data); =20 --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 825BC39282F for ; Sat, 25 Apr 2026 22:50:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157433; cv=none; b=ZWJ24YT8GCyr783HFRieLkQ6k485VtoSrSTNIZdx08teB4I/BVEsxnPGK3lceVY26Ul44/8QwSr9/qCuJ2o59PVOjSuHiRFjswSjj3bcNTTbM9IoPm0uVOK5x28C5i4GAewgwvN2bguX+D8LZe3jK0us6SsuYxIxjwUuw66Gjq0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157433; c=relaxed/simple; bh=gfLLI2JWYrhGkycbda+k+ahRBIJmlPsVfm7fy7d7O8E=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=UMqjj2XTkfJNSAgmmpYStY+HBPfKv64HSqyBY6fplj/+u7ywVlqf5Q4JyQ+kw6ho4DDtEufIUYYX/S/mlwLE1Lb50kythfBODDQ/PvsRROSm/DAcGnHC9fI1Kjr/YdPg4RUbRnx7OAxGcknfrlWlnkiRTl7tAkGe2KR38Y5yEVQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=ndSBOmTo; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="ndSBOmTo" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2d93379001eso20019705eec.1 for ; Sat, 25 Apr 2026 15:50:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157431; x=1777762231; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=22olXlLY2a/aW3iIRalXE3Od5F0yl9bqDC7eX7ett48=; b=ndSBOmTo4OIZiqqqmgnw5O0jTEjAxVZzuTABmO00wKA6F9EJbM9dqDoviXIiZ2Mwrh Tuz9sN3w/rX+i94Iv5jL2zP9weiybLy5LlsZPdJ7Io56wma3sowHYxaoBLoDmTBG8GKf G8SQffwQPcJIaqA240FtrlRaxakqZWLftlkUHuMJKXPyQJs7fy/YsxcgJmJJ1fDnQG79 FHrTZxZt+4rXBa2jR9T0ZHo7GGfmhk0APUYrmiLmuhPFAXiuUkD6OJEPMCZTm8OvD4BG B7dyLemdQvwhDFMJzQXi04f55xMe/i4eGfJ/6yRs1Y9jh2pDV/7cjcV6GqdpD+6I0jgn Pjng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157431; x=1777762231; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=22olXlLY2a/aW3iIRalXE3Od5F0yl9bqDC7eX7ett48=; b=TLJ81IWUzFg7DnvEaTvwcEc9g2Clju6DAmBWd6cHaV7ktrWaBkIMcEVat/1yIeHid4 GXHvonwbMeyYoqvizHksTpluwr83jamCJFWMtm0OQM2AhMpd2fphzeRYT+202EselIBC LoULnEygi3ii3f6TiwN7pJ/++ttQsjDci1QJ59pKra2zZex9cnpmhfOgi9HtS/h31Gbv BxK6iVqi5gr8S24WjTTO9qg/aAZmT9mGazuvZjjcyuHczbka1gqVUSmum8d1/7guXGVI sw8J4zyuuBXRUCGVQrUlxK3S7m9/ob5iFcuJDNFcTHcvZd6OKIgrhGhmD6ihu/jj7rcd jAow== X-Forwarded-Encrypted: i=1; AFNElJ8JBz71nBrbgpGFoaKd22R4p5Xkm9YQhQJagvCa7itzYlejB3a0jRu5djBiXWsBMdMEXs6P9sSt3J3N9Hw=@vger.kernel.org X-Gm-Message-State: AOJu0YzOkK82G3CfI+/0/08xmo07cYn8rjpfNeYtSvUjbsh6sEMskcib WTLX6+ZYYA5uyLgIjhIqJKK3vtrwlSNUqVuO4fXmZxcnusqxVjkWl9QTd6zngPVoWw8zPqMhGxw pv8t9EV9vQA== X-Received: from dyjf14.prod.google.com ([2002:a05:7300:680e:b0:2d9:9fa3:5aa6]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:cc14:b0:2c6:1557:9997 with SMTP id 5a478bee46e88-2e478839eb6mr19777325eec.14.1777157430513; Sat, 25 Apr 2026 15:50:30 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:01 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-10-irogers@google.com> Subject: [PATCH v7 09/59] perf data: Add open flag From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Avoid double opens and ensure only open files are closed. This addresses some issues with python integration where the data file wants to be opened before being given to a session. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- Changes in v2: 1. Fixed File Rotation: In perf_data__switch() , I added data->open =3D false; after the file is closed. This ensures that the subsequent perf_data__open() call will not exit early and will successfully open the new file. 2. Fixed Memory Leak: In open_dir() , I added a call to zfree(&data->file.path) if mkdir() fails, preventing the leak of the path string. --- tools/perf/util/data.c | 26 ++++++++++++++++++++++---- tools/perf/util/data.h | 4 +++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c index 94dc534a7386..17baf71897d1 100644 --- a/tools/perf/util/data.c +++ b/tools/perf/util/data.c @@ -346,8 +346,10 @@ static int open_dir(struct perf_data *data) return -1; =20 if (perf_data__is_write(data) && - mkdir(data->path, S_IRWXU) < 0) + mkdir(data->path, S_IRWXU) < 0) { + zfree(&data->file.path); return -1; + } =20 ret =3D open_file(data); =20 @@ -360,9 +362,16 @@ static int open_dir(struct perf_data *data) =20 int perf_data__open(struct perf_data *data) { - if (check_pipe(data)) + int ret; + + if (data->open) return 0; =20 + if (check_pipe(data)) { + data->open =3D true; + return 0; + } + /* currently it allows stdio for pipe only */ data->file.use_stdio =3D false; =20 @@ -375,16 +384,24 @@ int perf_data__open(struct perf_data *data) if (perf_data__is_read(data)) data->is_dir =3D is_dir(data); =20 - return perf_data__is_dir(data) ? - open_dir(data) : open_file_dup(data); + ret =3D perf_data__is_dir(data) ? open_dir(data) : open_file_dup(data); + + if (!ret) + data->open =3D true; + + return ret; } =20 void perf_data__close(struct perf_data *data) { + if (!data->open) + return; + if (perf_data__is_dir(data)) perf_data__close_dir(data); =20 perf_data_file__close(&data->file); + data->open =3D false; } =20 static ssize_t perf_data_file__read(struct perf_data_file *file, void *buf= , size_t size) @@ -457,6 +474,7 @@ int perf_data__switch(struct perf_data *data, =20 if (!at_exit) { perf_data_file__close(&data->file); + data->open =3D false; ret =3D perf_data__open(data); if (ret < 0) goto out; diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h index 8299fb5fa7da..76f57f60361f 100644 --- a/tools/perf/util/data.h +++ b/tools/perf/util/data.h @@ -50,6 +50,8 @@ struct perf_data { const char *path; /** @file: Underlying file to be used. */ struct perf_data_file file; + /** @open: Has the file or directory been opened. */ + bool open; /** @is_pipe: Underlying file is a pipe. */ bool is_pipe; /** @is_dir: Underlying file is a directory. */ @@ -59,7 +61,7 @@ struct perf_data { /** @in_place_update: A file opened for reading but will be written to. */ bool in_place_update; /** @mode: Read or write mode. */ - enum perf_data_mode mode; + enum perf_data_mode mode:8; =20 struct { /** @version: perf_dir_version. */ --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 47125393DDA for ; Sat, 25 Apr 2026 22:50:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157438; cv=none; b=PuPKz+ej659uarRs12LMnynxrt5aZs3HHh7F1D+VkXYv9m1ShfTZ3xnLaDtvMEuh+ZvJ0wzH4bbR6aj3MDC9pKW2vd/WAjL16hh1phM6hTxMV3//2HlYkTPUB+KqrjNErKwZRpxJOT4C+ADe79dt1Pbd+Gu5o9Y0fqC4hryX6Bc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157438; c=relaxed/simple; bh=NabNCVSMCJdRzuSXhuIpn6bpdJC4iaC3GMtQEny/mEo=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=mZfa/xrNEjQjRGwtqGBu6x6JRFjmlMm+Uc97lpEOhbZMVpnqCwmPBET6ediQ6rZTxL85+cRhLqz1ftpOHWtwxXwuQGFJcqGpIFRq0LLxjLWTiq5Lb28vRFRVGvV5OgkbZAyCxMsXuWrjMm9ODeVdOa2MEIZpi0p+R3hUYjjc1xE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=RuDRpkeI; arc=none smtp.client-ip=209.85.216.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="RuDRpkeI" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-3594620fe97so20178097a91.1 for ; Sat, 25 Apr 2026 15:50:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157432; x=1777762232; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=4js4mpe0AbRxh4eFHPAMoMYLQBEy3Kp/ZgYzP9W/zxc=; b=RuDRpkeID5kFlHS27yLnHDQU4oMdYujX3ACTSKtpWqkLYOeeKxggPAy2Ytu73JgbCQ N3TTouARVjH+Ug2OzRjNHoipppfi+nT0woGRV0zk8JaG7T/9DZ/OPViSED9I4d+N0/x9 /Sa4mjT8ZnyDyK9RnhQZJ0zWhsyfvVRCgcxSguWsUqrX4wHaaABEIa27PbEdoZud9WJq UvccKbDG5aZbkn5UlIKlrNn4qXCONiB+7WsuVO/WVT7sGtR0sALrZ8KtNgMIy83QdPX1 P+FoLNxuY3WTUQKXAB4dCOWhMEIT1P0wdHPf24MByPOyGuslrsplJlI6b3MvzDCh7JwP tBgA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157432; x=1777762232; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=4js4mpe0AbRxh4eFHPAMoMYLQBEy3Kp/ZgYzP9W/zxc=; b=Dy7oJwqG6AQav57jt/HKE3g9HmaRMkuY/7KpuS1asM1lVrgSruCgiQK2XfQ9jAbeb7 fEdnUpLH3nod9murQzJkJffv3HR13flo9McVhQezGcDGD1wGlOjkYkBB8NYhjrjJ2xDZ +jxT6EG4BKF4zo/FXGGQQ8C4kZJsT60ic4M8hslmZf0ffIYaUSjnw+DOXF5ycCp62a8Y pUDRDZPKlyqDZFjqkyk6WdCKotdotBrEtDFp3G8Zleot/Go1gnrz7f+GPuPLSjSs2Bgw n0ai3+YC3BqRQScAkuN6PLOzTJ4el1btRiV6xCdAwk9PxGlUcLpRINCwzGzyxJFbyOV4 iy7A== X-Forwarded-Encrypted: i=1; AFNElJ834tli6x+tjjuLbzvLqh9oT/0Rl8Kl/ug2M6D3OxGEnGT6AiL61VHuBGK2Tb0FHKyWcQiTXMFTkaYYZPw=@vger.kernel.org X-Gm-Message-State: AOJu0YxIVaZ4leDFrzPhoUBmYwl8NLldji4NQOHIpizPv7LQRO/jmkRY uVp5Th26TuTqTvIr9+XKfYJx4e938S1qKeLa8W6koeDodZqtRKYmtGBBbeo6GMvZLuW3aiBANBk FhkNsj8jETA== X-Received: from pgke24.prod.google.com ([2002:a63:f558:0:b0:c79:7975:38c5]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a20:9188:b0:394:518c:eb89 with SMTP id adf61e73a8af0-3a08d6782f7mr44019752637.2.1777157432403; Sat, 25 Apr 2026 15:50:32 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:02 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-11-irogers@google.com> Subject: [PATCH v7 10/59] perf evlist: Add reference count From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This a no-op for most of the perf tool. The reference count is set to 1 at allocation, the put will see the 1, decrement it and perform the delete. The purpose for adding the reference count is for the python code. Prior to this change the python code would clone evlists, but this has issues if events are opened, etc. This change adds a reference count for the evlists and a later change will add it to evsels. The combination is needed for the python code to operate correctly (not hit asserts in the evsel clone), but the changes are broken apart for the sake of smaller patches. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: Added evlist__put to pyrf_evlist__init in case init is called more than once. I double-checked trace__replay() and confirmed that trace->evlist is not assigned to session->evlist in that function. trace__replay creates a new session and uses its own evlist for processing file events, leaving trace->evlist pointing to the empty list created at startup. Therefore, the evlist__put(trace->evlist) call in trace__exit() is safe and correct to avoid leaking that empty list. v7: - Added pyrf_evlist__new to zero-initialize pevlist->evlist to fix crash on re-initialization. --- tools/perf/arch/x86/tests/hybrid.c | 2 +- tools/perf/arch/x86/tests/topdown.c | 2 +- tools/perf/arch/x86/util/iostat.c | 2 +- tools/perf/bench/evlist-open-close.c | 18 +- tools/perf/builtin-ftrace.c | 8 +- tools/perf/builtin-kvm.c | 4 +- tools/perf/builtin-lock.c | 2 +- tools/perf/builtin-record.c | 4 +- tools/perf/builtin-sched.c | 6 +- tools/perf/builtin-script.c | 2 +- tools/perf/builtin-stat.c | 10 +- tools/perf/builtin-top.c | 52 ++--- tools/perf/builtin-trace.c | 26 +-- tools/perf/tests/backward-ring-buffer.c | 18 +- tools/perf/tests/code-reading.c | 4 +- tools/perf/tests/event-times.c | 4 +- tools/perf/tests/event_update.c | 2 +- tools/perf/tests/evsel-roundtrip-name.c | 8 +- tools/perf/tests/expand-cgroup.c | 8 +- tools/perf/tests/hists_cumulate.c | 2 +- tools/perf/tests/hists_filter.c | 2 +- tools/perf/tests/hists_link.c | 2 +- tools/perf/tests/hists_output.c | 2 +- tools/perf/tests/hwmon_pmu.c | 2 +- tools/perf/tests/keep-tracking.c | 2 +- tools/perf/tests/mmap-basic.c | 18 +- tools/perf/tests/openat-syscall-tp-fields.c | 18 +- tools/perf/tests/parse-events.c | 4 +- tools/perf/tests/parse-metric.c | 4 +- tools/perf/tests/parse-no-sample-id-all.c | 2 +- tools/perf/tests/perf-record.c | 18 +- tools/perf/tests/perf-time-to-tsc.c | 2 +- tools/perf/tests/pfm.c | 4 +- tools/perf/tests/pmu-events.c | 6 +- tools/perf/tests/pmu.c | 4 +- tools/perf/tests/sw-clock.c | 14 +- tools/perf/tests/switch-tracking.c | 2 +- tools/perf/tests/task-exit.c | 14 +- tools/perf/tests/tool_pmu.c | 2 +- tools/perf/tests/topology.c | 2 +- tools/perf/util/cgroup.c | 4 +- tools/perf/util/data-convert-bt.c | 2 +- tools/perf/util/evlist.c | 20 +- tools/perf/util/evlist.h | 7 +- tools/perf/util/expr.c | 2 +- tools/perf/util/header.c | 12 +- tools/perf/util/metricgroup.c | 6 +- tools/perf/util/parse-events.c | 4 +- tools/perf/util/perf_api_probe.c | 2 +- tools/perf/util/python.c | 212 ++++++++------------ tools/perf/util/record.c | 2 +- tools/perf/util/session.c | 2 +- tools/perf/util/sideband_evlist.c | 16 +- 53 files changed, 279 insertions(+), 320 deletions(-) diff --git a/tools/perf/arch/x86/tests/hybrid.c b/tools/perf/arch/x86/tests= /hybrid.c index e221ea104174..dfb0ffc0d030 100644 --- a/tools/perf/arch/x86/tests/hybrid.c +++ b/tools/perf/arch/x86/tests/hybrid.c @@ -268,7 +268,7 @@ static int test_event(const struct evlist_test *e) ret =3D e->check(evlist); } parse_events_error__exit(&err); - evlist__delete(evlist); + evlist__put(evlist); =20 return ret; } diff --git a/tools/perf/arch/x86/tests/topdown.c b/tools/perf/arch/x86/test= s/topdown.c index 3ee4e5e71be3..2d0ae5b0d76c 100644 --- a/tools/perf/arch/x86/tests/topdown.c +++ b/tools/perf/arch/x86/tests/topdown.c @@ -56,7 +56,7 @@ static int event_cb(void *state, struct pmu_event_info *i= nfo) *ret =3D TEST_FAIL; } } - evlist__delete(evlist); + evlist__put(evlist); return 0; } =20 diff --git a/tools/perf/arch/x86/util/iostat.c b/tools/perf/arch/x86/util/i= ostat.c index 7442a2cd87ed..e0417552b0cb 100644 --- a/tools/perf/arch/x86/util/iostat.c +++ b/tools/perf/arch/x86/util/iostat.c @@ -337,7 +337,7 @@ int iostat_prepare(struct evlist *evlist, struct perf_s= tat_config *config) if (evlist->core.nr_entries > 0) { pr_warning("The -e and -M options are not supported." "All chosen events/metrics will be dropped\n"); - evlist__delete(evlist); + evlist__put(evlist); evlist =3D evlist__new(); if (!evlist) return -ENOMEM; diff --git a/tools/perf/bench/evlist-open-close.c b/tools/perf/bench/evlist= -open-close.c index faf9c34b4a5d..304929d1f67f 100644 --- a/tools/perf/bench/evlist-open-close.c +++ b/tools/perf/bench/evlist-open-close.c @@ -76,7 +76,7 @@ static struct evlist *bench__create_evlist(char *evstr, c= onst char *uid_str) parse_events_error__exit(&err); pr_err("Run 'perf list' for a list of valid events\n"); ret =3D 1; - goto out_delete_evlist; + goto out_put_evlist; } parse_events_error__exit(&err); if (uid_str) { @@ -85,24 +85,24 @@ static struct evlist *bench__create_evlist(char *evstr,= const char *uid_str) if (uid =3D=3D UINT_MAX) { pr_err("Invalid User: %s", uid_str); ret =3D -EINVAL; - goto out_delete_evlist; + goto out_put_evlist; } ret =3D parse_uid_filter(evlist, uid); if (ret) - goto out_delete_evlist; + goto out_put_evlist; } ret =3D evlist__create_maps(evlist, &opts.target); if (ret < 0) { pr_err("Not enough memory to create thread/cpu maps\n"); - goto out_delete_evlist; + goto out_put_evlist; } =20 evlist__config(evlist, &opts, NULL); =20 return evlist; =20 -out_delete_evlist: - evlist__delete(evlist); +out_put_evlist: + evlist__put(evlist); return NULL; } =20 @@ -151,7 +151,7 @@ static int bench_evlist_open_close__run(char *evstr, co= nst char *uid_str) evlist->core.nr_entries, evlist__count_evsel_fds(evlist)); printf(" Number of iterations:\t%d\n", iterations); =20 - evlist__delete(evlist); + evlist__put(evlist); =20 for (i =3D 0; i < iterations; i++) { pr_debug("Started iteration %d\n", i); @@ -162,7 +162,7 @@ static int bench_evlist_open_close__run(char *evstr, co= nst char *uid_str) gettimeofday(&start, NULL); err =3D bench__do_evlist_open_close(evlist); if (err) { - evlist__delete(evlist); + evlist__put(evlist); return err; } =20 @@ -171,7 +171,7 @@ static int bench_evlist_open_close__run(char *evstr, co= nst char *uid_str) runtime_us =3D timeval2usec(&diff); update_stats(&time_stats, runtime_us); =20 - evlist__delete(evlist); + evlist__put(evlist); pr_debug("Iteration %d took:\t%" PRIu64 "us\n", i, runtime_us); } =20 diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c index 8a7dbfb14535..676239148b87 100644 --- a/tools/perf/builtin-ftrace.c +++ b/tools/perf/builtin-ftrace.c @@ -1999,20 +1999,20 @@ int cmd_ftrace(int argc, const char **argv) =20 ret =3D evlist__create_maps(ftrace.evlist, &ftrace.target); if (ret < 0) - goto out_delete_evlist; + goto out_put_evlist; =20 if (argc) { ret =3D evlist__prepare_workload(ftrace.evlist, &ftrace.target, argv, false, ftrace__workload_exec_failed_signal); if (ret < 0) - goto out_delete_evlist; + goto out_put_evlist; } =20 ret =3D cmd_func(&ftrace); =20 -out_delete_evlist: - evlist__delete(ftrace.evlist); +out_put_evlist: + evlist__put(ftrace.evlist); =20 out_delete_filters: delete_filter_func(&ftrace.filters); diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 0c5e6b3aac74..d88855e3c7b4 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -1811,7 +1811,7 @@ static struct evlist *kvm_live_event_list(void) =20 out: if (err) { - evlist__delete(evlist); + evlist__put(evlist); evlist =3D NULL; } =20 @@ -1942,7 +1942,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, out: perf_session__delete(kvm->session); kvm->session =3D NULL; - evlist__delete(kvm->evlist); + evlist__put(kvm->evlist); =20 return err; } diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 5585aeb97684..c40d070f6c36 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -2145,7 +2145,7 @@ static int __cmd_contention(int argc, const char **ar= gv) =20 out_delete: lock_filter_finish(); - evlist__delete(con.evlist); + evlist__put(con.evlist); lock_contention_finish(&con); perf_session__delete(session); perf_env__exit(&host_env); diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4a5eba498c02..b4fffa936e01 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -4289,7 +4289,7 @@ int cmd_record(int argc, const char **argv) goto out; =20 evlist__splice_list_tail(rec->evlist, &def_evlist->core.entries); - evlist__delete(def_evlist); + evlist__put(def_evlist); } =20 if (rec->opts.target.tid && !rec->opts.no_inherit_set) @@ -4397,7 +4397,7 @@ int cmd_record(int argc, const char **argv) auxtrace_record__free(rec->itr); out_opts: evlist__close_control(rec->opts.ctl_fd, rec->opts.ctl_fd_ack, &rec->opts.= ctl_fd_close); - evlist__delete(rec->evlist); + evlist__put(rec->evlist); return err; } =20 diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 555247568e7a..d683642ab4e0 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -3829,7 +3829,7 @@ static int perf_sched__schedstat_record(struct perf_s= ched *sched, session =3D perf_session__new(&data, &sched->tool); if (IS_ERR(session)) { pr_err("Perf session creation failed.\n"); - evlist__delete(evlist); + evlist__put(evlist); return PTR_ERR(session); } =20 @@ -3925,7 +3925,7 @@ static int perf_sched__schedstat_record(struct perf_s= ched *sched, else fprintf(stderr, "[ perf sched stats: Failed !! ]\n"); =20 - evlist__delete(evlist); + evlist__put(evlist); close(fd); return err; } @@ -4724,7 +4724,7 @@ static int perf_sched__schedstat_live(struct perf_sch= ed *sched, free_cpu_domain_info(cd_map, sv, nr); out: free_schedstat(&cpu_head); - evlist__delete(evlist); + evlist__put(evlist); return err; } =20 diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 853b141a0d50..0ead134940d5 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -2269,7 +2269,7 @@ static int script_find_metrics(const struct pmu_metri= c *pm, } pr_debug("Found metric '%s' whose evsels match those of in the perf data\= n", pm->metric_name); - evlist__delete(metric_evlist); + evlist__put(metric_evlist); out: return 0; } diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 99d7db372b48..bfa3512e1686 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -2113,7 +2113,7 @@ static int add_default_events(void) stat_config.user_requested_cpu_list, stat_config.system_wide, stat_config.hardware_aware_grouping) < 0) { - evlist__delete(metric_evlist); + evlist__put(metric_evlist); ret =3D -1; break; } @@ -2125,7 +2125,7 @@ static int add_default_events(void) metricgroup__copy_metric_events(evlist, /*cgrp=3D*/NULL, &evlist->metric_events, &metric_evlist->metric_events); - evlist__delete(metric_evlist); + evlist__put(metric_evlist); } list_sort(/*priv=3D*/NULL, &evlist->core.entries, default_evlist_evsel_c= mp); =20 @@ -2146,7 +2146,7 @@ static int add_default_events(void) metricgroup__copy_metric_events(evsel_list, /*cgrp=3D*/NULL, &evsel_list->metric_events, &evlist->metric_events); - evlist__delete(evlist); + evlist__put(evlist); return ret; } =20 @@ -2381,7 +2381,7 @@ static int __cmd_report(int argc, const char **argv) =20 perf_stat.session =3D session; stat_config.output =3D stderr; - evlist__delete(evsel_list); + evlist__put(evsel_list); evsel_list =3D session->evlist; =20 ret =3D perf_session__process_events(session); @@ -3060,7 +3060,7 @@ int cmd_stat(int argc, const char **argv) if (smi_cost && smi_reset) sysfs__write_int(FREEZE_ON_SMI_PATH, 0); =20 - evlist__delete(evsel_list); + evlist__put(evsel_list); =20 evlist__close_control(stat_config.ctl_fd, stat_config.ctl_fd_ack, &stat_c= onfig.ctl_fd_close); =20 diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index f6eb543de537..c509cfef8285 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1652,14 +1652,14 @@ int cmd_top(int argc, const char **argv) perf_env__init(&host_env); status =3D perf_config(perf_top_config, &top); if (status) - goto out_delete_evlist; + goto out_put_evlist; /* * Since the per arch annotation init routine may need the cpuid, read * it here, since we are not getting this from the perf.data header. */ status =3D perf_env__set_cmdline(&host_env, argc, argv); if (status) - goto out_delete_evlist; + goto out_put_evlist; =20 status =3D perf_env__read_cpuid(&host_env); if (status) { @@ -1680,30 +1680,30 @@ int cmd_top(int argc, const char **argv) annotate_opts.disassembler_style =3D strdup(disassembler_style); if (!annotate_opts.disassembler_style) { status =3D -ENOMEM; - goto out_delete_evlist; + goto out_put_evlist; } } if (objdump_path) { annotate_opts.objdump_path =3D strdup(objdump_path); if (!annotate_opts.objdump_path) { status =3D -ENOMEM; - goto out_delete_evlist; + goto out_put_evlist; } } if (addr2line_path) { symbol_conf.addr2line_path =3D strdup(addr2line_path); if (!symbol_conf.addr2line_path) { status =3D -ENOMEM; - goto out_delete_evlist; + goto out_put_evlist; } } =20 status =3D symbol__validate_sym_arguments(); if (status) - goto out_delete_evlist; + goto out_put_evlist; =20 if (annotate_check_args() < 0) - goto out_delete_evlist; + goto out_put_evlist; =20 status =3D target__validate(target); if (status) { @@ -1718,15 +1718,15 @@ int cmd_top(int argc, const char **argv) struct evlist *def_evlist =3D evlist__new_default(target, callchain_para= m.enabled); =20 if (!def_evlist) - goto out_delete_evlist; + goto out_put_evlist; =20 evlist__splice_list_tail(top.evlist, &def_evlist->core.entries); - evlist__delete(def_evlist); + evlist__put(def_evlist); } =20 status =3D evswitch__init(&top.evswitch, top.evlist, stderr); if (status) - goto out_delete_evlist; + goto out_put_evlist; =20 if (symbol_conf.report_hierarchy) { /* disable incompatible options */ @@ -1737,18 +1737,18 @@ int cmd_top(int argc, const char **argv) pr_err("Error: --hierarchy and --fields options cannot be used together= \n"); parse_options_usage(top_usage, options, "fields", 0); parse_options_usage(NULL, options, "hierarchy", 0); - goto out_delete_evlist; + goto out_put_evlist; } } =20 if (top.stitch_lbr && !(callchain_param.record_mode =3D=3D CALLCHAIN_LBR)= ) { pr_err("Error: --stitch-lbr must be used with --call-graph lbr\n"); - goto out_delete_evlist; + goto out_put_evlist; } =20 if (nr_cgroups > 0 && opts->record_cgroup) { pr_err("--cgroup and --all-cgroups cannot be used together\n"); - goto out_delete_evlist; + goto out_put_evlist; } =20 if (branch_call_mode) { @@ -1772,7 +1772,7 @@ int cmd_top(int argc, const char **argv) status =3D perf_env__read_core_pmu_caps(&host_env); if (status) { pr_err("PMU capability data is not available\n"); - goto out_delete_evlist; + goto out_put_evlist; } } =20 @@ -1795,7 +1795,7 @@ int cmd_top(int argc, const char **argv) if (IS_ERR(top.session)) { status =3D PTR_ERR(top.session); top.session =3D NULL; - goto out_delete_evlist; + goto out_put_evlist; } top.evlist->session =3D top.session; =20 @@ -1805,7 +1805,7 @@ int cmd_top(int argc, const char **argv) if (field_order) parse_options_usage(sort_order ? NULL : top_usage, options, "fields", 0); - goto out_delete_evlist; + goto out_put_evlist; } =20 if (top.uid_str) { @@ -1814,18 +1814,18 @@ int cmd_top(int argc, const char **argv) if (uid =3D=3D UINT_MAX) { ui__error("Invalid User: %s", top.uid_str); status =3D -EINVAL; - goto out_delete_evlist; + goto out_put_evlist; } status =3D parse_uid_filter(top.evlist, uid); if (status) - goto out_delete_evlist; + goto out_put_evlist; } =20 if (evlist__create_maps(top.evlist, target) < 0) { ui__error("Couldn't create thread/CPU maps: %s\n", errno =3D=3D ENOENT ? "No such process" : str_error_r(errno, errbuf, = sizeof(errbuf))); status =3D -errno; - goto out_delete_evlist; + goto out_put_evlist; } =20 if (top.delay_secs < 1) @@ -1833,7 +1833,7 @@ int cmd_top(int argc, const char **argv) =20 if (record_opts__config(opts)) { status =3D -EINVAL; - goto out_delete_evlist; + goto out_put_evlist; } =20 top.sym_evsel =3D evlist__first(top.evlist); @@ -1848,14 +1848,14 @@ int cmd_top(int argc, const char **argv) =20 status =3D symbol__annotation_init(); if (status < 0) - goto out_delete_evlist; + goto out_put_evlist; =20 annotation_config__init(); =20 symbol_conf.try_vmlinux_path =3D (symbol_conf.vmlinux_name =3D=3D NULL); status =3D symbol__init(NULL); if (status < 0) - goto out_delete_evlist; + goto out_put_evlist; =20 sort__setup_elide(stdout); =20 @@ -1875,13 +1875,13 @@ int cmd_top(int argc, const char **argv) if (top.sb_evlist =3D=3D NULL) { pr_err("Couldn't create side band evlist.\n."); status =3D -EINVAL; - goto out_delete_evlist; + goto out_put_evlist; } =20 if (evlist__add_bpf_sb_event(top.sb_evlist, &host_env)) { pr_err("Couldn't ask for PERF_RECORD_BPF_EVENT side band events.\n."); status =3D -EINVAL; - goto out_delete_evlist; + goto out_put_evlist; } } #endif @@ -1896,8 +1896,8 @@ int cmd_top(int argc, const char **argv) if (!opts->no_bpf_event) evlist__stop_sb_thread(top.sb_evlist); =20 -out_delete_evlist: - evlist__delete(top.evlist); +out_put_evlist: + evlist__put(top.evlist); perf_session__delete(top.session); annotation_options__exit(); perf_env__exit(&host_env); diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index e58c49d047a2..da703d762433 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -4388,7 +4388,7 @@ static int trace__run(struct trace *trace, int argc, = const char **argv) =20 if (trace->summary_bpf) { if (trace_prepare_bpf_summary(trace->summary_mode) < 0) - goto out_delete_evlist; + goto out_put_evlist; =20 if (trace->summary_only) goto create_maps; @@ -4456,19 +4456,19 @@ static int trace__run(struct trace *trace, int argc= , const char **argv) err =3D evlist__create_maps(evlist, &trace->opts.target); if (err < 0) { fprintf(trace->output, "Problems parsing the target to trace, check your= options!\n"); - goto out_delete_evlist; + goto out_put_evlist; } =20 err =3D trace__symbols_init(trace, argc, argv, evlist); if (err < 0) { fprintf(trace->output, "Problems initializing symbol libraries!\n"); - goto out_delete_evlist; + goto out_put_evlist; } =20 if (trace->summary_mode =3D=3D SUMMARY__BY_TOTAL && !trace->summary_bpf) { trace->syscall_stats =3D alloc_syscall_stats(); if (!trace->syscall_stats) - goto out_delete_evlist; + goto out_put_evlist; } =20 evlist__config(evlist, &trace->opts, &callchain_param); @@ -4477,7 +4477,7 @@ static int trace__run(struct trace *trace, int argc, = const char **argv) err =3D evlist__prepare_workload(evlist, &trace->opts.target, argv, fals= e, NULL); if (err < 0) { fprintf(trace->output, "Couldn't run the workload!\n"); - goto out_delete_evlist; + goto out_put_evlist; } workload_pid =3D evlist->workload.pid; } @@ -4525,7 +4525,7 @@ static int trace__run(struct trace *trace, int argc, = const char **argv) =20 err =3D trace__expand_filters(trace, &evsel); if (err) - goto out_delete_evlist; + goto out_put_evlist; err =3D evlist__apply_filters(evlist, &evsel, &trace->opts.target); if (err < 0) goto out_error_apply_filters; @@ -4642,12 +4642,12 @@ static int trace__run(struct trace *trace, int argc= , const char **argv) } } =20 -out_delete_evlist: +out_put_evlist: trace_cleanup_bpf_summary(); delete_syscall_stats(trace->syscall_stats); trace__symbols__exit(trace); evlist__free_syscall_tp_fields(evlist); - evlist__delete(evlist); + evlist__put(evlist); cgroup__put(trace->cgroup); trace->evlist =3D NULL; trace->live =3D false; @@ -4672,21 +4672,21 @@ static int trace__run(struct trace *trace, int argc= , const char **argv) =20 out_error: fprintf(trace->output, "%s\n", errbuf); - goto out_delete_evlist; + goto out_put_evlist; =20 out_error_apply_filters: fprintf(trace->output, "Failed to set filter \"%s\" on event %s: %m\n", evsel->filter, evsel__name(evsel)); - goto out_delete_evlist; + goto out_put_evlist; } out_error_mem: fprintf(trace->output, "Not enough memory to run!\n"); - goto out_delete_evlist; + goto out_put_evlist; =20 out_errno: fprintf(trace->output, "%m\n"); - goto out_delete_evlist; + goto out_put_evlist; } =20 static int trace__replay(struct trace *trace) @@ -5364,7 +5364,7 @@ static void trace__exit(struct trace *trace) zfree(&trace->syscalls.table); } zfree(&trace->perfconfig_events); - evlist__delete(trace->evlist); + evlist__put(trace->evlist); trace->evlist =3D NULL; ordered_events__free(&trace->oe.data); #ifdef HAVE_LIBBPF_SUPPORT diff --git a/tools/perf/tests/backward-ring-buffer.c b/tools/perf/tests/bac= kward-ring-buffer.c index c5e7999f2817..2b49b002d749 100644 --- a/tools/perf/tests/backward-ring-buffer.c +++ b/tools/perf/tests/backward-ring-buffer.c @@ -111,7 +111,7 @@ static int test__backward_ring_buffer(struct test_suite= *test __maybe_unused, in err =3D evlist__create_maps(evlist, &opts.target); if (err < 0) { pr_debug("Not enough memory to create thread/cpu maps\n"); - goto out_delete_evlist; + goto out_put_evlist; } =20 parse_events_error__init(&parse_error); @@ -124,7 +124,7 @@ static int test__backward_ring_buffer(struct test_suite= *test __maybe_unused, in if (err) { pr_debug("Failed to parse tracepoint event, try use root\n"); ret =3D TEST_SKIP; - goto out_delete_evlist; + goto out_put_evlist; } =20 evlist__config(evlist, &opts, NULL); @@ -133,19 +133,19 @@ static int test__backward_ring_buffer(struct test_sui= te *test __maybe_unused, in if (err < 0) { pr_debug("perf_evlist__open: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); - goto out_delete_evlist; + goto out_put_evlist; } =20 ret =3D TEST_FAIL; err =3D do_test(evlist, opts.mmap_pages, &sample_count, &comm_count); if (err !=3D TEST_OK) - goto out_delete_evlist; + goto out_put_evlist; =20 if ((sample_count !=3D NR_ITERS) || (comm_count !=3D NR_ITERS)) { pr_err("Unexpected counter: sample_count=3D%d, comm_count=3D%d\n", sample_count, comm_count); - goto out_delete_evlist; + goto out_put_evlist; } =20 evlist__close(evlist); @@ -154,16 +154,16 @@ static int test__backward_ring_buffer(struct test_sui= te *test __maybe_unused, in if (err < 0) { pr_debug("perf_evlist__open: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); - goto out_delete_evlist; + goto out_put_evlist; } =20 err =3D do_test(evlist, 1, &sample_count, &comm_count); if (err !=3D TEST_OK) - goto out_delete_evlist; + goto out_put_evlist; =20 ret =3D TEST_OK; -out_delete_evlist: - evlist__delete(evlist); +out_put_evlist: + evlist__put(evlist); return ret; } =20 diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-readin= g.c index 47043a3a2fb4..fc65a17f67f7 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -807,7 +807,7 @@ static int do_test_code_reading(bool try_kcore) } =20 perf_evlist__set_maps(&evlist->core, NULL, NULL); - evlist__delete(evlist); + evlist__put(evlist); evlist =3D NULL; continue; } @@ -844,7 +844,7 @@ static int do_test_code_reading(bool try_kcore) out_put: thread__put(thread); out_err: - evlist__delete(evlist); + evlist__put(evlist); perf_cpu_map__put(cpus); perf_thread_map__put(threads); machine__delete(machine); diff --git a/tools/perf/tests/event-times.c b/tools/perf/tests/event-times.c index ae3b98bb42cf..94ab54ecd3f9 100644 --- a/tools/perf/tests/event-times.c +++ b/tools/perf/tests/event-times.c @@ -186,7 +186,7 @@ static int test_times(int (attach)(struct evlist *), err =3D attach(evlist); if (err =3D=3D TEST_SKIP) { pr_debug(" SKIP : not enough rights\n"); - evlist__delete(evlist); + evlist__put(evlist); return err; } =20 @@ -205,7 +205,7 @@ static int test_times(int (attach)(struct evlist *), count.ena, count.run); =20 out_err: - evlist__delete(evlist); + evlist__put(evlist); return !err ? TEST_OK : TEST_FAIL; } =20 diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_updat= e.c index facc65e29f20..73141b122d2f 100644 --- a/tools/perf/tests/event_update.c +++ b/tools/perf/tests/event_update.c @@ -117,7 +117,7 @@ static int test__event_update(struct test_suite *test _= _maybe_unused, int subtes TEST_ASSERT_VAL("failed to synthesize attr update cpus", !perf_event__synthesize_event_update_cpus(&tmp.tool, evsel, process_eve= nt_cpus)); =20 - evlist__delete(evlist); + evlist__put(evlist); return 0; } =20 diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evs= el-roundtrip-name.c index 1922cac13a24..6a220634c52f 100644 --- a/tools/perf/tests/evsel-roundtrip-name.c +++ b/tools/perf/tests/evsel-roundtrip-name.c @@ -33,7 +33,7 @@ static int perf_evsel__roundtrip_cache_name_test(void) if (err) { pr_debug("Failure to parse cache event '%s' possibly as PMUs don't su= pport it", name); - evlist__delete(evlist); + evlist__put(evlist); continue; } evlist__for_each_entry(evlist, evsel) { @@ -42,7 +42,7 @@ static int perf_evsel__roundtrip_cache_name_test(void) ret =3D TEST_FAIL; } } - evlist__delete(evlist); + evlist__put(evlist); } } } @@ -66,7 +66,7 @@ static int perf_evsel__name_array_test(const char *const = names[], int nr_names) if (err) { pr_debug("failed to parse event '%s', err %d\n", names[i], err); - evlist__delete(evlist); + evlist__put(evlist); ret =3D TEST_FAIL; continue; } @@ -76,7 +76,7 @@ static int perf_evsel__name_array_test(const char *const = names[], int nr_names) ret =3D TEST_FAIL; } } - evlist__delete(evlist); + evlist__put(evlist); } return ret; } diff --git a/tools/perf/tests/expand-cgroup.c b/tools/perf/tests/expand-cgr= oup.c index dd547f2f77cc..a7a445f12693 100644 --- a/tools/perf/tests/expand-cgroup.c +++ b/tools/perf/tests/expand-cgroup.c @@ -106,7 +106,7 @@ static int expand_default_events(void) TEST_ASSERT_VAL("failed to get evlist", evlist); =20 ret =3D test_expand_events(evlist); - evlist__delete(evlist); + evlist__put(evlist); return ret; } =20 @@ -133,7 +133,7 @@ static int expand_group_events(void) ret =3D test_expand_events(evlist); out: parse_events_error__exit(&err); - evlist__delete(evlist); + evlist__put(evlist); return ret; } =20 @@ -164,7 +164,7 @@ static int expand_libpfm_events(void) =20 ret =3D test_expand_events(evlist); out: - evlist__delete(evlist); + evlist__put(evlist); return ret; } =20 @@ -188,7 +188,7 @@ static int expand_metric_events(void) ret =3D test_expand_events(evlist); =20 out: - evlist__delete(evlist); + evlist__put(evlist); return ret; } =20 diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cum= ulate.c index 606aa926a8fc..eca4ecb63ca8 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c @@ -744,7 +744,7 @@ static int test__hists_cumulate(struct test_suite *test= __maybe_unused, int subt =20 out: /* tear down everything */ - evlist__delete(evlist); + evlist__put(evlist); machines__exit(&machines); put_fake_samples(); =20 diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filte= r.c index cc6b26e373d1..0d09dc306019 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c @@ -332,7 +332,7 @@ static int test__hists_filter(struct test_suite *test _= _maybe_unused, int subtes =20 out: /* tear down everything */ - evlist__delete(evlist); + evlist__put(evlist); reset_output_field(); machines__exit(&machines); put_fake_samples(); diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 996f5f0b3bd1..9646c3b7b4de 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -352,7 +352,7 @@ static int test__hists_link(struct test_suite *test __m= aybe_unused, int subtest =20 out: /* tear down everything */ - evlist__delete(evlist); + evlist__put(evlist); reset_output_field(); machines__exit(&machines); put_fake_samples(); diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_outpu= t.c index 7818950d786e..3f3bc978553e 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c @@ -631,7 +631,7 @@ static int test__hists_output(struct test_suite *test _= _maybe_unused, int subtes =20 out: /* tear down everything */ - evlist__delete(evlist); + evlist__put(evlist); machines__exit(&machines); put_fake_samples(); =20 diff --git a/tools/perf/tests/hwmon_pmu.c b/tools/perf/tests/hwmon_pmu.c index ada6e445c4c4..1b60c3a900f1 100644 --- a/tools/perf/tests/hwmon_pmu.c +++ b/tools/perf/tests/hwmon_pmu.c @@ -214,7 +214,7 @@ static int do_test(size_t i, bool with_pmu, bool with_a= lias) =20 out: parse_events_error__exit(&err); - evlist__delete(evlist); + evlist__put(evlist); return ret; } =20 diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-track= ing.c index 729cc9cc1cb7..51cfd6522867 100644 --- a/tools/perf/tests/keep-tracking.c +++ b/tools/perf/tests/keep-tracking.c @@ -153,7 +153,7 @@ static int test__keep_tracking(struct test_suite *test = __maybe_unused, int subte out_err: if (evlist) { evlist__disable(evlist); - evlist__delete(evlist); + evlist__put(evlist); } perf_cpu_map__put(cpus); perf_thread_map__put(threads); diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index 8d04f6edb004..e6501791c505 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c @@ -94,7 +94,7 @@ static int test__basic_mmap(struct test_suite *test __may= be_unused, int subtest /* Permissions failure, flag the failure as a skip. */ err =3D TEST_SKIP; } - goto out_delete_evlist; + goto out_put_evlist; } =20 evsels[i]->core.attr.wakeup_events =3D 1; @@ -106,7 +106,7 @@ static int test__basic_mmap(struct test_suite *test __m= aybe_unused, int subtest pr_debug("failed to open counter: %s, " "tweak /proc/sys/kernel/perf_event_paranoid?\n", str_error_r(errno, sbuf, sizeof(sbuf))); - goto out_delete_evlist; + goto out_put_evlist; } =20 nr_events[i] =3D 0; @@ -116,7 +116,7 @@ static int test__basic_mmap(struct test_suite *test __m= aybe_unused, int subtest if (evlist__mmap(evlist, 128) < 0) { pr_debug("failed to mmap events: %d (%s)\n", errno, str_error_r(errno, sbuf, sizeof(sbuf))); - goto out_delete_evlist; + goto out_put_evlist; } =20 for (i =3D 0; i < nsyscalls; ++i) @@ -134,7 +134,7 @@ static int test__basic_mmap(struct test_suite *test __m= aybe_unused, int subtest if (event->header.type !=3D PERF_RECORD_SAMPLE) { pr_debug("unexpected %s event\n", perf_event__name(event->header.type)); - goto out_delete_evlist; + goto out_put_evlist; } =20 perf_sample__init(&sample, /*all=3D*/false); @@ -142,7 +142,7 @@ static int test__basic_mmap(struct test_suite *test __m= aybe_unused, int subtest if (err) { pr_err("Can't parse sample, err =3D %d\n", err); perf_sample__exit(&sample); - goto out_delete_evlist; + goto out_put_evlist; } =20 err =3D -1; @@ -151,7 +151,7 @@ static int test__basic_mmap(struct test_suite *test __m= aybe_unused, int subtest if (evsel =3D=3D NULL) { pr_debug("event with id %" PRIu64 " doesn't map to an evsel\n", sample.id); - goto out_delete_evlist; + goto out_put_evlist; } nr_events[evsel->core.idx]++; perf_mmap__consume(&md->core); @@ -166,12 +166,12 @@ static int test__basic_mmap(struct test_suite *test _= _maybe_unused, int subtest expected_nr_events[evsel->core.idx], evsel__name(evsel), nr_events[evsel->core.idx]); err =3D -1; - goto out_delete_evlist; + goto out_put_evlist; } } =20 -out_delete_evlist: - evlist__delete(evlist); +out_put_evlist: + evlist__put(evlist); out_free_cpus: perf_cpu_map__put(cpus); out_free_threads: diff --git a/tools/perf/tests/openat-syscall-tp-fields.c b/tools/perf/tests= /openat-syscall-tp-fields.c index 2a139d2781a8..3ff595c7a86a 100644 --- a/tools/perf/tests/openat-syscall-tp-fields.c +++ b/tools/perf/tests/openat-syscall-tp-fields.c @@ -51,7 +51,7 @@ static int test__syscall_openat_tp_fields(struct test_sui= te *test __maybe_unused if (IS_ERR(evsel)) { pr_debug("%s: evsel__newtp\n", __func__); ret =3D PTR_ERR(evsel) =3D=3D -EACCES ? TEST_SKIP : TEST_FAIL; - goto out_delete_evlist; + goto out_put_evlist; } =20 evlist__add(evlist, evsel); @@ -59,7 +59,7 @@ static int test__syscall_openat_tp_fields(struct test_sui= te *test __maybe_unused err =3D evlist__create_maps(evlist, &opts.target); if (err < 0) { pr_debug("%s: evlist__create_maps\n", __func__); - goto out_delete_evlist; + goto out_put_evlist; } =20 evsel__config(evsel, &opts, NULL); @@ -70,14 +70,14 @@ static int test__syscall_openat_tp_fields(struct test_s= uite *test __maybe_unused if (err < 0) { pr_debug("perf_evlist__open: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); - goto out_delete_evlist; + goto out_put_evlist; } =20 err =3D evlist__mmap(evlist, UINT_MAX); if (err < 0) { pr_debug("evlist__mmap: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); - goto out_delete_evlist; + goto out_put_evlist; } =20 evlist__enable(evlist); @@ -115,7 +115,7 @@ static int test__syscall_openat_tp_fields(struct test_s= uite *test __maybe_unused if (err) { pr_debug("Can't parse sample, err =3D %d\n", err); perf_sample__exit(&sample); - goto out_delete_evlist; + goto out_put_evlist; } =20 tp_flags =3D evsel__intval(evsel, &sample, "flags"); @@ -123,7 +123,7 @@ static int test__syscall_openat_tp_fields(struct test_s= uite *test __maybe_unused if (flags !=3D tp_flags) { pr_debug("%s: Expected flags=3D%#x, got %#x\n", __func__, flags, tp_flags); - goto out_delete_evlist; + goto out_put_evlist; } =20 goto out_ok; @@ -136,13 +136,13 @@ static int test__syscall_openat_tp_fields(struct test= _suite *test __maybe_unused =20 if (++nr_polls > 5) { pr_debug("%s: no events!\n", __func__); - goto out_delete_evlist; + goto out_put_evlist; } } out_ok: ret =3D TEST_OK; -out_delete_evlist: - evlist__delete(evlist); +out_put_evlist: + evlist__put(evlist); out: return ret; } diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-event= s.c index 05c3e899b425..19dc7b7475d2 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -2568,7 +2568,7 @@ static int test_event(const struct evlist_test *e) ret =3D e->check(evlist); } parse_events_error__exit(&err); - evlist__delete(evlist); + evlist__put(evlist); =20 return ret; } @@ -2594,7 +2594,7 @@ static int test_event_fake_pmu(const char *str) } =20 parse_events_error__exit(&err); - evlist__delete(evlist); + evlist__put(evlist); =20 return ret; } diff --git a/tools/perf/tests/parse-metric.c b/tools/perf/tests/parse-metri= c.c index 7c7f489a5eb0..3f0ec839c056 100644 --- a/tools/perf/tests/parse-metric.c +++ b/tools/perf/tests/parse-metric.c @@ -84,7 +84,7 @@ static int __compute_metric(const char *name, struct valu= e *vals, =20 cpus =3D perf_cpu_map__new("0"); if (!cpus) { - evlist__delete(evlist); + evlist__put(evlist); return -ENOMEM; } =20 @@ -113,7 +113,7 @@ static int __compute_metric(const char *name, struct va= lue *vals, /* ... cleanup. */ evlist__free_stats(evlist); perf_cpu_map__put(cpus); - evlist__delete(evlist); + evlist__put(evlist); return err; } =20 diff --git a/tools/perf/tests/parse-no-sample-id-all.c b/tools/perf/tests/p= arse-no-sample-id-all.c index 50e68b7d43aa..d5a8d065809e 100644 --- a/tools/perf/tests/parse-no-sample-id-all.c +++ b/tools/perf/tests/parse-no-sample-id-all.c @@ -49,7 +49,7 @@ static int process_events(union perf_event **events, size= _t count) for (i =3D 0; i < count && !err; i++) err =3D process_event(&evlist, events[i]); =20 - evlist__delete(evlist); + evlist__put(evlist); =20 return err; } diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index ad44cc68820b..f95752b2ed1c 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -105,7 +105,7 @@ static int test__PERF_RECORD(struct test_suite *test __= maybe_unused, int subtest err =3D evlist__create_maps(evlist, &opts.target); if (err < 0) { pr_debug("Not enough memory to create thread/cpu maps\n"); - goto out_delete_evlist; + goto out_put_evlist; } =20 /* @@ -117,7 +117,7 @@ static int test__PERF_RECORD(struct test_suite *test __= maybe_unused, int subtest err =3D evlist__prepare_workload(evlist, &opts.target, argv, false, NULL); if (err < 0) { pr_debug("Couldn't run the workload!\n"); - goto out_delete_evlist; + goto out_put_evlist; } =20 /* @@ -134,7 +134,7 @@ static int test__PERF_RECORD(struct test_suite *test __= maybe_unused, int subtest pr_debug("sched__get_first_possible_cpu: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); evlist__cancel_workload(evlist); - goto out_delete_evlist; + goto out_put_evlist; } =20 cpu =3D err; @@ -146,7 +146,7 @@ static int test__PERF_RECORD(struct test_suite *test __= maybe_unused, int subtest pr_debug("sched_setaffinity: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); evlist__cancel_workload(evlist); - goto out_delete_evlist; + goto out_put_evlist; } =20 /* @@ -158,7 +158,7 @@ static int test__PERF_RECORD(struct test_suite *test __= maybe_unused, int subtest pr_debug("perf_evlist__open: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); evlist__cancel_workload(evlist); - goto out_delete_evlist; + goto out_put_evlist; } =20 /* @@ -171,7 +171,7 @@ static int test__PERF_RECORD(struct test_suite *test __= maybe_unused, int subtest pr_debug("evlist__mmap: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); evlist__cancel_workload(evlist); - goto out_delete_evlist; + goto out_put_evlist; } =20 /* @@ -209,7 +209,7 @@ static int test__PERF_RECORD(struct test_suite *test __= maybe_unused, int subtest if (verbose > 0) perf_event__fprintf(event, NULL, stderr); pr_debug("Couldn't parse sample\n"); - goto out_delete_evlist; + goto out_put_evlist; } =20 if (verbose > 0) { @@ -350,9 +350,9 @@ static int test__PERF_RECORD(struct test_suite *test __= maybe_unused, int subtest pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]"); ++errs; } -out_delete_evlist: +out_put_evlist: CPU_FREE(cpu_mask); - evlist__delete(evlist); + evlist__put(evlist); out: perf_sample__exit(&sample); if (err =3D=3D -EACCES) diff --git a/tools/perf/tests/perf-time-to-tsc.c b/tools/perf/tests/perf-ti= me-to-tsc.c index cca41bd37ae3..d3538fa20af3 100644 --- a/tools/perf/tests/perf-time-to-tsc.c +++ b/tools/perf/tests/perf-time-to-tsc.c @@ -201,7 +201,7 @@ static int test__perf_time_to_tsc(struct test_suite *te= st __maybe_unused, int su err =3D TEST_OK; =20 out_err: - evlist__delete(evlist); + evlist__put(evlist); perf_cpu_map__put(cpus); perf_thread_map__put(threads); return err; diff --git a/tools/perf/tests/pfm.c b/tools/perf/tests/pfm.c index fca4a86452df..8d19b1bfecbc 100644 --- a/tools/perf/tests/pfm.c +++ b/tools/perf/tests/pfm.c @@ -80,7 +80,7 @@ static int test__pfm_events(struct test_suite *test __may= be_unused, evlist__nr_groups(evlist), 0); =20 - evlist__delete(evlist); + evlist__put(evlist); } return 0; } @@ -165,7 +165,7 @@ static int test__pfm_group(struct test_suite *test __ma= ybe_unused, evlist__nr_groups(evlist), table[i].nr_groups); =20 - evlist__delete(evlist); + evlist__put(evlist); } return 0; } diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index a99716862168..236bbbad5773 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -797,7 +797,7 @@ static int check_parse_id(const char *id, struct parse_= events_error *error) /*warn_if_reordered=3D*/true, /*fake_tp=3D*/false); free(dup); =20 - evlist__delete(evlist); + evlist__put(evlist); return ret; } =20 @@ -844,7 +844,7 @@ static int test__parsing_callback(const struct pmu_metr= ic *pm, =20 cpus =3D perf_cpu_map__new("0"); if (!cpus) { - evlist__delete(evlist); + evlist__put(evlist); return -ENOMEM; } =20 @@ -899,7 +899,7 @@ static int test__parsing_callback(const struct pmu_metr= ic *pm, /* ... cleanup. */ evlist__free_stats(evlist); perf_cpu_map__put(cpus); - evlist__delete(evlist); + evlist__put(evlist); return err; } =20 diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c index 0ebf2d7b2cb4..3d931c1f99dd 100644 --- a/tools/perf/tests/pmu.c +++ b/tools/perf/tests/pmu.c @@ -287,7 +287,7 @@ static int test__pmu_usr_chgs(struct test_suite *test _= _maybe_unused, int subtes ret =3D TEST_OK; err_out: parse_events_terms__exit(&terms); - evlist__delete(evlist); + evlist__put(evlist); test_pmu_put(dir, pmu); return ret; } @@ -339,7 +339,7 @@ static int test__pmu_events(struct test_suite *test __m= aybe_unused, int subtest ret =3D TEST_OK; err_out: parse_events_error__exit(&err); - evlist__delete(evlist); + evlist__put(evlist); test_pmu_put(dir, pmu); return ret; } diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c index b6e46975379c..bb6b62cf51d1 100644 --- a/tools/perf/tests/sw-clock.c +++ b/tools/perf/tests/sw-clock.c @@ -59,7 +59,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_i= d) evsel =3D evsel__new(&attr); if (evsel =3D=3D NULL) { pr_debug("evsel__new\n"); - goto out_delete_evlist; + goto out_put_evlist; } evlist__add(evlist, evsel); =20 @@ -68,7 +68,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_i= d) if (!cpus || !threads) { err =3D -ENOMEM; pr_debug("Not enough memory to create thread/cpu maps\n"); - goto out_delete_evlist; + goto out_put_evlist; } =20 perf_evlist__set_maps(&evlist->core, cpus, threads); @@ -80,14 +80,14 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock= _id) pr_debug("Couldn't open evlist: %s\nHint: check %s, using %" PRIu64 " in= this test.\n", str_error_r(errno, sbuf, sizeof(sbuf)), knob, (u64)attr.sample_freq); - goto out_delete_evlist; + goto out_put_evlist; } =20 err =3D evlist__mmap(evlist, 128); if (err < 0) { pr_debug("failed to mmap event: %d (%s)\n", errno, str_error_r(errno, sbuf, sizeof(sbuf))); - goto out_delete_evlist; + goto out_put_evlist; } =20 evlist__enable(evlist); @@ -113,7 +113,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock= _id) if (err < 0) { pr_debug("Error during parse sample\n"); perf_sample__exit(&sample); - goto out_delete_evlist; + goto out_put_evlist; } =20 total_periods +=3D sample.period; @@ -131,10 +131,10 @@ static int __test__sw_clock_freq(enum perf_sw_ids clo= ck_id) err =3D -1; } =20 -out_delete_evlist: +out_put_evlist: perf_cpu_map__put(cpus); perf_thread_map__put(threads); - evlist__delete(evlist); + evlist__put(evlist); return err; } =20 diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-t= racking.c index 72a8289e846d..306151c83af8 100644 --- a/tools/perf/tests/switch-tracking.c +++ b/tools/perf/tests/switch-tracking.c @@ -579,7 +579,7 @@ static int test__switch_tracking(struct test_suite *tes= t __maybe_unused, int sub out: if (evlist) { evlist__disable(evlist); - evlist__delete(evlist); + evlist__put(evlist); } perf_cpu_map__put(cpus); perf_thread_map__put(threads); diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index 4053ff2813bb..a46650b10689 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c @@ -74,7 +74,7 @@ static int test__task_exit(struct test_suite *test __mayb= e_unused, int subtest _ if (!cpus || !threads) { err =3D -ENOMEM; pr_debug("Not enough memory to create thread/cpu maps\n"); - goto out_delete_evlist; + goto out_put_evlist; } =20 perf_evlist__set_maps(&evlist->core, cpus, threads); @@ -82,7 +82,7 @@ static int test__task_exit(struct test_suite *test __mayb= e_unused, int subtest _ err =3D evlist__prepare_workload(evlist, &target, argv, false, workload_e= xec_failed_signal); if (err < 0) { pr_debug("Couldn't run the workload!\n"); - goto out_delete_evlist; + goto out_put_evlist; } =20 evsel =3D evlist__first(evlist); @@ -101,14 +101,14 @@ static int test__task_exit(struct test_suite *test __= maybe_unused, int subtest _ if (err < 0) { pr_debug("Couldn't open the evlist: %s\n", str_error_r(-err, sbuf, sizeof(sbuf))); - goto out_delete_evlist; + goto out_put_evlist; } =20 if (evlist__mmap(evlist, 128) < 0) { pr_debug("failed to mmap events: %d (%s)\n", errno, str_error_r(errno, sbuf, sizeof(sbuf))); err =3D -1; - goto out_delete_evlist; + goto out_put_evlist; } =20 evlist__start_workload(evlist); @@ -133,7 +133,7 @@ static int test__task_exit(struct test_suite *test __ma= ybe_unused, int subtest _ if (retry_count++ > 1000) { pr_debug("Failed after retrying 1000 times\n"); err =3D -1; - goto out_delete_evlist; + goto out_put_evlist; } =20 goto retry; @@ -144,10 +144,10 @@ static int test__task_exit(struct test_suite *test __= maybe_unused, int subtest _ err =3D -1; } =20 -out_delete_evlist: +out_put_evlist: perf_cpu_map__put(cpus); perf_thread_map__put(threads); - evlist__delete(evlist); + evlist__put(evlist); return err; } =20 diff --git a/tools/perf/tests/tool_pmu.c b/tools/perf/tests/tool_pmu.c index 1e900ef92e37..e78ff9dcea97 100644 --- a/tools/perf/tests/tool_pmu.c +++ b/tools/perf/tests/tool_pmu.c @@ -67,7 +67,7 @@ static int do_test(enum tool_pmu_event ev, bool with_pmu) =20 out: parse_events_error__exit(&err); - evlist__delete(evlist); + evlist__put(evlist); return ret; } =20 diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c index f54502ebef4b..4ecf5d750313 100644 --- a/tools/perf/tests/topology.c +++ b/tools/perf/tests/topology.c @@ -57,7 +57,7 @@ static int session_write_header(char *path) !perf_session__write_header(session, session->evlist, perf_data__fd(&data), true)); =20 - evlist__delete(session->evlist); + evlist__put(session->evlist); perf_session__delete(session); =20 return 0; diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index 1b5664d1481f..652a45aac828 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -520,8 +520,8 @@ int evlist__expand_cgroup(struct evlist *evlist, const = char *str, bool open_cgro cgrp_event_expanded =3D true; =20 out_err: - evlist__delete(orig_list); - evlist__delete(tmp_list); + evlist__put(orig_list); + evlist__put(tmp_list); metricgroup__rblist_exit(&orig_metric_events); release_cgroup_list(); =20 diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-conve= rt-bt.c index 3b8f2df823a9..a85ae53db7c5 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -1363,7 +1363,7 @@ static void cleanup_events(struct perf_session *sessi= on) zfree(&evsel->priv); } =20 - evlist__delete(evlist); + evlist__put(evlist); session->evlist =3D NULL; } =20 diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 35d65fe50e06..b5a7895debf5 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -75,7 +75,7 @@ int sigqueue(pid_t pid, int sig, const union sigval value= ); #define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y)) #define SID(e, x, y) xyarray__entry(e->core.sample_id, x, y) =20 -void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus, +static void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus, struct perf_thread_map *threads) { perf_evlist__init(&evlist->core); @@ -88,6 +88,7 @@ void evlist__init(struct evlist *evlist, struct perf_cpu_= map *cpus, evlist->nr_br_cntr =3D -1; metricgroup__rblist_init(&evlist->metric_events); INIT_LIST_HEAD(&evlist->deferred_samples); + refcount_set(&evlist->refcnt, 1); } =20 struct evlist *evlist__new(void) @@ -139,7 +140,7 @@ struct evlist *evlist__new_default(const struct target = *target, bool sample_call =20 return evlist; out_err: - evlist__delete(evlist); + evlist__put(evlist); return NULL; } =20 @@ -148,13 +149,19 @@ struct evlist *evlist__new_dummy(void) struct evlist *evlist =3D evlist__new(); =20 if (evlist && evlist__add_dummy(evlist)) { - evlist__delete(evlist); + evlist__put(evlist); evlist =3D NULL; } =20 return evlist; } =20 +struct evlist *evlist__get(struct evlist *evlist) +{ + refcount_inc(&evlist->refcnt); + return evlist; +} + /** * evlist__set_id_pos - set the positions of event ids. * @evlist: selected event list @@ -193,7 +200,7 @@ static void evlist__purge(struct evlist *evlist) evlist->core.nr_entries =3D 0; } =20 -void evlist__exit(struct evlist *evlist) +static void evlist__exit(struct evlist *evlist) { metricgroup__rblist_exit(&evlist->metric_events); event_enable_timer__exit(&evlist->eet); @@ -202,11 +209,14 @@ void evlist__exit(struct evlist *evlist) perf_evlist__exit(&evlist->core); } =20 -void evlist__delete(struct evlist *evlist) +void evlist__put(struct evlist *evlist) { if (evlist =3D=3D NULL) return; =20 + if (!refcount_dec_and_test(&evlist->refcnt)) + return; + evlist__free_stats(evlist); evlist__munmap(evlist); evlist__close(evlist); diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index e54761c670b6..a9820a6aad5b 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -61,6 +61,7 @@ struct event_enable_timer; =20 struct evlist { struct perf_evlist core; + refcount_t refcnt; bool enabled; bool no_affinity; int id_pos; @@ -109,10 +110,8 @@ struct evsel_str_handler { struct evlist *evlist__new(void); struct evlist *evlist__new_default(const struct target *target, bool sampl= e_callchains); struct evlist *evlist__new_dummy(void); -void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus, - struct perf_thread_map *threads); -void evlist__exit(struct evlist *evlist); -void evlist__delete(struct evlist *evlist); +struct evlist *evlist__get(struct evlist *evlist); +void evlist__put(struct evlist *evlist); =20 void evlist__add(struct evlist *evlist, struct evsel *entry); void evlist__remove(struct evlist *evlist, struct evsel *evsel); diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c index 644769e92708..cf54bbbc8ddc 100644 --- a/tools/perf/util/expr.c +++ b/tools/perf/util/expr.c @@ -450,7 +450,7 @@ double expr__has_event(const struct expr_parse_ctx *ctx= , bool compute_ids, const ret =3D parse_event(tmp, id) ? 0 : 1; } out: - evlist__delete(tmp); + evlist__put(tmp); return ret; } =20 diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f30e48eb3fc3..f9887d2fc8ed 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -4909,12 +4909,12 @@ int perf_session__read_header(struct perf_session *= session) evsel =3D evsel__new(&f_attr.attr); =20 if (evsel =3D=3D NULL) - goto out_delete_evlist; + goto out_put_evlist; =20 evsel->needs_swap =3D header->needs_swap; /* * Do it before so that if perf_evsel__alloc_id fails, this - * entry gets purged too at evlist__delete(). + * entry gets purged too at evlist__put(). */ evlist__add(session->evlist, evsel); =20 @@ -4925,7 +4925,7 @@ int perf_session__read_header(struct perf_session *se= ssion) * hattr->ids threads. */ if (perf_evsel__alloc_id(&evsel->core, 1, nr_ids)) - goto out_delete_evlist; + goto out_put_evlist; =20 lseek(fd, f_attr.ids.offset, SEEK_SET); =20 @@ -4944,7 +4944,7 @@ int perf_session__read_header(struct perf_session *se= ssion) perf_file_section__process); =20 if (evlist__prepare_tracepoint_events(session->evlist, session->tevent.pe= vent)) - goto out_delete_evlist; + goto out_put_evlist; #else perf_header__process_sections(header, fd, NULL, perf_file_section__proces= s); #endif @@ -4953,8 +4953,8 @@ int perf_session__read_header(struct perf_session *se= ssion) out_errno: return -errno; =20 -out_delete_evlist: - evlist__delete(session->evlist); +out_put_evlist: + evlist__put(session->evlist); session->evlist =3D NULL; return -ENOMEM; } diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index 4db9578efd81..191ec2d8a250 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -214,7 +214,7 @@ static void metric__free(struct metric *m) zfree(&m->metric_refs); expr__ctx_free(m->pctx); zfree(&m->modifier); - evlist__delete(m->evlist); + evlist__put(m->evlist); free(m); } =20 @@ -1335,7 +1335,7 @@ static int parse_ids(bool metric_no_merge, bool fake_= pmu, parsed_evlist =3D NULL; err_out: parse_events_error__exit(&parse_error); - evlist__delete(parsed_evlist); + evlist__put(parsed_evlist); strbuf_release(&events); return ret; } @@ -1546,7 +1546,7 @@ static int parse_groups(struct evlist *perf_evlist, =20 if (combined_evlist) { evlist__splice_list_tail(perf_evlist, &combined_evlist->core.entries); - evlist__delete(combined_evlist); + evlist__put(combined_evlist); } =20 list_for_each_entry(m, &metric_list, nd) { diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 1497e1f2a08c..f0809be63ad8 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -2316,7 +2316,7 @@ int __parse_events(struct evlist *evlist, const char = *str, const char *pmu_filte =20 /* * There are 2 users - builtin-record and builtin-test objects. - * Both call evlist__delete in case of error, so we dont + * Both call evlist__put in case of error, so we dont * need to bother. */ return ret; @@ -2519,7 +2519,7 @@ int parse_events_option_new_evlist(const struct optio= n *opt, const char *str, in } ret =3D parse_events_option(opt, str, unset); if (ret) { - evlist__delete(*args->evlistp); + evlist__put(*args->evlistp); *args->evlistp =3D NULL; } =20 diff --git a/tools/perf/util/perf_api_probe.c b/tools/perf/util/perf_api_pr= obe.c index e1904a330b28..f61c4ec52827 100644 --- a/tools/perf/util/perf_api_probe.c +++ b/tools/perf/util/perf_api_probe.c @@ -57,7 +57,7 @@ static int perf_do_probe_api(setup_probe_fn_t fn, struct = perf_cpu cpu, const cha err =3D 0; =20 out_delete: - evlist__delete(evlist); + evlist__put(evlist); return err; } =20 diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 1e6c99efff90..aeecdb497fac 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -1272,7 +1272,7 @@ static int pyrf_evsel__setup_types(void) struct pyrf_evlist { PyObject_HEAD =20 - struct evlist evlist; + struct evlist *evlist; }; =20 static int pyrf_evlist__init(struct pyrf_evlist *pevlist, @@ -1285,15 +1285,22 @@ static int pyrf_evlist__init(struct pyrf_evlist *pe= vlist, if (!PyArg_ParseTuple(args, "OO", &pcpus, &pthreads)) return -1; =20 + evlist__put(pevlist->evlist); + pevlist->evlist =3D evlist__new(); + if (!pevlist->evlist) { + PyErr_NoMemory(); + return -1; + } threads =3D ((struct pyrf_thread_map *)pthreads)->threads; cpus =3D ((struct pyrf_cpu_map *)pcpus)->cpus; - evlist__init(&pevlist->evlist, cpus, threads); + perf_evlist__set_maps(&pevlist->evlist->core, cpus, threads); + return 0; } =20 static void pyrf_evlist__delete(struct pyrf_evlist *pevlist) { - evlist__exit(&pevlist->evlist); + evlist__put(pevlist->evlist); Py_TYPE(pevlist)->tp_free((PyObject*)pevlist); } =20 @@ -1302,7 +1309,7 @@ static PyObject *pyrf_evlist__all_cpus(struct pyrf_ev= list *pevlist) struct pyrf_cpu_map *pcpu_map =3D PyObject_New(struct pyrf_cpu_map, &pyrf= _cpu_map__type); =20 if (pcpu_map) - pcpu_map->cpus =3D perf_cpu_map__get(pevlist->evlist.core.all_cpus); + pcpu_map->cpus =3D perf_cpu_map__get(pevlist->evlist->core.all_cpus); =20 return (PyObject *)pcpu_map; } @@ -1315,7 +1322,7 @@ static PyObject *pyrf_evlist__metrics(struct pyrf_evl= ist *pevlist) if (!list) return NULL; =20 - for (node =3D rb_first_cached(&pevlist->evlist.metric_events.entries); no= de; + for (node =3D rb_first_cached(&pevlist->evlist->metric_events.entries); n= ode; node =3D rb_next(node)) { struct metric_event *me =3D container_of(node, struct metric_event, nd); struct list_head *pos; @@ -1421,7 +1428,7 @@ static PyObject *pyrf_evlist__compute_metric(struct p= yrf_evlist *pevlist, if (!PyArg_ParseTuple(args, "sii", &metric, &cpu, &thread)) return NULL; =20 - for (node =3D rb_first_cached(&pevlist->evlist.metric_events.entries); + for (node =3D rb_first_cached(&pevlist->evlist->metric_events.entries); mexp =3D=3D NULL && node; node =3D rb_next(node)) { struct metric_event *me =3D container_of(node, struct metric_event, nd); @@ -1437,7 +1444,7 @@ static PyObject *pyrf_evlist__compute_metric(struct p= yrf_evlist *pevlist, if (e->metric_events[0] =3D=3D NULL) continue; =20 - evlist__for_each_entry(&pevlist->evlist, pos2) { + evlist__for_each_entry(pevlist->evlist, pos2) { if (pos2->metric_leader !=3D e->metric_events[0]) continue; cpu_idx =3D perf_cpu_map__idx(pos2->core.cpus, @@ -1482,7 +1489,7 @@ static PyObject *pyrf_evlist__compute_metric(struct p= yrf_evlist *pevlist, static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist, PyObject *args, PyObject *kwargs) { - struct evlist *evlist =3D &pevlist->evlist; + struct evlist *evlist =3D pevlist->evlist; static char *kwlist[] =3D { "pages", "overwrite", NULL }; int pages =3D 128, overwrite =3D false; =20 @@ -1502,7 +1509,7 @@ static PyObject *pyrf_evlist__mmap(struct pyrf_evlist= *pevlist, static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist, PyObject *args, PyObject *kwargs) { - struct evlist *evlist =3D &pevlist->evlist; + struct evlist *evlist =3D pevlist->evlist; static char *kwlist[] =3D { "timeout", NULL }; int timeout =3D -1, n; =20 @@ -1522,7 +1529,7 @@ static PyObject *pyrf_evlist__get_pollfd(struct pyrf_= evlist *pevlist, PyObject *args __maybe_unused, PyObject *kwargs __maybe_unused) { - struct evlist *evlist =3D &pevlist->evlist; + struct evlist *evlist =3D pevlist->evlist; PyObject *list =3D PyList_New(0); int i; =20 @@ -1551,7 +1558,7 @@ static PyObject *pyrf_evlist__add(struct pyrf_evlist = *pevlist, PyObject *args, PyObject *kwargs __maybe_unused) { - struct evlist *evlist =3D &pevlist->evlist; + struct evlist *evlist =3D pevlist->evlist; PyObject *pevsel; struct evsel *evsel; =20 @@ -1583,7 +1590,7 @@ static struct mmap *get_md(struct evlist *evlist, int= cpu) static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, PyObject *args, PyObject *kwargs) { - struct evlist *evlist =3D &pevlist->evlist; + struct evlist *evlist =3D pevlist->evlist; union perf_event *event; int sample_id_all =3D 1, cpu; static char *kwlist[] =3D { "cpu", "sample_id_all", NULL }; @@ -1640,7 +1647,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf= _evlist *pevlist, static PyObject *pyrf_evlist__open(struct pyrf_evlist *pevlist, PyObject *args, PyObject *kwargs) { - struct evlist *evlist =3D &pevlist->evlist; + struct evlist *evlist =3D pevlist->evlist; =20 if (evlist__open(evlist) < 0) { PyErr_SetFromErrno(PyExc_OSError); @@ -1653,7 +1660,7 @@ static PyObject *pyrf_evlist__open(struct pyrf_evlist= *pevlist, =20 static PyObject *pyrf_evlist__close(struct pyrf_evlist *pevlist) { - struct evlist *evlist =3D &pevlist->evlist; + struct evlist *evlist =3D pevlist->evlist; =20 evlist__close(evlist); =20 @@ -1679,7 +1686,7 @@ static PyObject *pyrf_evlist__config(struct pyrf_evli= st *pevlist) .no_buffering =3D true, .no_inherit =3D true, }; - struct evlist *evlist =3D &pevlist->evlist; + struct evlist *evlist =3D pevlist->evlist; =20 evlist__config(evlist, &opts, &callchain_param); Py_INCREF(Py_None); @@ -1688,14 +1695,14 @@ static PyObject *pyrf_evlist__config(struct pyrf_ev= list *pevlist) =20 static PyObject *pyrf_evlist__disable(struct pyrf_evlist *pevlist) { - evlist__disable(&pevlist->evlist); + evlist__disable(pevlist->evlist); Py_INCREF(Py_None); return Py_None; } =20 static PyObject *pyrf_evlist__enable(struct pyrf_evlist *pevlist) { - evlist__enable(&pevlist->evlist); + evlist__enable(pevlist->evlist); Py_INCREF(Py_None); return Py_None; } @@ -1786,7 +1793,23 @@ static Py_ssize_t pyrf_evlist__length(PyObject *obj) { struct pyrf_evlist *pevlist =3D (void *)obj; =20 - return pevlist->evlist.core.nr_entries; + return pevlist->evlist->core.nr_entries; +} + +static PyObject *pyrf_evsel__from_evsel(struct evsel *evsel) +{ + struct pyrf_evsel *pevsel =3D PyObject_New(struct pyrf_evsel, &pyrf_evsel= __type); + + if (!pevsel) + return NULL; + + memset(&pevsel->evsel, 0, sizeof(pevsel->evsel)); + evsel__init(&pevsel->evsel, &evsel->core.attr, evsel->core.idx); + + evsel__clone(&pevsel->evsel, evsel); + if (evsel__is_group_leader(evsel)) + evsel__set_leader(&pevsel->evsel, &pevsel->evsel); + return (PyObject *)pevsel; } =20 static PyObject *pyrf_evlist__item(PyObject *obj, Py_ssize_t i) @@ -1794,17 +1817,16 @@ static PyObject *pyrf_evlist__item(PyObject *obj, P= y_ssize_t i) struct pyrf_evlist *pevlist =3D (void *)obj; struct evsel *pos; =20 - if (i >=3D pevlist->evlist.core.nr_entries) { + if (i >=3D pevlist->evlist->core.nr_entries) { PyErr_SetString(PyExc_IndexError, "Index out of range"); return NULL; } =20 - evlist__for_each_entry(&pevlist->evlist, pos) { + evlist__for_each_entry(pevlist->evlist, pos) { if (i-- =3D=3D 0) break; } - - return Py_BuildValue("O", container_of(pos, struct pyrf_evsel, evsel)); + return pyrf_evsel__from_evsel(pos); } =20 static PyObject *pyrf_evlist__str(PyObject *self) @@ -1816,7 +1838,7 @@ static PyObject *pyrf_evlist__str(PyObject *self) PyObject *result; =20 strbuf_addstr(&sb, "evlist(["); - evlist__for_each_entry(&pevlist->evlist, pos) { + evlist__for_each_entry(pevlist->evlist, pos) { if (!first) strbuf_addch(&sb, ','); if (!pos->pmu) @@ -1852,9 +1874,19 @@ static PyTypeObject pyrf_evlist__type =3D { .tp_str =3D pyrf_evlist__str, }; =20 +static PyObject *pyrf_evlist__new(PyTypeObject *type, PyObject *args, PyOb= ject *kwargs) +{ + struct pyrf_evlist *pevlist; + + pevlist =3D (struct pyrf_evlist *)PyType_GenericNew(type, args, kwargs); + if (pevlist) + pevlist->evlist =3D NULL; + return (PyObject *)pevlist; +} + static int pyrf_evlist__setup_types(void) { - pyrf_evlist__type.tp_new =3D PyType_GenericNew; + pyrf_evlist__type.tp_new =3D pyrf_evlist__new; return PyType_Ready(&pyrf_evlist__type); } =20 @@ -1957,157 +1989,74 @@ static PyObject *pyrf__tracepoint(struct pyrf_evse= l *pevsel, return PyLong_FromLong(tp_pmu__id(sys, name)); } =20 -static PyObject *pyrf_evsel__from_evsel(struct evsel *evsel) -{ - struct pyrf_evsel *pevsel =3D PyObject_New(struct pyrf_evsel, &pyrf_evsel= __type); - - if (!pevsel) - return NULL; - - memset(&pevsel->evsel, 0, sizeof(pevsel->evsel)); - evsel__init(&pevsel->evsel, &evsel->core.attr, evsel->core.idx); - - evsel__clone(&pevsel->evsel, evsel); - if (evsel__is_group_leader(evsel)) - evsel__set_leader(&pevsel->evsel, &pevsel->evsel); - return (PyObject *)pevsel; -} - -static int evlist__pos(struct evlist *evlist, struct evsel *evsel) -{ - struct evsel *pos; - int idx =3D 0; - - evlist__for_each_entry(evlist, pos) { - if (evsel =3D=3D pos) - return idx; - idx++; - } - return -1; -} - -static struct evsel *evlist__at(struct evlist *evlist, int idx) -{ - struct evsel *pos; - int idx2 =3D 0; - - evlist__for_each_entry(evlist, pos) { - if (idx =3D=3D idx2) - return pos; - idx2++; - } - return NULL; -} - static PyObject *pyrf_evlist__from_evlist(struct evlist *evlist) { struct pyrf_evlist *pevlist =3D PyObject_New(struct pyrf_evlist, &pyrf_ev= list__type); - struct evsel *pos; - struct rb_node *node; =20 if (!pevlist) return NULL; =20 - memset(&pevlist->evlist, 0, sizeof(pevlist->evlist)); - evlist__init(&pevlist->evlist, evlist->core.all_cpus, evlist->core.thread= s); - evlist__for_each_entry(evlist, pos) { - struct pyrf_evsel *pevsel =3D (void *)pyrf_evsel__from_evsel(pos); - - evlist__add(&pevlist->evlist, &pevsel->evsel); - } - evlist__for_each_entry(&pevlist->evlist, pos) { - struct evsel *leader =3D evsel__leader(pos); - - if (pos !=3D leader) { - int idx =3D evlist__pos(evlist, leader); - - if (idx >=3D 0) - evsel__set_leader(pos, evlist__at(&pevlist->evlist, idx)); - else if (leader =3D=3D NULL) - evsel__set_leader(pos, pos); - } - - leader =3D pos->metric_leader; - - if (pos !=3D leader) { - int idx =3D evlist__pos(evlist, leader); - - if (idx >=3D 0) - pos->metric_leader =3D evlist__at(&pevlist->evlist, idx); - else if (leader =3D=3D NULL) - pos->metric_leader =3D pos; - } - } - metricgroup__copy_metric_events(&pevlist->evlist, /*cgrp=3D*/NULL, - &pevlist->evlist.metric_events, - &evlist->metric_events); - for (node =3D rb_first_cached(&pevlist->evlist.metric_events.entries); no= de; - node =3D rb_next(node)) { - struct metric_event *me =3D container_of(node, struct metric_event, nd); - struct list_head *mpos; - int idx =3D evlist__pos(evlist, me->evsel); - - if (idx >=3D 0) - me->evsel =3D evlist__at(&pevlist->evlist, idx); - list_for_each(mpos, &me->head) { - struct metric_expr *e =3D container_of(mpos, struct metric_expr, nd); - - for (int j =3D 0; e->metric_events[j]; j++) { - idx =3D evlist__pos(evlist, e->metric_events[j]); - if (idx >=3D 0) - e->metric_events[j] =3D evlist__at(&pevlist->evlist, idx); - } - } - } + pevlist->evlist =3D evlist__get(evlist); return (PyObject *)pevlist; } =20 static PyObject *pyrf__parse_events(PyObject *self, PyObject *args) { const char *input; - struct evlist evlist =3D {}; + struct evlist *evlist =3D evlist__new(); struct parse_events_error err; PyObject *result; PyObject *pcpus =3D NULL, *pthreads =3D NULL; struct perf_cpu_map *cpus; struct perf_thread_map *threads; =20 - if (!PyArg_ParseTuple(args, "s|OO", &input, &pcpus, &pthreads)) + if (!evlist) + return PyErr_NoMemory(); + + if (!PyArg_ParseTuple(args, "s|OO", &input, &pcpus, &pthreads)) { + evlist__put(evlist); return NULL; + } =20 threads =3D pthreads ? ((struct pyrf_thread_map *)pthreads)->threads : NU= LL; cpus =3D pcpus ? ((struct pyrf_cpu_map *)pcpus)->cpus : NULL; =20 parse_events_error__init(&err); - evlist__init(&evlist, cpus, threads); - if (parse_events(&evlist, input, &err)) { + perf_evlist__set_maps(&evlist->core, cpus, threads); + if (parse_events(evlist, input, &err)) { parse_events_error__print(&err, input); PyErr_SetFromErrno(PyExc_OSError); + evlist__put(evlist); return NULL; } - result =3D pyrf_evlist__from_evlist(&evlist); - evlist__exit(&evlist); + result =3D pyrf_evlist__from_evlist(evlist); + evlist__put(evlist); return result; } =20 static PyObject *pyrf__parse_metrics(PyObject *self, PyObject *args) { const char *input, *pmu =3D NULL; - struct evlist evlist =3D {}; + struct evlist *evlist =3D evlist__new(); PyObject *result; PyObject *pcpus =3D NULL, *pthreads =3D NULL; struct perf_cpu_map *cpus; struct perf_thread_map *threads; int ret; =20 - if (!PyArg_ParseTuple(args, "s|sOO", &input, &pmu, &pcpus, &pthreads)) + if (!evlist) + return PyErr_NoMemory(); + + if (!PyArg_ParseTuple(args, "s|sOO", &input, &pmu, &pcpus, &pthreads)) { + evlist__put(evlist); return NULL; + } =20 threads =3D pthreads ? ((struct pyrf_thread_map *)pthreads)->threads : NU= LL; cpus =3D pcpus ? ((struct pyrf_cpu_map *)pcpus)->cpus : NULL; =20 - evlist__init(&evlist, cpus, threads); - ret =3D metricgroup__parse_groups(&evlist, pmu ?: "all", input, + perf_evlist__set_maps(&evlist->core, cpus, threads); + ret =3D metricgroup__parse_groups(evlist, pmu ?: "all", input, /*metric_no_group=3D*/ false, /*metric_no_merge=3D*/ false, /*metric_no_threshold=3D*/ true, @@ -2115,12 +2064,13 @@ static PyObject *pyrf__parse_metrics(PyObject *self= , PyObject *args) /*system_wide=3D*/true, /*hardware_aware_grouping=3D*/ false); if (ret) { + evlist__put(evlist); errno =3D -ret; PyErr_SetFromErrno(PyExc_OSError); return NULL; } - result =3D pyrf_evlist__from_evlist(&evlist); - evlist__exit(&evlist); + result =3D pyrf_evlist__from_evlist(evlist); + evlist__put(evlist); return result; } =20 diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index e867de8ddaaa..8a5fc7d5e43c 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c @@ -264,7 +264,7 @@ bool evlist__can_select_event(struct evlist *evlist, co= nst char *str) ret =3D true; =20 out_delete: - evlist__delete(temp_evlist); + evlist__put(temp_evlist); return ret; } =20 diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index fe0de2a0277f..1ac6cd43c38b 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -264,7 +264,7 @@ void perf_session__delete(struct perf_session *session) machines__exit(&session->machines); if (session->data) { if (perf_data__is_read(session->data)) - evlist__delete(session->evlist); + evlist__put(session->evlist); perf_data__close(session->data); } #ifdef HAVE_LIBTRACEEVENT diff --git a/tools/perf/util/sideband_evlist.c b/tools/perf/util/sideband_e= vlist.c index 388846f17bc1..b84a5463e039 100644 --- a/tools/perf/util/sideband_evlist.c +++ b/tools/perf/util/sideband_evlist.c @@ -102,7 +102,7 @@ int evlist__start_sb_thread(struct evlist *evlist, stru= ct target *target) return 0; =20 if (evlist__create_maps(evlist, target)) - goto out_delete_evlist; + goto out_put_evlist; =20 if (evlist->core.nr_entries > 1) { bool can_sample_identifier =3D perf_can_sample_identifier(); @@ -116,25 +116,25 @@ int evlist__start_sb_thread(struct evlist *evlist, st= ruct target *target) evlist__for_each_entry(evlist, counter) { if (evsel__open(counter, evlist->core.user_requested_cpus, evlist->core.threads) < 0) - goto out_delete_evlist; + goto out_put_evlist; } =20 if (evlist__mmap(evlist, UINT_MAX)) - goto out_delete_evlist; + goto out_put_evlist; =20 evlist__for_each_entry(evlist, counter) { if (evsel__enable(counter)) - goto out_delete_evlist; + goto out_put_evlist; } =20 evlist->thread.done =3D 0; if (pthread_create(&evlist->thread.th, NULL, perf_evlist__poll_thread, ev= list)) - goto out_delete_evlist; + goto out_put_evlist; =20 return 0; =20 -out_delete_evlist: - evlist__delete(evlist); +out_put_evlist: + evlist__put(evlist); evlist =3D NULL; return -1; } @@ -145,5 +145,5 @@ void evlist__stop_sb_thread(struct evlist *evlist) return; evlist->thread.done =3D 1; pthread_join(evlist->thread.th, NULL); - evlist__delete(evlist); + evlist__put(evlist); } --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8CCF73947B7 for ; Sat, 25 Apr 2026 22:50:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157438; cv=none; b=gCNpJiTMXTHF78+Qd6H4XgccEUGEVRD81rGmJMYieHWuitjhxmM24Zf7X92Ij5Z/aXs+jZWrGhUdGDdmVbDHK81oDJe3ZhMZbkrJViU75RDqrvXOuqh4q7a3EQ7cN/WIZDfzAvGcCkKDfhq0O5QijIBNFvm8qDUW2YhvFOGLkCE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157438; c=relaxed/simple; bh=2XCyoc9BwOl4x5ltIY2DvLYLgJ0ynzaEN9iUFnKounw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=P9TG3pt4hbYToi8sqdz9BmD8idruZuir5XxgjTA2qTQRIbBepYp7PCXfv2qjEED5g0U72iN6ObLPtnovDdAGZq4ETJzYfR8Ill4RVri25IKE5BxTIj5arBYeHw3GNILIV5mR2/bStaA3urHE6hIB3AywoHWDjgJRl5LcpPGkIms= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=PgO1umBZ; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="PgO1umBZ" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2de07c12745so25488141eec.1 for ; Sat, 25 Apr 2026 15:50:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157435; x=1777762235; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=W4/ldTUx+X3JFAGJYUEWulUKnsIikq3Sj7Pym4j01yg=; b=PgO1umBZ4RK2viPM2UNAoASlHq7TfWwk8Mx2uOmCHEsNtXqKTApimL2HygtfqTjLD1 EIpry3Ah3fnqwpKzVIFdwpNU7WT5W+YgJRE8+A+29yrB5ykNaEZNg7Im7apCN/vCeyB+ Mi4cr+Vmo8p2oXcNP3CTyZf2gVjkH1WJ6AMOwKN2ELIVKRlQ6irBA6uJLBauB76/M35T ZZbIlzwmpx+DnqNIffduhi0tL9fgVBoP0TgpdrBPRM7STsKVBJDKhjsJp8/DUIDuxfV+ lSTxCsz8gC8RUP4tUqscq38ey8j2ZSpXJQHH3TPsRcYHgptHIIQefUotRFeQa3Zef+mE M2ag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157435; x=1777762235; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=W4/ldTUx+X3JFAGJYUEWulUKnsIikq3Sj7Pym4j01yg=; b=e/lcBL/N4EB3wDKdIjXfx5pGfJ3JDMCHdPIAfZ62v4qLeI5YGGsG1IGOFzQ3iJ+UKA isXVHBzPQ1Af8aNvmFNAFX4zPAeMIWRt2Zm3GnTgPsneCRZYKB9eiaR3ziHdhlZvKWp+ f4VVviKYQPLU0Gr+LrgdDYS6fC/2hvVNvT7hX5kV5dViTucPkV2lAvf+pG4gOoX2jdMb 6aDF9PLgZAwGHI7W25GI1A0e/iHfLG18wlxypQaQ0ExNt8FRV/19kQc7j+qCCNWoYNId w9MGduUJdXNdSD0sKgtSA+tTyswYsT00MiPfX+KcwclzL8+6afvC6Zkpv9nCutph+hZJ 6nPA== X-Forwarded-Encrypted: i=1; AFNElJ+f8aOVpumdIaANluzFi3bIijx4GQ2snvpn1tVMiE1wWakmcfRt3uNRES9OPqWaCjxdvmTjIlaWvUlJJos=@vger.kernel.org X-Gm-Message-State: AOJu0Yx1udrdysyR5yyI4VRC0pxR98aADFEftlHAGctH4JzsoemUTXVp rdKbQj31Z1ZRHC/pdkJLjKHP7Y1mSSdXRA6iwDoko1/p59nzU1IvK+lo8hMEDqvAv9+s9HFE+0S eF4YJbjbXog== X-Received: from dyckg11.prod.google.com ([2002:a05:7301:d18b:b0:2e8:5e04:ef25]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:6da7:b0:2d1:299f:521a with SMTP id 5a478bee46e88-2e483d82ca9mr22598718eec.26.1777157434419; Sat, 25 Apr 2026 15:50:34 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:03 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-12-irogers@google.com> Subject: [PATCH v7 11/59] perf evsel: Add reference count From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" As with evlist this a no-op for most of the perf tool. The reference count is set to 1 at allocation, the put will see the 1, decrement it and perform the delete. The purpose for adding the reference count is for the python code. Prior to this change the python code would clone evsels, but this has issues if events are opened, etc. leading to assertion failures. With a reference count the same evsel can be used and the reference count incremented for the python usage. To not change the python evsel API getset functions are added for the evsel members, no set function is provided for size as it doesn't make sense to alter this. Signed-off-by: Ian Rogers --- v2: 1. Fixed Potential Crash in pyrf_event__new : Initialized pevent->evsel =3D NULL; to avoid garbage pointer dereference if evlist__event2evsel() fails in read_on_cpu . 2. Fixed Memory Leak in pyrf_evsel__init : Added evsel__put(pevsel->evsel) before overwriting it to handle repeated __init__ calls. 3. Fixed Exception Contract: Added PyErr_NoMemory() when evsel__new() fails in pyrf_evsel__init . 4. Fixed NULL Pointer Dereference on Property Access: Added a custom tp_getattro ( pyrf_evsel__getattro ) to pyrf_evsel__type to check if pevsel->evsel is NULL and raise a ValueError if so, covering all property accesses. 5. Fixed Reference Count in pyrf_evlist__add : Added evsel__get(evsel) when adding to the evlist . 6. Fixed Reference Count in pyrf_evlist__read_on_cpu : Added evsel__get(evsel) when assigning to pevent->evsel . v7: - Added pyrf_evsel__new to zero-initialize pevsel->evsel to fix crash on re-initialization. --- tools/perf/builtin-trace.c | 12 +- tools/perf/tests/evsel-tp-sched.c | 4 +- tools/perf/tests/openat-syscall-all-cpus.c | 6 +- tools/perf/tests/openat-syscall.c | 6 +- tools/perf/util/bpf_counter_cgroup.c | 2 +- tools/perf/util/cgroup.c | 2 +- tools/perf/util/evlist.c | 2 +- tools/perf/util/evsel.c | 26 ++- tools/perf/util/evsel.h | 11 +- tools/perf/util/parse-events.y | 2 +- tools/perf/util/pfm.c | 2 +- tools/perf/util/print-events.c | 2 +- tools/perf/util/python.c | 243 +++++++++++++++++---- tools/perf/util/session.c | 1 + 14 files changed, 248 insertions(+), 73 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index da703d762433..6ea935c13538 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -448,10 +448,10 @@ static int evsel__init_tp_ptr_field(struct evsel *evs= el, struct tp_field *field, ({ struct syscall_tp *sc =3D __evsel__syscall_tp(evsel);\ evsel__init_tp_ptr_field(evsel, &sc->name, #name); }) =20 -static void evsel__delete_priv(struct evsel *evsel) +static void evsel__put_and_free_priv(struct evsel *evsel) { zfree(&evsel->priv); - evsel__delete(evsel); + evsel__put(evsel); } =20 static int evsel__init_syscall_tp(struct evsel *evsel) @@ -531,7 +531,7 @@ static struct evsel *perf_evsel__raw_syscall_newtp(cons= t char *direction, void * return evsel; =20 out_delete: - evsel__delete_priv(evsel); + evsel__put_and_free_priv(evsel); return NULL; } =20 @@ -3584,7 +3584,7 @@ static bool evlist__add_vfs_getname(struct evlist *ev= list) =20 list_del_init(&evsel->core.node); evsel->evlist =3D NULL; - evsel__delete(evsel); + evsel__put(evsel); } =20 return found; @@ -3698,9 +3698,9 @@ static int trace__add_syscall_newtp(struct trace *tra= ce) return ret; =20 out_delete_sys_exit: - evsel__delete_priv(sys_exit); + evsel__put_and_free_priv(sys_exit); out_delete_sys_enter: - evsel__delete_priv(sys_enter); + evsel__put_and_free_priv(sys_enter); goto out; } =20 diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-= sched.c index 226196fb9677..9e456f88a13a 100644 --- a/tools/perf/tests/evsel-tp-sched.c +++ b/tools/perf/tests/evsel-tp-sched.c @@ -64,7 +64,7 @@ static int test__perf_evsel__tp_sched_test(struct test_su= ite *test __maybe_unuse if (evsel__test_field(evsel, "next_prio", 4, true)) ret =3D TEST_FAIL; =20 - evsel__delete(evsel); + evsel__put(evsel); =20 evsel =3D evsel__newtp("sched", "sched_wakeup"); =20 @@ -85,7 +85,7 @@ static int test__perf_evsel__tp_sched_test(struct test_su= ite *test __maybe_unuse if (evsel__test_field(evsel, "target_cpu", 4, true)) ret =3D TEST_FAIL; =20 - evsel__delete(evsel); + evsel__put(evsel); return ret; } =20 diff --git a/tools/perf/tests/openat-syscall-all-cpus.c b/tools/perf/tests/= openat-syscall-all-cpus.c index 0be43f8db3bd..cc63df2b3bc5 100644 --- a/tools/perf/tests/openat-syscall-all-cpus.c +++ b/tools/perf/tests/openat-syscall-all-cpus.c @@ -59,7 +59,7 @@ static int test__openat_syscall_event_on_all_cpus(struct = test_suite *test __mayb "tweak /proc/sys/kernel/perf_event_paranoid?\n", str_error_r(errno, sbuf, sizeof(sbuf))); err =3D TEST_SKIP; - goto out_evsel_delete; + goto out_evsel_put; } =20 perf_cpu_map__for_each_cpu(cpu, idx, cpus) { @@ -116,8 +116,8 @@ static int test__openat_syscall_event_on_all_cpus(struc= t test_suite *test __mayb evsel__free_counts(evsel); out_close_fd: perf_evsel__close_fd(&evsel->core); -out_evsel_delete: - evsel__delete(evsel); +out_evsel_put: + evsel__put(evsel); out_cpu_map_delete: perf_cpu_map__put(cpus); out_thread_map_delete: diff --git a/tools/perf/tests/openat-syscall.c b/tools/perf/tests/openat-sy= scall.c index b54cbe5f1808..9f16f0dd3a29 100644 --- a/tools/perf/tests/openat-syscall.c +++ b/tools/perf/tests/openat-syscall.c @@ -42,7 +42,7 @@ static int test__openat_syscall_event(struct test_suite *= test __maybe_unused, "tweak /proc/sys/kernel/perf_event_paranoid?\n", str_error_r(errno, sbuf, sizeof(sbuf))); err =3D TEST_SKIP; - goto out_evsel_delete; + goto out_evsel_put; } =20 for (i =3D 0; i < nr_openat_calls; ++i) { @@ -64,8 +64,8 @@ static int test__openat_syscall_event(struct test_suite *= test __maybe_unused, err =3D TEST_OK; out_close_fd: perf_evsel__close_fd(&evsel->core); -out_evsel_delete: - evsel__delete(evsel); +out_evsel_put: + evsel__put(evsel); out_thread_map_delete: perf_thread_map__put(threads); return err; diff --git a/tools/perf/util/bpf_counter_cgroup.c b/tools/perf/util/bpf_cou= nter_cgroup.c index 519fee3dc3d0..339df94ef438 100644 --- a/tools/perf/util/bpf_counter_cgroup.c +++ b/tools/perf/util/bpf_counter_cgroup.c @@ -316,7 +316,7 @@ static int bperf_cgrp__destroy(struct evsel *evsel) return 0; =20 bperf_cgroup_bpf__destroy(skel); - evsel__delete(cgrp_switch); // it'll destroy on_switch progs too + evsel__put(cgrp_switch); // it'll destroy on_switch progs too =20 return 0; } diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index 652a45aac828..914744724467 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -469,7 +469,7 @@ int evlist__expand_cgroup(struct evlist *evlist, const = char *str, bool open_cgro =20 /* copy the list and set to the new cgroup. */ evlist__for_each_entry(orig_list, pos) { - struct evsel *evsel =3D evsel__clone(/*dest=3D*/NULL, pos); + struct evsel *evsel =3D evsel__clone(pos); =20 if (evsel =3D=3D NULL) goto out_err; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index b5a7895debf5..a362f338f104 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -194,7 +194,7 @@ static void evlist__purge(struct evlist *evlist) evlist__for_each_entry_safe(evlist, n, pos) { list_del_init(&pos->core.node); pos->evlist =3D NULL; - evsel__delete(pos); + evsel__put(pos); } =20 evlist->core.nr_entries =3D 0; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index e03727d395e9..a54aae079c22 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -388,10 +388,11 @@ bool evsel__is_function_event(struct evsel *evsel) #undef FUNCTION_EVENT } =20 -void evsel__init(struct evsel *evsel, +static void evsel__init(struct evsel *evsel, struct perf_event_attr *attr, int idx) { perf_evsel__init(&evsel->core, attr, idx); + refcount_set(&evsel->refcnt, 1); evsel->tracking =3D !idx; evsel->unit =3D strdup(""); evsel->scale =3D 1.0; @@ -472,7 +473,7 @@ static int evsel__copy_config_terms(struct evsel *dst, = struct evsel *src) * The assumption is that @orig is not configured nor opened yet. * So we only care about the attributes that can be set while it's parsed. */ -struct evsel *evsel__clone(struct evsel *dest, struct evsel *orig) +struct evsel *evsel__clone(struct evsel *orig) { struct evsel *evsel; =20 @@ -485,11 +486,7 @@ struct evsel *evsel__clone(struct evsel *dest, struct = evsel *orig) if (orig->bpf_obj) return NULL; =20 - if (dest) - evsel =3D dest; - else - evsel =3D evsel__new(&orig->core.attr); - + evsel =3D evsel__new(&orig->core.attr); if (evsel =3D=3D NULL) return NULL; =20 @@ -574,7 +571,7 @@ struct evsel *evsel__clone(struct evsel *dest, struct e= vsel *orig) return evsel; =20 out_err: - evsel__delete(evsel); + evsel__put(evsel); return NULL; } =20 @@ -633,6 +630,12 @@ struct evsel *evsel__newtp_idx(const char *sys, const = char *name, int idx, bool return ERR_PTR(err); } =20 +struct evsel *evsel__get(struct evsel *evsel) +{ + refcount_inc(&evsel->refcnt); + return evsel; +} + #ifdef HAVE_LIBTRACEEVENT struct tep_event *evsel__tp_format(struct evsel *evsel) { @@ -1857,7 +1860,7 @@ void evsel__set_priv_destructor(void (*destructor)(vo= id *priv)) evsel__priv_destructor =3D destructor; } =20 -void evsel__exit(struct evsel *evsel) +static void evsel__exit(struct evsel *evsel) { assert(list_empty(&evsel->core.node)); assert(evsel->evlist =3D=3D NULL); @@ -1892,11 +1895,14 @@ void evsel__exit(struct evsel *evsel) xyarray__delete(evsel->start_times); } =20 -void evsel__delete(struct evsel *evsel) +void evsel__put(struct evsel *evsel) { if (!evsel) return; =20 + if (!refcount_dec_and_test(&evsel->refcnt)) + return; + evsel__exit(evsel); free(evsel); } diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index b099c8e5dd86..35b1bbca9036 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -6,6 +6,7 @@ =20 #include #include +#include #include #include =20 @@ -47,6 +48,7 @@ typedef int (evsel__sb_cb_t)(union perf_event *event, voi= d *data); struct evsel { struct perf_evsel core; struct evlist *evlist; + refcount_t refcnt; off_t id_offset; int id_pos; int is_pos; @@ -262,7 +264,7 @@ static inline struct evsel *evsel__new(struct perf_even= t_attr *attr) return evsel__new_idx(attr, 0); } =20 -struct evsel *evsel__clone(struct evsel *dest, struct evsel *orig); +struct evsel *evsel__clone(struct evsel *orig); =20 int copy_config_terms(struct list_head *dst, struct list_head *src); void free_config_terms(struct list_head *config_terms); @@ -277,14 +279,13 @@ static inline struct evsel *evsel__newtp(const char *= sys, const char *name) return evsel__newtp_idx(sys, name, 0, true); } =20 +struct evsel *evsel__get(struct evsel *evsel); +void evsel__put(struct evsel *evsel); + #ifdef HAVE_LIBTRACEEVENT struct tep_event *evsel__tp_format(struct evsel *evsel); #endif =20 -void evsel__init(struct evsel *evsel, struct perf_event_attr *attr, int id= x); -void evsel__exit(struct evsel *evsel); -void evsel__delete(struct evsel *evsel); - void evsel__set_priv_destructor(void (*destructor)(void *priv)); =20 struct callchain_param; diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index c194de5ec1ec..b531b1f0ceb3 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -47,7 +47,7 @@ static void free_list_evsel(struct list_head* list_evsel) =20 list_for_each_entry_safe(evsel, tmp, list_evsel, core.node) { list_del_init(&evsel->core.node); - evsel__delete(evsel); + evsel__put(evsel); } free(list_evsel); } diff --git a/tools/perf/util/pfm.c b/tools/perf/util/pfm.c index d9043f4afbe7..5f53c2f68a96 100644 --- a/tools/perf/util/pfm.c +++ b/tools/perf/util/pfm.c @@ -159,7 +159,7 @@ static bool is_libpfm_event_supported(const char *name,= struct perf_cpu_map *cpu result =3D false; =20 evsel__close(evsel); - evsel__delete(evsel); + evsel__put(evsel); =20 return result; } diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c index cb27e2898aa0..0242243681b6 100644 --- a/tools/perf/util/print-events.c +++ b/tools/perf/util/print-events.c @@ -174,7 +174,7 @@ bool is_event_supported(u8 type, u64 config) } =20 evsel__close(evsel); - evsel__delete(evsel); + evsel__put(evsel); } =20 perf_thread_map__put(tmap); diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index aeecdb497fac..a0ec63d39969 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -274,6 +274,7 @@ static PyMemberDef pyrf_sample_event__members[] =3D { =20 static void pyrf_sample_event__delete(struct pyrf_event *pevent) { + evsel__put(pevent->evsel); perf_sample__exit(&pevent->sample); Py_TYPE(pevent)->tp_free((PyObject*)pevent); } @@ -506,8 +507,10 @@ static PyObject *pyrf_event__new(const union perf_even= t *event) =20 ptype =3D pyrf_event__type[event->header.type]; pevent =3D PyObject_New(struct pyrf_event, ptype); - if (pevent !=3D NULL) + if (pevent !=3D NULL) { memcpy(&pevent->event, event, event->header.size); + pevent->evsel =3D NULL; + } return (PyObject *)pevent; } =20 @@ -945,7 +948,7 @@ static int pyrf_counts_values__setup_types(void) struct pyrf_evsel { PyObject_HEAD =20 - struct evsel evsel; + struct evsel *evsel; }; =20 static int pyrf_evsel__init(struct pyrf_evsel *pevsel, @@ -1053,20 +1056,25 @@ static int pyrf_evsel__init(struct pyrf_evsel *pevs= el, attr.sample_id_all =3D sample_id_all; attr.size =3D sizeof(attr); =20 - evsel__init(&pevsel->evsel, &attr, idx); + evsel__put(pevsel->evsel); + pevsel->evsel =3D evsel__new(&attr); + if (!pevsel->evsel) { + PyErr_NoMemory(); + return -1; + } return 0; } =20 static void pyrf_evsel__delete(struct pyrf_evsel *pevsel) { - evsel__exit(&pevsel->evsel); + evsel__put(pevsel->evsel); Py_TYPE(pevsel)->tp_free((PyObject*)pevsel); } =20 static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, PyObject *args, PyObject *kwargs) { - struct evsel *evsel =3D &pevsel->evsel; + struct evsel *evsel =3D pevsel->evsel; struct perf_cpu_map *cpus =3D NULL; struct perf_thread_map *threads =3D NULL; PyObject *pcpus =3D NULL, *pthreads =3D NULL; @@ -1102,7 +1110,7 @@ static PyObject *pyrf_evsel__cpus(struct pyrf_evsel *= pevsel) struct pyrf_cpu_map *pcpu_map =3D PyObject_New(struct pyrf_cpu_map, &pyrf= _cpu_map__type); =20 if (pcpu_map) - pcpu_map->cpus =3D perf_cpu_map__get(pevsel->evsel.core.cpus); + pcpu_map->cpus =3D perf_cpu_map__get(pevsel->evsel->core.cpus); =20 return (PyObject *)pcpu_map; } @@ -1113,7 +1121,7 @@ static PyObject *pyrf_evsel__threads(struct pyrf_evse= l *pevsel) PyObject_New(struct pyrf_thread_map, &pyrf_thread_map__type); =20 if (pthread_map) - pthread_map->threads =3D perf_thread_map__get(pevsel->evsel.core.threads= ); + pthread_map->threads =3D perf_thread_map__get(pevsel->evsel->core.thread= s); =20 return (PyObject *)pthread_map; } @@ -1147,7 +1155,7 @@ static int evsel__ensure_counts(struct evsel *evsel) static PyObject *pyrf_evsel__read(struct pyrf_evsel *pevsel, PyObject *args, PyObject *kwargs) { - struct evsel *evsel =3D &pevsel->evsel; + struct evsel *evsel =3D pevsel->evsel; int cpu =3D 0, cpu_idx, thread =3D 0, thread_idx; struct perf_counts_values *old_count, *new_count; struct pyrf_counts_values *count_values =3D PyObject_New(struct pyrf_coun= ts_values, @@ -1192,7 +1200,7 @@ static PyObject *pyrf_evsel__read(struct pyrf_evsel *= pevsel, static PyObject *pyrf_evsel__str(PyObject *self) { struct pyrf_evsel *pevsel =3D (void *)self; - struct evsel *evsel =3D &pevsel->evsel; + struct evsel *evsel =3D pevsel->evsel; =20 return PyUnicode_FromFormat("evsel(%s/%s/)", evsel__pmu_name(evsel), evse= l__name(evsel)); } @@ -1225,30 +1233,183 @@ static PyMethodDef pyrf_evsel__methods[] =3D { { .ml_name =3D NULL, } }; =20 -#define evsel_member_def(member, ptype, help) \ - { #member, ptype, \ - offsetof(struct pyrf_evsel, evsel.member), \ - 0, help } +static PyObject *pyrf_evsel__get_tracking(PyObject *self, void */*closure*= /) +{ + struct pyrf_evsel *pevsel =3D (void *)self; =20 -#define evsel_attr_member_def(member, ptype, help) \ - { #member, ptype, \ - offsetof(struct pyrf_evsel, evsel.core.attr.member), \ - 0, help } + if (pevsel->evsel->tracking) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} =20 -static PyMemberDef pyrf_evsel__members[] =3D { - evsel_member_def(tracking, T_BOOL, "tracking event."), - evsel_attr_member_def(type, T_UINT, "attribute type."), - evsel_attr_member_def(size, T_UINT, "attribute size."), - evsel_attr_member_def(config, T_ULONGLONG, "attribute config."), - evsel_attr_member_def(sample_period, T_ULONGLONG, "attribute sample_perio= d."), - evsel_attr_member_def(sample_type, T_ULONGLONG, "attribute sample_type."), - evsel_attr_member_def(read_format, T_ULONGLONG, "attribute read_format."), - evsel_attr_member_def(wakeup_events, T_UINT, "attribute wakeup_events."), - { .name =3D NULL, }, +static int pyrf_evsel__set_tracking(PyObject *self, PyObject *val, void */= *closure*/) +{ + struct pyrf_evsel *pevsel =3D (void *)self; + + pevsel->evsel->tracking =3D Py_IsTrue(val) ? true : false; + return 0; +} + +static int pyrf_evsel__set_attr_config(PyObject *self, PyObject *val, void= */*closure*/) +{ + struct pyrf_evsel *pevsel =3D (void *)self; + + pevsel->evsel->core.attr.config =3D PyLong_AsUnsignedLongLong(val); + return PyErr_Occurred() ? -1 : 0; +} + +static PyObject *pyrf_evsel__get_attr_config(PyObject *self, void */*closu= re*/) +{ + struct pyrf_evsel *pevsel =3D (void *)self; + + return PyLong_FromUnsignedLongLong(pevsel->evsel->core.attr.config); +} + +static int pyrf_evsel__set_attr_read_format(PyObject *self, PyObject *val,= void */*closure*/) +{ + struct pyrf_evsel *pevsel =3D (void *)self; + + pevsel->evsel->core.attr.read_format =3D PyLong_AsUnsignedLongLong(val); + return PyErr_Occurred() ? -1 : 0; +} + +static PyObject *pyrf_evsel__get_attr_read_format(PyObject *self, void */*= closure*/) +{ + struct pyrf_evsel *pevsel =3D (void *)self; + + return PyLong_FromUnsignedLongLong(pevsel->evsel->core.attr.read_format); +} + +static int pyrf_evsel__set_attr_sample_period(PyObject *self, PyObject *va= l, void */*closure*/) +{ + struct pyrf_evsel *pevsel =3D (void *)self; + + pevsel->evsel->core.attr.sample_period =3D PyLong_AsUnsignedLongLong(val); + return PyErr_Occurred() ? -1 : 0; +} + +static PyObject *pyrf_evsel__get_attr_sample_period(PyObject *self, void *= /*closure*/) +{ + struct pyrf_evsel *pevsel =3D (void *)self; + + return PyLong_FromUnsignedLongLong(pevsel->evsel->core.attr.sample_period= ); +} + +static int pyrf_evsel__set_attr_sample_type(PyObject *self, PyObject *val,= void */*closure*/) +{ + struct pyrf_evsel *pevsel =3D (void *)self; + + pevsel->evsel->core.attr.sample_type =3D PyLong_AsUnsignedLongLong(val); + return PyErr_Occurred() ? -1 : 0; +} + +static PyObject *pyrf_evsel__get_attr_sample_type(PyObject *self, void */*= closure*/) +{ + struct pyrf_evsel *pevsel =3D (void *)self; + + return PyLong_FromUnsignedLongLong(pevsel->evsel->core.attr.sample_type); +} + +static PyObject *pyrf_evsel__get_attr_size(PyObject *self, void */*closure= */) +{ + struct pyrf_evsel *pevsel =3D (void *)self; + + return PyLong_FromUnsignedLong(pevsel->evsel->core.attr.size); +} + +static int pyrf_evsel__set_attr_type(PyObject *self, PyObject *val, void *= /*closure*/) +{ + struct pyrf_evsel *pevsel =3D (void *)self; + + pevsel->evsel->core.attr.type =3D PyLong_AsUnsignedLong(val); + return PyErr_Occurred() ? -1 : 0; +} + +static PyObject *pyrf_evsel__get_attr_type(PyObject *self, void */*closure= */) +{ + struct pyrf_evsel *pevsel =3D (void *)self; + + return PyLong_FromUnsignedLong(pevsel->evsel->core.attr.type); +} + +static int pyrf_evsel__set_attr_wakeup_events(PyObject *self, PyObject *va= l, void */*closure*/) +{ + struct pyrf_evsel *pevsel =3D (void *)self; + + pevsel->evsel->core.attr.wakeup_events =3D PyLong_AsUnsignedLong(val); + return PyErr_Occurred() ? -1 : 0; +} + +static PyObject *pyrf_evsel__get_attr_wakeup_events(PyObject *self, void *= /*closure*/) +{ + struct pyrf_evsel *pevsel =3D (void *)self; + + return PyLong_FromUnsignedLong(pevsel->evsel->core.attr.wakeup_events); +} + +static PyGetSetDef pyrf_evsel__getset[] =3D { + { + .name =3D "tracking", + .get =3D pyrf_evsel__get_tracking, + .set =3D pyrf_evsel__set_tracking, + .doc =3D "tracking event.", + }, + { + .name =3D "config", + .get =3D pyrf_evsel__get_attr_config, + .set =3D pyrf_evsel__set_attr_config, + .doc =3D "attribute config.", + }, + { + .name =3D "read_format", + .get =3D pyrf_evsel__get_attr_read_format, + .set =3D pyrf_evsel__set_attr_read_format, + .doc =3D "attribute read_format.", + }, + { + .name =3D "sample_period", + .get =3D pyrf_evsel__get_attr_sample_period, + .set =3D pyrf_evsel__set_attr_sample_period, + .doc =3D "attribute sample_period.", + }, + { + .name =3D "sample_type", + .get =3D pyrf_evsel__get_attr_sample_type, + .set =3D pyrf_evsel__set_attr_sample_type, + .doc =3D "attribute sample_type.", + }, + { + .name =3D "size", + .get =3D pyrf_evsel__get_attr_size, + .doc =3D "attribute size.", + }, + { + .name =3D "type", + .get =3D pyrf_evsel__get_attr_type, + .set =3D pyrf_evsel__set_attr_type, + .doc =3D "attribute type.", + }, + { + .name =3D "wakeup_events", + .get =3D pyrf_evsel__get_attr_wakeup_events, + .set =3D pyrf_evsel__set_attr_wakeup_events, + .doc =3D "attribute wakeup_events.", + }, + { .name =3D NULL}, }; =20 static const char pyrf_evsel__doc[] =3D PyDoc_STR("perf event selector lis= t object."); =20 +static PyObject *pyrf_evsel__getattro(struct pyrf_evsel *pevsel, PyObject = *attr_name) +{ + if (!pevsel->evsel) { + PyErr_SetString(PyExc_ValueError, "evsel not initialized"); + return NULL; + } + return PyObject_GenericGetAttr((PyObject *) pevsel, attr_name); +} + static PyTypeObject pyrf_evsel__type =3D { PyVarObject_HEAD_INIT(NULL, 0) .tp_name =3D "perf.evsel", @@ -1256,16 +1417,27 @@ static PyTypeObject pyrf_evsel__type =3D { .tp_dealloc =3D (destructor)pyrf_evsel__delete, .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, .tp_doc =3D pyrf_evsel__doc, - .tp_members =3D pyrf_evsel__members, + .tp_getset =3D pyrf_evsel__getset, .tp_methods =3D pyrf_evsel__methods, .tp_init =3D (initproc)pyrf_evsel__init, .tp_str =3D pyrf_evsel__str, .tp_repr =3D pyrf_evsel__str, + .tp_getattro =3D (getattrofunc) pyrf_evsel__getattro, }; =20 +static PyObject *pyrf_evsel__new(PyTypeObject *type, PyObject *args, PyObj= ect *kwargs) +{ + struct pyrf_evsel *pevsel; + + pevsel =3D (struct pyrf_evsel *)PyType_GenericNew(type, args, kwargs); + if (pevsel) + pevsel->evsel =3D NULL; + return (PyObject *)pevsel; +} + static int pyrf_evsel__setup_types(void) { - pyrf_evsel__type.tp_new =3D PyType_GenericNew; + pyrf_evsel__type.tp_new =3D pyrf_evsel__new; return PyType_Ready(&pyrf_evsel__type); } =20 @@ -1566,9 +1738,9 @@ static PyObject *pyrf_evlist__add(struct pyrf_evlist = *pevlist, return NULL; =20 Py_INCREF(pevsel); - evsel =3D &((struct pyrf_evsel *)pevsel)->evsel; + evsel =3D ((struct pyrf_evsel *)pevsel)->evsel; evsel->core.idx =3D evlist->core.nr_entries; - evlist__add(evlist, evsel); + evlist__add(evlist, evsel__get(evsel)); =20 return Py_BuildValue("i", evlist->core.nr_entries); } @@ -1626,7 +1798,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf= _evlist *pevlist, return Py_None; } =20 - pevent->evsel =3D evsel; + pevent->evsel =3D evsel__get(evsel); =20 perf_mmap__consume(&md->core); =20 @@ -1803,12 +1975,7 @@ static PyObject *pyrf_evsel__from_evsel(struct evsel= *evsel) if (!pevsel) return NULL; =20 - memset(&pevsel->evsel, 0, sizeof(pevsel->evsel)); - evsel__init(&pevsel->evsel, &evsel->core.attr, evsel->core.idx); - - evsel__clone(&pevsel->evsel, evsel); - if (evsel__is_group_leader(evsel)) - evsel__set_leader(&pevsel->evsel, &pevsel->evsel); + pevsel->evsel =3D evsel__get(evsel); return (PyObject *)pevsel; } =20 diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 1ac6cd43c38b..deb5b9dfe44c 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1373,6 +1373,7 @@ static int evlist__deliver_deferred_callchain(struct = evlist *evlist, sample->evsel =3D evlist__id2evsel(evlist, sample->id); ret =3D tool->callchain_deferred(tool, event, sample, sample->evsel, machine); + evsel__put(sample->evsel); sample->evsel =3D saved_evsel; return ret; } --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A25A339479B for ; Sat, 25 Apr 2026 22:50:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157450; cv=none; b=TrLRtP0NA6bM8BybkSsT5rBStQpKkDDkAoyOKVJjecGhbtfM5GDF0lo/RaZ4ZqktChg6y2WnOwNkkdhc8yzDbVAav4rbXJTSVtsz4xN7Qm8IfY/3aSIqxJZjtE2Om6gboJV+hX+kf48Y6ZnteCciEhjt137+1XlnsiN4iHl73FA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157450; c=relaxed/simple; bh=dAzWD5Bwl1G3x6e9tpKWThvQtzj9ie23un3dNc951Pg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=hJ2PBAXY3D6/5aiYkwut0LURnEe0mZsY4uIlgiVdr0TxDvg9c1sEYeKL5N3E5AlKYMSwjIyJehT1uuzsWbKWU6aC/V8b8R8xyx5HaX90CS+/Zl8vtFBUz4PJ2prE0ubD4o/159Nuj1RutAUttuNDabHwyAxDAJsEhpukmNAhCe4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=ms6QEATi; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="ms6QEATi" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2c0f6593ef5so11439571eec.1 for ; Sat, 25 Apr 2026 15:50:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157437; x=1777762237; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=+Ea86zoowaajW4P8UXDyFtKsCd9e4VX6oJCHzI9t1wo=; b=ms6QEATidAxUJdZovCuIOmnXAA2zM/EFeLmWRiLmm/n18xuOg7bxp6Ux2J9dagVZ8+ oRoEDuaEYMBDBcYu/N7D0j5HTbPVDH1okAYTUcK2ezKeMmaHYSfo9TE/vr1XSTov+lD/ uKqzLaKcXZYFc/esvQhAm9cFh3CPjVNYmBg79kqqHEpa2NZ7+Vlto7XViyUOark3M2Ix qoAnkskAs7C9GhojHrieD8zAuasDT+WW9RUQqdOkiI7N9mAXsKMVV2UXSGzmyegjaOZ9 cbuSf+ZcB/fztlgn8AgCNwNE+HB8bnQ9i1Ig9+yngerijp6ALyLYYflvNzGqMzBSxrJp BWaQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157437; x=1777762237; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=+Ea86zoowaajW4P8UXDyFtKsCd9e4VX6oJCHzI9t1wo=; b=LVXhYL7ed7qf1rUQ0MwPlj8qyDGh+49/D9+i8DMkBfr8zbcvCbeYPO29pm3wG5ewnt lU0zI6Ytxrm03nPNahhjO6PQ3H2tp6Ur7fJ4npVT80S+ca5OOTMCxMF8l5U1zQ79rrHn VhovAj5m0DsmpX6ZbAcS1GTecEJdZ9jvqMHEU/qG/PT9sXeBGCZ5djZE1DRlCGDs4WcS 2YC0Uj0JRh5WE+B1+QSw/DabXssZueb4ZOes32f8GNk+r69NRCDctJVVe7aCfjq+yUxZ fYaoANNCQnHWhPOibHVAhoG7fuzKkQtFZGEjJ3thhKzW5orr8NSPcqEOTpvui9aDD9Bd iSWA== X-Forwarded-Encrypted: i=1; AFNElJ9ByETrOK47ePFP8kT1wEhROnW0Ftedbn14pwToxAac3/i5UM1ZacnMoHNEd00JgimrC1dFNrNURZo1uFc=@vger.kernel.org X-Gm-Message-State: AOJu0YwYv8zMw/1k3K8JAV+FyX79X14yjWac6ovJOApL4t6omDdhfeTD cNCqd5/zhDg0dDTgut/dT/z7ZHcJkWEfp58lzkLYn7cW2+Kmh+M19Ccs8GypYoF3eIuQQwUgKlv bxOELVeTIxg== X-Received: from dyjf27.prod.google.com ([2002:a05:7300:681b:b0:2d8:c14d:f374]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7301:1e8c:b0:2e2:4979:eb5 with SMTP id 5a478bee46e88-2e46c48a953mr20207840eec.10.1777157436617; Sat, 25 Apr 2026 15:50:36 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:04 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-13-irogers@google.com> Subject: [PATCH v7 12/59] perf evlist: Add reference count checking From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Now the evlist is reference counted, add reference count checking so that gets and puts are paired and easy to debug. Reference count checking is documented here: https://perfwiki.github.io/main/reference-count-checking/ This large patch is adding accessors to evlist functions and switching to their use. There was some minor renaming as evlist__mmap is now an accessor to the mmap variable, and the original evlist__mmap is renamed to evlist__do_mmap. Signed-off-by: Ian Rogers --- v2: 1. Fixed Memory Leak in evlist__new : Added free(evlist) in the else branch if ADD_RC_CHK fails, preventing a leak of the allocated raw structure. 2. Fixed Potential NULL Dereference: Added a NULL check after from_list_start(_evlist) in perf_evlist__mmap_cb_get() . 3. Fixed Use-After-Free Risk: In evlist__add() , I changed entry->evlist =3D evlist; to entry->evlist =3D evlist__get(evlist); . This ensures that the evsel holds a valid reference (wrapper) to the evlist , preventing it from becoming a dangling pointer if the original wrapper is freed. 4. Fixed Test Masking Bug: In test__perf_time__parse_for_ranges() , I replaced TEST_ASSERT_VAL with a manual check and return false; to avoid boolean evaluation of -1 inadvertently passing the test. 5. Fix reference count checker memory leaks from missed puts and due to cyclic evsel to evlist references. A leak still exists in __perf_evlist__propagate_maps due to empty CPU maps and not deleting the removed evsel. --- tools/perf/arch/arm/util/cs-etm.c | 10 +- tools/perf/arch/arm64/util/arm-spe.c | 8 +- tools/perf/arch/arm64/util/hisi-ptt.c | 2 +- tools/perf/arch/x86/tests/hybrid.c | 20 +- tools/perf/arch/x86/util/auxtrace.c | 2 +- tools/perf/arch/x86/util/intel-bts.c | 6 +- tools/perf/arch/x86/util/intel-pt.c | 9 +- tools/perf/arch/x86/util/iostat.c | 6 +- tools/perf/bench/evlist-open-close.c | 11 +- tools/perf/builtin-annotate.c | 2 +- tools/perf/builtin-ftrace.c | 6 +- tools/perf/builtin-inject.c | 4 +- tools/perf/builtin-kvm.c | 10 +- tools/perf/builtin-kwork.c | 8 +- tools/perf/builtin-record.c | 91 ++--- tools/perf/builtin-report.c | 6 +- tools/perf/builtin-sched.c | 20 +- tools/perf/builtin-script.c | 13 +- tools/perf/builtin-stat.c | 71 ++-- tools/perf/builtin-top.c | 52 +-- tools/perf/builtin-trace.c | 22 +- tools/perf/tests/backward-ring-buffer.c | 8 +- tools/perf/tests/code-reading.c | 10 +- tools/perf/tests/event-times.c | 2 +- tools/perf/tests/event_update.c | 2 +- tools/perf/tests/expand-cgroup.c | 4 +- tools/perf/tests/hwmon_pmu.c | 5 +- tools/perf/tests/keep-tracking.c | 8 +- tools/perf/tests/mmap-basic.c | 6 +- tools/perf/tests/openat-syscall-tp-fields.c | 8 +- tools/perf/tests/parse-events.c | 135 +++---- tools/perf/tests/parse-metric.c | 4 +- tools/perf/tests/perf-record.c | 20 +- tools/perf/tests/perf-time-to-tsc.c | 10 +- tools/perf/tests/pfm.c | 8 +- tools/perf/tests/pmu-events.c | 5 +- tools/perf/tests/sample-parsing.c | 38 +- tools/perf/tests/sw-clock.c | 6 +- tools/perf/tests/switch-tracking.c | 8 +- tools/perf/tests/task-exit.c | 6 +- tools/perf/tests/time-utils-test.c | 14 +- tools/perf/tests/tool_pmu.c | 5 +- tools/perf/tests/topology.c | 2 +- tools/perf/ui/browsers/annotate.c | 2 +- tools/perf/ui/browsers/hists.c | 22 +- tools/perf/util/amd-sample-raw.c | 2 +- tools/perf/util/annotate-data.c | 2 +- tools/perf/util/annotate.c | 10 +- tools/perf/util/auxtrace.c | 14 +- tools/perf/util/block-info.c | 4 +- tools/perf/util/bpf_counter.c | 2 +- tools/perf/util/bpf_counter_cgroup.c | 8 +- tools/perf/util/bpf_ftrace.c | 9 +- tools/perf/util/bpf_lock_contention.c | 12 +- tools/perf/util/bpf_off_cpu.c | 14 +- tools/perf/util/cgroup.c | 20 +- tools/perf/util/evlist.c | 386 ++++++++++++-------- tools/perf/util/evlist.h | 251 ++++++++++++- tools/perf/util/evsel.c | 6 +- tools/perf/util/evsel.h | 4 +- tools/perf/util/header.c | 39 +- tools/perf/util/header.h | 2 +- tools/perf/util/intel-tpebs.c | 7 +- tools/perf/util/metricgroup.c | 6 +- tools/perf/util/parse-events.c | 6 +- tools/perf/util/pfm.c | 2 +- tools/perf/util/python.c | 30 +- tools/perf/util/record.c | 9 +- tools/perf/util/sample-raw.c | 4 +- tools/perf/util/session.c | 56 +-- tools/perf/util/sideband_evlist.c | 24 +- tools/perf/util/sort.c | 2 +- tools/perf/util/stat-display.c | 6 +- tools/perf/util/stat-shadow.c | 4 +- tools/perf/util/stat.c | 4 +- tools/perf/util/stream.c | 4 +- tools/perf/util/synthetic-events.c | 11 +- tools/perf/util/time-utils.c | 12 +- tools/perf/util/top.c | 4 +- 79 files changed, 1004 insertions(+), 689 deletions(-) diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/c= s-etm.c index cdf8e3e60606..d2861d66a661 100644 --- a/tools/perf/arch/arm/util/cs-etm.c +++ b/tools/perf/arch/arm/util/cs-etm.c @@ -201,7 +201,7 @@ static int cs_etm_validate_config(struct perf_pmu *cs_e= tm_pmu, { unsigned int idx; int err =3D 0; - struct perf_cpu_map *event_cpus =3D evsel->evlist->core.user_requested_cp= us; + struct perf_cpu_map *event_cpus =3D evlist__core(evsel->evlist)->user_req= uested_cpus; struct perf_cpu_map *intersect_cpus; struct perf_cpu cpu; =20 @@ -325,7 +325,7 @@ static int cs_etm_recording_options(struct auxtrace_rec= ord *itr, container_of(itr, struct cs_etm_recording, itr); struct perf_pmu *cs_etm_pmu =3D ptr->cs_etm_pmu; struct evsel *evsel, *cs_etm_evsel =3D NULL; - struct perf_cpu_map *cpus =3D evlist->core.user_requested_cpus; + struct perf_cpu_map *cpus =3D evlist__core(evlist)->user_requested_cpus; bool privileged =3D perf_event_paranoid_check(-1); int err =3D 0; =20 @@ -551,7 +551,7 @@ cs_etm_info_priv_size(struct auxtrace_record *itr, { unsigned int idx; int etmv3 =3D 0, etmv4 =3D 0, ete =3D 0; - struct perf_cpu_map *event_cpus =3D evlist->core.user_requested_cpus; + struct perf_cpu_map *event_cpus =3D evlist__core(evlist)->user_requested_= cpus; struct perf_cpu_map *intersect_cpus; struct perf_cpu cpu; struct perf_pmu *cs_etm_pmu =3D cs_etm_get_pmu(itr); @@ -790,7 +790,7 @@ static int cs_etm_info_fill(struct auxtrace_record *itr, u32 offset; u64 nr_cpu, type; struct perf_cpu_map *cpu_map; - struct perf_cpu_map *event_cpus =3D session->evlist->core.user_requested_= cpus; + struct perf_cpu_map *event_cpus =3D evlist__core(session->evlist)->user_r= equested_cpus; struct perf_cpu_map *online_cpus =3D perf_cpu_map__new_online_cpus(); struct cs_etm_recording *ptr =3D container_of(itr, struct cs_etm_recording, itr); @@ -800,7 +800,7 @@ static int cs_etm_info_fill(struct auxtrace_record *itr, if (priv_size !=3D cs_etm_info_priv_size(itr, session->evlist)) return -EINVAL; =20 - if (!session->evlist->core.nr_mmaps) + if (!evlist__core(session->evlist)->nr_mmaps) return -EINVAL; =20 /* If the cpu_map has the "any" CPU all online CPUs are involved */ diff --git a/tools/perf/arch/arm64/util/arm-spe.c b/tools/perf/arch/arm64/u= til/arm-spe.c index f00d72d087fc..abbc67109fc0 100644 --- a/tools/perf/arch/arm64/util/arm-spe.c +++ b/tools/perf/arch/arm64/util/arm-spe.c @@ -60,7 +60,7 @@ static bool arm_spe_is_set_freq(struct evsel *evsel) */ static struct perf_cpu_map *arm_spe_find_cpus(struct evlist *evlist) { - struct perf_cpu_map *event_cpus =3D evlist->core.user_requested_cpus; + struct perf_cpu_map *event_cpus =3D evlist__core(evlist)->user_requested_= cpus; struct perf_cpu_map *online_cpus =3D perf_cpu_map__new_online_cpus(); struct perf_cpu_map *intersect_cpus; =20 @@ -157,7 +157,7 @@ static int arm_spe_info_fill(struct auxtrace_record *it= r, if (priv_size !=3D arm_spe_info_priv_size(itr, session->evlist)) return -EINVAL; =20 - if (!session->evlist->core.nr_mmaps) + if (!evlist__core(session->evlist)->nr_mmaps) return -EINVAL; =20 cpu_map =3D arm_spe_find_cpus(session->evlist); @@ -363,7 +363,7 @@ static int arm_spe_setup_tracking_event(struct evlist *= evlist, { int err; struct evsel *tracking_evsel; - struct perf_cpu_map *cpus =3D evlist->core.user_requested_cpus; + struct perf_cpu_map *cpus =3D evlist__core(evlist)->user_requested_cpus; =20 /* Add dummy event to keep tracking */ err =3D parse_event(evlist, "dummy:u"); @@ -396,7 +396,7 @@ static int arm_spe_recording_options(struct auxtrace_re= cord *itr, struct arm_spe_recording *sper =3D container_of(itr, struct arm_spe_recording, itr); struct evsel *evsel, *tmp; - struct perf_cpu_map *cpus =3D evlist->core.user_requested_cpus; + struct perf_cpu_map *cpus =3D evlist__core(evlist)->user_requested_cpus; bool discard =3D false; int err; u64 discard_bit; diff --git a/tools/perf/arch/arm64/util/hisi-ptt.c b/tools/perf/arch/arm64/= util/hisi-ptt.c index fe457fd58c9e..52257715d2b7 100644 --- a/tools/perf/arch/arm64/util/hisi-ptt.c +++ b/tools/perf/arch/arm64/util/hisi-ptt.c @@ -53,7 +53,7 @@ static int hisi_ptt_info_fill(struct auxtrace_record *itr, if (priv_size !=3D HISI_PTT_AUXTRACE_PRIV_SIZE) return -EINVAL; =20 - if (!session->evlist->core.nr_mmaps) + if (!evlist__core(session->evlist)->nr_mmaps) return -EINVAL; =20 auxtrace_info->type =3D PERF_AUXTRACE_HISI_PTT; diff --git a/tools/perf/arch/x86/tests/hybrid.c b/tools/perf/arch/x86/tests= /hybrid.c index dfb0ffc0d030..0477e17b8e53 100644 --- a/tools/perf/arch/x86/tests/hybrid.c +++ b/tools/perf/arch/x86/tests/hybrid.c @@ -26,7 +26,7 @@ static int test__hybrid_hw_event_with_pmu(struct evlist *= evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_VAL("wrong number of entries", 1 =3D=3D evlist->core.nr_entri= es); + TEST_ASSERT_VAL("wrong number of entries", 1 =3D=3D evlist__nr_entries(ev= list)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE =3D=3D evsel->core.attr.= type); TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RA= W)); TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCL= ES)); @@ -38,7 +38,7 @@ static int test__hybrid_hw_group_event(struct evlist *evl= ist) struct evsel *evsel, *leader; =20 evsel =3D leader =3D evlist__first(evlist); - TEST_ASSERT_VAL("wrong number of entries", 2 =3D=3D evlist->core.nr_entri= es); + TEST_ASSERT_VAL("wrong number of entries", 2 =3D=3D evlist__nr_entries(ev= list)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE =3D=3D evsel->core.attr.= type); TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RA= W)); TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCL= ES)); @@ -57,7 +57,7 @@ static int test__hybrid_sw_hw_group_event(struct evlist *= evlist) struct evsel *evsel, *leader; =20 evsel =3D leader =3D evlist__first(evlist); - TEST_ASSERT_VAL("wrong number of entries", 2 =3D=3D evlist->core.nr_entri= es); + TEST_ASSERT_VAL("wrong number of entries", 2 =3D=3D evlist__nr_entries(ev= list)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE =3D=3D evsel->core.attr.= type); TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); =20 @@ -74,7 +74,7 @@ static int test__hybrid_hw_sw_group_event(struct evlist *= evlist) struct evsel *evsel, *leader; =20 evsel =3D leader =3D evlist__first(evlist); - TEST_ASSERT_VAL("wrong number of entries", 2 =3D=3D evlist->core.nr_entri= es); + TEST_ASSERT_VAL("wrong number of entries", 2 =3D=3D evlist__nr_entries(ev= list)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE =3D=3D evsel->core.attr.= type); TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RA= W)); TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCL= ES)); @@ -91,7 +91,7 @@ static int test__hybrid_group_modifier1(struct evlist *ev= list) struct evsel *evsel, *leader; =20 evsel =3D leader =3D evlist__first(evlist); - TEST_ASSERT_VAL("wrong number of entries", 2 =3D=3D evlist->core.nr_entri= es); + TEST_ASSERT_VAL("wrong number of entries", 2 =3D=3D evlist__nr_entries(ev= list)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE =3D=3D evsel->core.attr.= type); TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RA= W)); TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCL= ES)); @@ -113,7 +113,7 @@ static int test__hybrid_raw1(struct evlist *evlist) { struct perf_evsel *evsel; =20 - perf_evlist__for_each_evsel(&evlist->core, evsel) { + perf_evlist__for_each_evsel(evlist__core(evlist), evsel) { struct perf_pmu *pmu =3D perf_pmus__find_by_type(evsel->attr.type); =20 TEST_ASSERT_VAL("missing pmu", pmu); @@ -127,7 +127,7 @@ static int test__hybrid_raw2(struct evlist *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_VAL("wrong number of entries", 1 =3D=3D evlist->core.nr_entri= es); + TEST_ASSERT_VAL("wrong number of entries", 1 =3D=3D evlist__nr_entries(ev= list)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW =3D=3D evsel->core.attr.type); TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); return TEST_OK; @@ -137,7 +137,7 @@ static int test__hybrid_cache_event(struct evlist *evli= st) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_VAL("wrong number of entries", 1 =3D=3D evlist->core.nr_entri= es); + TEST_ASSERT_VAL("wrong number of entries", 1 =3D=3D evlist__nr_entries(ev= list)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE =3D=3D evsel->core.attr.= type); TEST_ASSERT_VAL("wrong config", 0x2 =3D=3D (evsel->core.attr.config & 0xf= fffffff)); return TEST_OK; @@ -148,7 +148,7 @@ static int test__checkevent_pmu(struct evlist *evlist) =20 struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_VAL("wrong number of entries", 1 =3D=3D evlist->core.nr_entri= es); + TEST_ASSERT_VAL("wrong number of entries", 1 =3D=3D evlist__nr_entries(ev= list)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW =3D=3D evsel->core.attr.type); TEST_ASSERT_VAL("wrong config", 10 =3D=3D evsel->core.attr.config); TEST_ASSERT_VAL("wrong config1", 1 =3D=3D evsel->core.attr.config1); @@ -168,7 +168,7 @@ static int test__hybrid_hw_group_event_2(struct evlist = *evlist) struct evsel *evsel, *leader; =20 evsel =3D leader =3D evlist__first(evlist); - TEST_ASSERT_VAL("wrong number of entries", 2 =3D=3D evlist->core.nr_entri= es); + TEST_ASSERT_VAL("wrong number of entries", 2 =3D=3D evlist__nr_entries(ev= list)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE =3D=3D evsel->core.attr.= type); TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RA= W)); TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCL= ES)); diff --git a/tools/perf/arch/x86/util/auxtrace.c b/tools/perf/arch/x86/util= /auxtrace.c index ecbf61a7eb3a..84fce0b51ccf 100644 --- a/tools/perf/arch/x86/util/auxtrace.c +++ b/tools/perf/arch/x86/util/auxtrace.c @@ -55,7 +55,7 @@ struct auxtrace_record *auxtrace_record__init(struct evli= st *evlist, int *err) { char buffer[64]; - struct perf_cpu cpu =3D perf_cpu_map__min(evlist->core.all_cpus); + struct perf_cpu cpu =3D perf_cpu_map__min(evlist__core(evlist)->all_cpus); int ret; =20 *err =3D 0; diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/uti= l/intel-bts.c index 100a23d27998..d44d568a6d21 100644 --- a/tools/perf/arch/x86/util/intel-bts.c +++ b/tools/perf/arch/x86/util/intel-bts.c @@ -79,10 +79,10 @@ static int intel_bts_info_fill(struct auxtrace_record *= itr, if (priv_size !=3D INTEL_BTS_AUXTRACE_PRIV_SIZE) return -EINVAL; =20 - if (!session->evlist->core.nr_mmaps) + if (!evlist__core(session->evlist)->nr_mmaps) return -EINVAL; =20 - pc =3D session->evlist->mmap[0].core.base; + pc =3D evlist__mmap(session->evlist)[0].core.base; if (pc) { err =3D perf_read_tsc_conversion(pc, &tc); if (err) { @@ -114,7 +114,7 @@ static int intel_bts_recording_options(struct auxtrace_= record *itr, container_of(itr, struct intel_bts_recording, itr); struct perf_pmu *intel_bts_pmu =3D btsr->intel_bts_pmu; struct evsel *evsel, *intel_bts_evsel =3D NULL; - const struct perf_cpu_map *cpus =3D evlist->core.user_requested_cpus; + const struct perf_cpu_map *cpus =3D evlist__core(evlist)->user_requested_= cpus; bool privileged =3D perf_event_paranoid_check(-1); =20 if (opts->auxtrace_sample_mode) { diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util= /intel-pt.c index 0307ff15d9fc..a533114c0048 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -360,10 +360,10 @@ static int intel_pt_info_fill(struct auxtrace_record = *itr, filter =3D intel_pt_find_filter(session->evlist, ptr->intel_pt_pmu); filter_str_len =3D filter ? strlen(filter) : 0; =20 - if (!session->evlist->core.nr_mmaps) + if (!evlist__core(session->evlist)->nr_mmaps) return -EINVAL; =20 - pc =3D session->evlist->mmap[0].core.base; + pc =3D evlist__mmap(session->evlist)[0].core.base; if (pc) { err =3D perf_read_tsc_conversion(pc, &tc); if (err) { @@ -376,7 +376,8 @@ static int intel_pt_info_fill(struct auxtrace_record *i= tr, ui__warning("Intel Processor Trace: TSC not available\n"); } =20 - per_cpu_mmaps =3D !perf_cpu_map__is_any_cpu_or_is_empty(session->evlist->= core.user_requested_cpus); + per_cpu_mmaps =3D !perf_cpu_map__is_any_cpu_or_is_empty( + evlist__core(session->evlist)->user_requested_cpus); =20 auxtrace_info->type =3D PERF_AUXTRACE_INTEL_PT; auxtrace_info->priv[INTEL_PT_PMU_TYPE] =3D intel_pt_pmu->type; @@ -621,7 +622,7 @@ static int intel_pt_recording_options(struct auxtrace_r= ecord *itr, struct perf_pmu *intel_pt_pmu =3D ptr->intel_pt_pmu; bool have_timing_info, need_immediate =3D false; struct evsel *evsel, *intel_pt_evsel =3D NULL; - const struct perf_cpu_map *cpus =3D evlist->core.user_requested_cpus; + const struct perf_cpu_map *cpus =3D evlist__core(evlist)->user_requested_= cpus; bool privileged =3D perf_event_paranoid_check(-1); u64 tsc_bit; int err; diff --git a/tools/perf/arch/x86/util/iostat.c b/tools/perf/arch/x86/util/i= ostat.c index e0417552b0cb..a0baa6cdefd8 100644 --- a/tools/perf/arch/x86/util/iostat.c +++ b/tools/perf/arch/x86/util/iostat.c @@ -334,7 +334,7 @@ static int iostat_event_group(struct evlist *evl, =20 int iostat_prepare(struct evlist *evlist, struct perf_stat_config *config) { - if (evlist->core.nr_entries > 0) { + if (evlist__nr_entries(evlist) > 0) { pr_warning("The -e and -M options are not supported." "All chosen events/metrics will be dropped\n"); evlist__put(evlist); @@ -400,7 +400,7 @@ void iostat_prefix(struct evlist *evlist, struct perf_stat_config *config, char *prefix, struct timespec *ts) { - struct iio_root_port *rp =3D evlist->selected->priv; + struct iio_root_port *rp =3D evlist__selected(evlist)->priv; =20 if (rp) { /* @@ -463,7 +463,7 @@ void iostat_print_counters(struct evlist *evlist, iostat_prefix(evlist, config, prefix, ts); fprintf(config->output, "%s", prefix); evlist__for_each_entry(evlist, counter) { - perf_device =3D evlist->selected->priv; + perf_device =3D evlist__selected(evlist)->priv; if (perf_device && perf_device !=3D counter->priv) { evlist__set_selected(evlist, counter); iostat_prefix(evlist, config, prefix, ts); diff --git a/tools/perf/bench/evlist-open-close.c b/tools/perf/bench/evlist= -open-close.c index 304929d1f67f..748ebbe458f4 100644 --- a/tools/perf/bench/evlist-open-close.c +++ b/tools/perf/bench/evlist-open-close.c @@ -116,7 +116,7 @@ static int bench__do_evlist_open_close(struct evlist *e= vlist) return err; } =20 - err =3D evlist__mmap(evlist, opts.mmap_pages); + err =3D evlist__do_mmap(evlist, opts.mmap_pages); if (err < 0) { pr_err("evlist__mmap: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); return err; @@ -124,7 +124,7 @@ static int bench__do_evlist_open_close(struct evlist *e= vlist) =20 evlist__enable(evlist); evlist__disable(evlist); - evlist__munmap(evlist); + evlist__do_munmap(evlist); evlist__close(evlist); =20 return 0; @@ -145,10 +145,11 @@ static int bench_evlist_open_close__run(char *evstr, = const char *uid_str) =20 init_stats(&time_stats); =20 - printf(" Number of cpus:\t%d\n", perf_cpu_map__nr(evlist->core.user_requ= ested_cpus)); - printf(" Number of threads:\t%d\n", evlist->core.threads->nr); + printf(" Number of cpus:\t%d\n", + perf_cpu_map__nr(evlist__core(evlist)->user_requested_cpus)); + printf(" Number of threads:\t%d\n", evlist__core(evlist)->threads->nr); printf(" Number of events:\t%d (%d fds)\n", - evlist->core.nr_entries, evlist__count_evsel_fds(evlist)); + evlist__nr_entries(evlist), evlist__count_evsel_fds(evlist)); printf(" Number of iterations:\t%d\n", iterations); =20 evlist__put(evlist); diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 5e57b78548f4..3c14fbec7b3d 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -928,7 +928,7 @@ int cmd_annotate(int argc, const char **argv) */ if ((use_browser =3D=3D 1 || annotate.use_stdio2) && annotate.has_br_stac= k) { sort__mode =3D SORT_MODE__BRANCH; - if (annotate.session->evlist->nr_br_cntr > 0) + if (evlist__nr_br_cntr(annotate.session->evlist) > 0) annotate_opts.show_br_cntr =3D true; } =20 diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c index 676239148b87..9e4c5220d43c 100644 --- a/tools/perf/builtin-ftrace.c +++ b/tools/perf/builtin-ftrace.c @@ -377,9 +377,9 @@ static int set_tracing_pid(struct perf_ftrace *ftrace) if (target__has_cpu(&ftrace->target)) return 0; =20 - for (i =3D 0; i < perf_thread_map__nr(ftrace->evlist->core.threads); i++)= { + for (i =3D 0; i < perf_thread_map__nr(evlist__core(ftrace->evlist)->threa= ds); i++) { scnprintf(buf, sizeof(buf), "%d", - perf_thread_map__pid(ftrace->evlist->core.threads, i)); + perf_thread_map__pid(evlist__core(ftrace->evlist)->threads, i)); if (append_tracing_file("set_ftrace_pid", buf) < 0) return -1; } @@ -413,7 +413,7 @@ static int set_tracing_cpumask(struct perf_cpu_map *cpu= map) =20 static int set_tracing_cpu(struct perf_ftrace *ftrace) { - struct perf_cpu_map *cpumap =3D ftrace->evlist->core.user_requested_cpus; + struct perf_cpu_map *cpumap =3D evlist__core(ftrace->evlist)->user_reques= ted_cpus; =20 if (!target__has_cpu(&ftrace->target)) return 0; diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 88c0ef4f5ff1..8869268701d5 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -1427,7 +1427,7 @@ static int synthesize_id_index(struct perf_inject *in= ject, size_t new_cnt) struct perf_session *session =3D inject->session; struct evlist *evlist =3D session->evlist; struct machine *machine =3D &session->machines.host; - size_t from =3D evlist->core.nr_entries - new_cnt; + size_t from =3D evlist__nr_entries(evlist) - new_cnt; =20 return __perf_event__synthesize_id_index(&inject->tool, perf_event__repip= e, evlist, machine, from); @@ -1962,7 +1962,7 @@ static int host__finished_init(const struct perf_tool= *tool, struct perf_session if (ret) return ret; =20 - ret =3D synthesize_id_index(inject, gs->session->evlist->core.nr_entries); + ret =3D synthesize_id_index(inject, evlist__nr_entries(gs->session->evlis= t)); if (ret) { pr_err("Failed to synthesize id_index\n"); return ret; diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index d88855e3c7b4..d14e2a9126ee 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -1222,7 +1222,7 @@ static s64 perf_kvm__mmap_read_idx(struct perf_kvm_st= at *kvm, int idx, int err; =20 *mmap_time =3D ULLONG_MAX; - md =3D &evlist->mmap[idx]; + md =3D &evlist__mmap(evlist)[idx]; err =3D perf_mmap__read_init(&md->core); if (err < 0) return (err =3D=3D -EAGAIN) ? 0 : -1; @@ -1267,7 +1267,7 @@ static int perf_kvm__mmap_read(struct perf_kvm_stat *= kvm) s64 n, ntotal =3D 0; u64 flush_time =3D ULLONG_MAX, mmap_time; =20 - for (i =3D 0; i < kvm->evlist->core.nr_mmaps; i++) { + for (i =3D 0; i < evlist__core(kvm->evlist)->nr_mmaps; i++) { n =3D perf_kvm__mmap_read_idx(kvm, i, &mmap_time); if (n < 0) return -1; @@ -1450,7 +1450,7 @@ static int kvm_events_live_report(struct perf_kvm_sta= t *kvm) evlist__enable(kvm->evlist); =20 while (!done) { - struct fdarray *fda =3D &kvm->evlist->core.pollfd; + struct fdarray *fda =3D &evlist__core(kvm->evlist)->pollfd; int rc; =20 rc =3D perf_kvm__mmap_read(kvm); @@ -1532,7 +1532,7 @@ static int kvm_live_open_events(struct perf_kvm_stat = *kvm) goto out; } =20 - if (evlist__mmap(evlist, kvm->opts.mmap_pages) < 0) { + if (evlist__do_mmap(evlist, kvm->opts.mmap_pages) < 0) { ui__error("Failed to mmap the events: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); evlist__close(evlist); @@ -1932,7 +1932,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, perf_session__set_id_hdr_size(kvm->session); ordered_events__set_copy_on_queue(&kvm->session->ordered_events, true); machine__synthesize_threads(&kvm->session->machines.host, &kvm->opts.targ= et, - kvm->evlist->core.threads, true, false, 1); + evlist__core(kvm->evlist)->threads, true, false, 1); err =3D kvm_live_open_events(kvm); if (err) goto out; diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index 9d3a4c779a41..270644c7ec46 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -1776,7 +1776,7 @@ static int perf_kwork__check_config(struct perf_kwork= *kwork, } } =20 - list_for_each_entry(evsel, &session->evlist->core.entries, core.node) { + list_for_each_entry(evsel, &evlist__core(session->evlist)->entries, core.= node) { if (kwork->show_callchain && !evsel__has_callchain(evsel)) { pr_debug("Samples do not have callchains\n"); kwork->show_callchain =3D 0; @@ -1826,9 +1826,9 @@ static int perf_kwork__read_events(struct perf_kwork = *kwork) goto out_delete; } =20 - kwork->nr_events =3D session->evlist->stats.nr_events[0]; - kwork->nr_lost_events =3D session->evlist->stats.total_lost; - kwork->nr_lost_chunks =3D session->evlist->stats.nr_events[PERF_RECORD_LO= ST]; + kwork->nr_events =3D evlist__stats(session->evlist)->nr_events[0]; + kwork->nr_lost_events =3D evlist__stats(session->evlist)->total_lost; + kwork->nr_lost_chunks =3D evlist__stats(session->evlist)->nr_events[PERF_= RECORD_LOST]; =20 out_delete: perf_session__delete(session); diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index b4fffa936e01..b09d2b5f31e3 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -501,12 +501,12 @@ static void record__aio_mmap_read_sync(struct record = *rec) { int i; struct evlist *evlist =3D rec->evlist; - struct mmap *maps =3D evlist->mmap; + struct mmap *maps =3D evlist__mmap(evlist); =20 if (!record__aio_enabled(rec)) return; =20 - for (i =3D 0; i < evlist->core.nr_mmaps; i++) { + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) { struct mmap *map =3D &maps[i]; =20 if (map->core.base) @@ -810,8 +810,8 @@ static int record__auxtrace_read_snapshot_all(struct re= cord *rec) int i; int rc =3D 0; =20 - for (i =3D 0; i < rec->evlist->core.nr_mmaps; i++) { - struct mmap *map =3D &rec->evlist->mmap[i]; + for (i =3D 0; i < evlist__core(rec->evlist)->nr_mmaps; i++) { + struct mmap *map =3D &evlist__mmap(rec->evlist)[i]; =20 if (!map->auxtrace_mmap.base) continue; @@ -1053,15 +1053,15 @@ static void record__thread_data_close_pipes(struct = record_thread *thread_data) =20 static bool evlist__per_thread(struct evlist *evlist) { - return cpu_map__is_dummy(evlist->core.user_requested_cpus); + return cpu_map__is_dummy(evlist__core(evlist)->user_requested_cpus); } =20 static int record__thread_data_init_maps(struct record_thread *thread_data= , struct evlist *evlist) { - int m, tm, nr_mmaps =3D evlist->core.nr_mmaps; - struct mmap *mmap =3D evlist->mmap; - struct mmap *overwrite_mmap =3D evlist->overwrite_mmap; - struct perf_cpu_map *cpus =3D evlist->core.all_cpus; + int m, tm, nr_mmaps =3D evlist__core(evlist)->nr_mmaps; + struct mmap *mmap =3D evlist__mmap(evlist); + struct mmap *overwrite_mmap =3D evlist__overwrite_mmap(evlist); + struct perf_cpu_map *cpus =3D evlist__core(evlist)->all_cpus; bool per_thread =3D evlist__per_thread(evlist); =20 if (per_thread) @@ -1116,16 +1116,17 @@ static int record__thread_data_init_pollfd(struct r= ecord_thread *thread_data, st overwrite_map =3D thread_data->overwrite_maps ? thread_data->overwrite_maps[tm] : NULL; =20 - for (f =3D 0; f < evlist->core.pollfd.nr; f++) { - void *ptr =3D evlist->core.pollfd.priv[f].ptr; + for (f =3D 0; f < evlist__core(evlist)->pollfd.nr; f++) { + void *ptr =3D evlist__core(evlist)->pollfd.priv[f].ptr; =20 if ((map && ptr =3D=3D map) || (overwrite_map && ptr =3D=3D overwrite_m= ap)) { pos =3D fdarray__dup_entry_from(&thread_data->pollfd, f, - &evlist->core.pollfd); + &evlist__core(evlist)->pollfd); if (pos < 0) return pos; pr_debug2("thread_data[%p]: pollfd[%d] <- event_fd=3D%d\n", - thread_data, pos, evlist->core.pollfd.entries[f].fd); + thread_data, pos, + evlist__core(evlist)->pollfd.entries[f].fd); } } } @@ -1169,7 +1170,7 @@ static int record__update_evlist_pollfd_from_thread(s= truct record *rec, struct evlist *evlist, struct record_thread *thread_data) { - struct pollfd *e_entries =3D evlist->core.pollfd.entries; + struct pollfd *e_entries =3D evlist__core(evlist)->pollfd.entries; struct pollfd *t_entries =3D thread_data->pollfd.entries; int err =3D 0; size_t i; @@ -1193,7 +1194,7 @@ static int record__dup_non_perf_events(struct record = *rec, struct evlist *evlist, struct record_thread *thread_data) { - struct fdarray *fda =3D &evlist->core.pollfd; + struct fdarray *fda =3D &evlist__core(evlist)->pollfd; int i, ret; =20 for (i =3D 0; i < fda->nr; i++) { @@ -1320,17 +1321,17 @@ static int record__mmap_evlist(struct record *rec, return ret; =20 if (record__threads_enabled(rec)) { - ret =3D perf_data__create_dir(&rec->data, evlist->core.nr_mmaps); + ret =3D perf_data__create_dir(&rec->data, evlist__core(evlist)->nr_mmaps= ); if (ret) { errno =3D -ret; pr_err("Failed to create data directory: %m\n"); return ret; } - for (i =3D 0; i < evlist->core.nr_mmaps; i++) { - if (evlist->mmap) - evlist->mmap[i].file =3D &rec->data.dir.files[i]; - if (evlist->overwrite_mmap) - evlist->overwrite_mmap[i].file =3D &rec->data.dir.files[i]; + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) { + if (evlist__mmap(evlist)) + evlist__mmap(evlist)[i].file =3D &rec->data.dir.files[i]; + if (evlist__overwrite_mmap(evlist)) + evlist__overwrite_mmap(evlist)[i].file =3D &rec->data.dir.files[i]; } } =20 @@ -1479,11 +1480,11 @@ static int record__open(struct record *rec) =20 static void set_timestamp_boundary(struct record *rec, u64 sample_time) { - if (rec->evlist->first_sample_time =3D=3D 0) - rec->evlist->first_sample_time =3D sample_time; + if (evlist__first_sample_time(rec->evlist) =3D=3D 0) + evlist__set_first_sample_time(rec->evlist, sample_time); =20 if (sample_time) - rec->evlist->last_sample_time =3D sample_time; + evlist__set_last_sample_time(rec->evlist, sample_time); } =20 static int process_sample_event(const struct perf_tool *tool, @@ -1652,7 +1653,7 @@ static int record__mmap_read_evlist(struct record *re= c, struct evlist *evlist, if (!maps) return 0; =20 - if (overwrite && evlist->bkw_mmap_state !=3D BKW_MMAP_DATA_PENDING) + if (overwrite && evlist__bkw_mmap_state(evlist) !=3D BKW_MMAP_DATA_PENDIN= G) return 0; =20 if (record__aio_enabled(rec)) @@ -1807,7 +1808,7 @@ static void record__init_features(struct record *rec) if (rec->no_buildid) perf_header__clear_feat(&session->header, HEADER_BUILD_ID); =20 - if (!have_tracepoints(&rec->evlist->core.entries)) + if (!have_tracepoints(&evlist__core(rec->evlist)->entries)) perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); =20 if (!rec->opts.branch_stack) @@ -1873,7 +1874,7 @@ static int record__synthesize_workload(struct record = *rec, bool tail) if (rec->opts.tail_synthesize !=3D tail) return 0; =20 - thread_map =3D thread_map__new_by_tid(rec->evlist->workload.pid); + thread_map =3D thread_map__new_by_tid(evlist__workload_pid(rec->evlist)); if (thread_map =3D=3D NULL) return -1; =20 @@ -2066,10 +2067,10 @@ static void alarm_sig_handler(int sig); static const struct perf_event_mmap_page *evlist__pick_pc(struct evlist *e= vlist) { if (evlist) { - if (evlist->mmap && evlist->mmap[0].core.base) - return evlist->mmap[0].core.base; - if (evlist->overwrite_mmap && evlist->overwrite_mmap[0].core.base) - return evlist->overwrite_mmap[0].core.base; + if (evlist__mmap(evlist) && evlist__mmap(evlist)[0].core.base) + return evlist__mmap(evlist)[0].core.base; + if (evlist__overwrite_mmap(evlist) && evlist__overwrite_mmap(evlist)[0].= core.base) + return evlist__overwrite_mmap(evlist)[0].core.base; } return NULL; } @@ -2149,7 +2150,7 @@ static int record__synthesize(struct record *rec, boo= l tail) if (err) goto out; =20 - err =3D perf_event__synthesize_thread_map2(&rec->tool, rec->evlist->core.= threads, + err =3D perf_event__synthesize_thread_map2(&rec->tool, evlist__core(rec->= evlist)->threads, process_synthesized_event, NULL); if (err < 0) { @@ -2157,7 +2158,7 @@ static int record__synthesize(struct record *rec, boo= l tail) return err; } =20 - err =3D perf_event__synthesize_cpu_map(&rec->tool, rec->evlist->core.all_= cpus, + err =3D perf_event__synthesize_cpu_map(&rec->tool, evlist__core(rec->evli= st)->all_cpus, process_synthesized_event, NULL); if (err < 0) { pr_err("Couldn't synthesize cpu map.\n"); @@ -2190,7 +2191,7 @@ static int record__synthesize(struct record *rec, boo= l tail) bool needs_mmap =3D rec->opts.synth & PERF_SYNTH_MMAP; =20 err =3D __machine__synthesize_threads(machine, tool, &opts->target, - rec->evlist->core.threads, + evlist__core(rec->evlist)->threads, f, needs_mmap, opts->record_data_mmap, rec->opts.nr_threads_synthesize); } @@ -2543,7 +2544,7 @@ static int __cmd_record(struct record *rec, int argc,= const char **argv) * because we synthesize event name through the pipe * and need the id for that. */ - if (data->is_pipe && rec->evlist->core.nr_entries =3D=3D 1) + if (data->is_pipe && evlist__nr_entries(rec->evlist) =3D=3D 1) rec->opts.sample_id =3D true; =20 if (rec->timestamp_filename && perf_data__is_pipe(data)) { @@ -2567,7 +2568,7 @@ static int __cmd_record(struct record *rec, int argc,= const char **argv) } /* Debug message used by test scripts */ pr_debug3("perf record done opening and mmapping events\n"); - env->comp_mmap_len =3D session->evlist->core.mmap_len; + env->comp_mmap_len =3D evlist__core(session->evlist)->mmap_len; =20 if (rec->opts.kcore) { err =3D record__kcore_copy(&session->machines.host, data); @@ -2668,7 +2669,7 @@ static int __cmd_record(struct record *rec, int argc,= const char **argv) * Synthesize COMM event to prevent it. */ tgid =3D perf_event__synthesize_comm(tool, event, - rec->evlist->workload.pid, + evlist__workload_pid(rec->evlist), process_synthesized_event, machine); free(event); @@ -2688,7 +2689,7 @@ static int __cmd_record(struct record *rec, int argc,= const char **argv) * Synthesize NAMESPACES event for the command specified. */ perf_event__synthesize_namespaces(tool, event, - rec->evlist->workload.pid, + evlist__workload_pid(rec->evlist), tgid, process_synthesized_event, machine); free(event); @@ -2705,7 +2706,7 @@ static int __cmd_record(struct record *rec, int argc,= const char **argv) } } =20 - err =3D event_enable_timer__start(rec->evlist->eet); + err =3D event_enable_timer__start(evlist__event_enable_timer(rec->evlist)= ); if (err) goto out_child; =20 @@ -2767,7 +2768,7 @@ static int __cmd_record(struct record *rec, int argc,= const char **argv) * record__mmap_read_all() didn't collect data from * overwritable ring buffer. Read again. */ - if (rec->evlist->bkw_mmap_state =3D=3D BKW_MMAP_RUNNING) + if (evlist__bkw_mmap_state(rec->evlist) =3D=3D BKW_MMAP_RUNNING) continue; trigger_ready(&switch_output_trigger); =20 @@ -2836,7 +2837,7 @@ static int __cmd_record(struct record *rec, int argc,= const char **argv) } } =20 - err =3D event_enable_timer__process(rec->evlist->eet); + err =3D event_enable_timer__process(evlist__event_enable_timer(rec->evli= st)); if (err < 0) goto out_child; if (err) { @@ -2904,7 +2905,7 @@ static int __cmd_record(struct record *rec, int argc,= const char **argv) int exit_status; =20 if (!child_finished) - kill(rec->evlist->workload.pid, SIGTERM); + kill(evlist__workload_pid(rec->evlist), SIGTERM); =20 wait(&exit_status); =20 @@ -4030,7 +4031,7 @@ static int record__init_thread_default_masks(struct r= ecord *rec, struct perf_cpu static int record__init_thread_masks(struct record *rec) { int ret =3D 0; - struct perf_cpu_map *cpus =3D rec->evlist->core.all_cpus; + struct perf_cpu_map *cpus =3D evlist__core(rec->evlist)->all_cpus; =20 if (!record__threads_enabled(rec)) return record__init_thread_default_masks(rec, cpus); @@ -4281,14 +4282,14 @@ int cmd_record(int argc, const char **argv) if (record.opts.overwrite) record.opts.tail_synthesize =3D true; =20 - if (rec->evlist->core.nr_entries =3D=3D 0) { + if (evlist__nr_entries(rec->evlist) =3D=3D 0) { struct evlist *def_evlist =3D evlist__new_default(&rec->opts.target, callchain_param.enabled); =20 if (!def_evlist) goto out; =20 - evlist__splice_list_tail(rec->evlist, &def_evlist->core.entries); + evlist__splice_list_tail(rec->evlist, &evlist__core(def_evlist)->entries= ); evlist__put(def_evlist); } =20 diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 95c0bdba6b11..38b66763b99a 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -561,7 +561,7 @@ static int evlist__tty_browse_hists(struct evlist *evli= st, struct report *rep, c =20 if (!quiet) { fprintf(stdout, "#\n# Total Lost Samples: %" PRIu64 "\n#\n", - evlist->stats.total_lost_samples); + evlist__stats(evlist)->total_lost_samples); } =20 evlist__for_each_entry(evlist, pos) { @@ -1155,7 +1155,7 @@ static int __cmd_report(struct report *rep) PERF_HPP_REPORT__BLOCK_AVG_CYCLES, }; =20 - if (session->evlist->nr_br_cntr > 0) + if (evlist__nr_br_cntr(session->evlist) > 0) block_hpps[nr_hpps++] =3D PERF_HPP_REPORT__BLOCK_BRANCH_COUNTER; =20 block_hpps[nr_hpps++] =3D PERF_HPP_REPORT__BLOCK_RANGE; @@ -1290,7 +1290,7 @@ static int process_attr(const struct perf_tool *tool = __maybe_unused, * on events sample_type. */ sample_type =3D evlist__combined_sample_type(*pevlist); - session =3D (*pevlist)->session; + session =3D evlist__session(*pevlist); callchain_param_setup(sample_type, perf_session__e_machine(session, /*e_f= lags=3D*/NULL)); return 0; } diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index d683642ab4e0..d3fa9c70790f 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1951,9 +1951,9 @@ static int perf_sched__read_events(struct perf_sched = *sched) goto out_delete; } =20 - sched->nr_events =3D session->evlist->stats.nr_events[0]; - sched->nr_lost_events =3D session->evlist->stats.total_lost; - sched->nr_lost_chunks =3D session->evlist->stats.nr_events[PERF_RECORD_L= OST]; + sched->nr_events =3D evlist__stats(session->evlist)->nr_events[0]; + sched->nr_lost_events =3D evlist__stats(session->evlist)->total_lost; + sched->nr_lost_chunks =3D evlist__stats(session->evlist)->nr_events[PERF= _RECORD_LOST]; } =20 rc =3D 0; @@ -3211,7 +3211,7 @@ static int timehist_check_attr(struct perf_sched *sch= ed, struct evsel *evsel; struct evsel_runtime *er; =20 - list_for_each_entry(evsel, &evlist->core.entries, core.node) { + list_for_each_entry(evsel, &evlist__core(evlist)->entries, core.node) { er =3D evsel__get_runtime(evsel); if (er =3D=3D NULL) { pr_err("Failed to allocate memory for evsel runtime data\n"); @@ -3382,9 +3382,9 @@ static int perf_sched__timehist(struct perf_sched *sc= hed) goto out; } =20 - sched->nr_events =3D evlist->stats.nr_events[0]; - sched->nr_lost_events =3D evlist->stats.total_lost; - sched->nr_lost_chunks =3D evlist->stats.nr_events[PERF_RECORD_LOST]; + sched->nr_events =3D evlist__stats(evlist)->nr_events[0]; + sched->nr_lost_events =3D evlist__stats(evlist)->total_lost; + sched->nr_lost_chunks =3D evlist__stats(evlist)->nr_events[PERF_RECORD_LO= ST]; =20 if (sched->summary) timehist_print_summary(sched, session); @@ -3887,7 +3887,7 @@ static int perf_sched__schedstat_record(struct perf_s= ched *sched, if (err < 0) goto out; =20 - user_requested_cpus =3D evlist->core.user_requested_cpus; + user_requested_cpus =3D evlist__core(evlist)->user_requested_cpus; =20 err =3D perf_event__synthesize_schedstat(&(sched->tool), process_synthesized_schedstat_event, @@ -4509,7 +4509,7 @@ static int perf_sched__schedstat_report(struct perf_s= ched *sched) if (err < 0) goto out; =20 - user_requested_cpus =3D session->evlist->core.user_requested_cpus; + user_requested_cpus =3D evlist__core(session->evlist)->user_requested_cpu= s; =20 err =3D perf_session__process_events(session); =20 @@ -4675,7 +4675,7 @@ static int perf_sched__schedstat_live(struct perf_sch= ed *sched, if (err < 0) goto out; =20 - user_requested_cpus =3D evlist->core.user_requested_cpus; + user_requested_cpus =3D evlist__core(evlist)->user_requested_cpus; =20 err =3D perf_event__synthesize_schedstat(&(sched->tool), process_synthesized_event_live, diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 0ead134940d5..3e3692088154 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -2224,9 +2224,10 @@ static int script_find_metrics(const struct pmu_metr= ic *pm, evlist__for_each_entry(metric_evlist, metric_evsel) { struct evsel *script_evsel =3D map_metric_evsel_to_script_evsel(script_evlist, metric_evsel); - struct metric_event *metric_me =3D metricgroup__lookup(&metric_evlist->m= etric_events, - metric_evsel, - /*create=3D*/false); + struct metric_event *metric_me =3D + metricgroup__lookup(evlist__metric_events(metric_evlist), + metric_evsel, + /*create=3D*/false); =20 if (script_evsel->metric_id =3D=3D NULL) { script_evsel->metric_id =3D metric_evsel->metric_id; @@ -2246,7 +2247,7 @@ static int script_find_metrics(const struct pmu_metri= c *pm, if (metric_me) { struct metric_expr *expr; struct metric_event *script_me =3D - metricgroup__lookup(&script_evlist->metric_events, + metricgroup__lookup(evlist__metric_events(script_evlist), script_evsel, /*create=3D*/true); =20 @@ -2316,7 +2317,7 @@ static void perf_sample__fprint_metric(struct thread = *thread, assert(stat_config.aggr_mode =3D=3D AGGR_GLOBAL); stat_config.aggr_get_id =3D script_aggr_cpu_id_get; stat_config.aggr_map =3D - cpu_aggr_map__new(evsel->evlist->core.user_requested_cpus, + cpu_aggr_map__new(evlist__core(evsel->evlist)->user_requested_cpus, aggr_cpu_id__global, /*data=3D*/NULL, /*needs_sort=3D*/false); } @@ -3898,7 +3899,7 @@ static int set_maps(struct perf_script *script) if (WARN_ONCE(script->allocated, "stats double allocation\n")) return -EINVAL; =20 - perf_evlist__set_maps(&evlist->core, script->cpus, script->threads); + perf_evlist__set_maps(evlist__core(evlist), script->cpus, script->threads= ); =20 if (evlist__alloc_stats(&stat_config, evlist, /*alloc_raw=3D*/true)) return -ENOMEM; diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index bfa3512e1686..fe06d057edf0 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -321,7 +321,7 @@ static int read_single_counter(struct evsel *counter, i= nt cpu_map_idx, int threa */ static int read_counter_cpu(struct evsel *counter, int cpu_map_idx) { - int nthreads =3D perf_thread_map__nr(evsel_list->core.threads); + int nthreads =3D perf_thread_map__nr(evlist__core(evsel_list)->threads); int thread; =20 if (!counter->supported) @@ -628,11 +628,12 @@ static int dispatch_events(bool forks, int timeout, i= nt interval, int *times) time_to_sleep =3D sleep_time; =20 while (!done) { - if (forks) + if (forks) { child_exited =3D waitpid(child_pid, &status, WNOHANG); - else - child_exited =3D !is_target_alive(&target, evsel_list->core.threads) ? = 1 : 0; - + } else { + child_exited =3D !is_target_alive(&target, + evlist__core(evsel_list)->threads) ? 1 : 0; + } if (child_exited) break; =20 @@ -681,14 +682,15 @@ static enum counter_recovery stat_handle_error(struct= evsel *counter, int err) return COUNTER_RETRY; } if (target__has_per_thread(&target) && err !=3D EOPNOTSUPP && - evsel_list->core.threads && evsel_list->core.threads->err_thread !=3D= -1) { + evlist__core(evsel_list)->threads && + evlist__core(evsel_list)->threads->err_thread !=3D -1) { /* * For global --per-thread case, skip current * error thread. */ - if (!thread_map__remove(evsel_list->core.threads, - evsel_list->core.threads->err_thread)) { - evsel_list->core.threads->err_thread =3D -1; + if (!thread_map__remove(evlist__core(evsel_list)->threads, + evlist__core(evsel_list)->threads->err_thread)) { + evlist__core(evsel_list)->threads->err_thread =3D -1; counter->supported =3D true; return COUNTER_RETRY; } @@ -787,11 +789,12 @@ static int __run_perf_stat(int argc, const char **arg= v, int run_idx) bool second_pass =3D false, has_supported_counters; =20 if (forks) { - if (evlist__prepare_workload(evsel_list, &target, argv, is_pipe, workloa= d_exec_failed_signal) < 0) { + if (evlist__prepare_workload(evsel_list, &target, argv, is_pipe, + workload_exec_failed_signal) < 0) { perror("failed to prepare workload"); return -1; } - child_pid =3D evsel_list->workload.pid; + child_pid =3D evlist__workload_pid(evsel_list); } =20 evlist__for_each_entry(evsel_list, counter) { @@ -1199,7 +1202,7 @@ static int parse_cputype(const struct option *opt, const struct perf_pmu *pmu; struct evlist *evlist =3D *(struct evlist **)opt->value; =20 - if (!list_empty(&evlist->core.entries)) { + if (!list_empty(&evlist__core(evlist)->entries)) { fprintf(stderr, "Must define cputype before events/metrics\n"); return -1; } @@ -1220,7 +1223,7 @@ static int parse_pmu_filter(const struct option *opt, { struct evlist *evlist =3D *(struct evlist **)opt->value; =20 - if (!list_empty(&evlist->core.entries)) { + if (!list_empty(&evlist__core(evlist)->entries)) { fprintf(stderr, "Must define pmu-filter before events/metrics\n"); return -1; } @@ -1586,8 +1589,9 @@ static int perf_stat_init_aggr_mode(void) =20 if (get_id) { bool needs_sort =3D stat_config.aggr_mode !=3D AGGR_NONE; - stat_config.aggr_map =3D cpu_aggr_map__new(evsel_list->core.user_request= ed_cpus, - get_id, /*data=3D*/NULL, needs_sort); + stat_config.aggr_map =3D cpu_aggr_map__new( + evlist__core(evsel_list)->user_requested_cpus, + get_id, /*data=3D*/NULL, needs_sort); if (!stat_config.aggr_map) { pr_err("cannot build %s map\n", aggr_mode__string[stat_config.aggr_mode= ]); return -1; @@ -1596,7 +1600,7 @@ static int perf_stat_init_aggr_mode(void) } =20 if (stat_config.aggr_mode =3D=3D AGGR_THREAD) { - nr =3D perf_thread_map__nr(evsel_list->core.threads); + nr =3D perf_thread_map__nr(evlist__core(evsel_list)->threads); stat_config.aggr_map =3D cpu_aggr_map__empty_new(nr); if (stat_config.aggr_map =3D=3D NULL) return -ENOMEM; @@ -1615,7 +1619,7 @@ static int perf_stat_init_aggr_mode(void) * taking the highest cpu number to be the size of * the aggregation translate cpumap. */ - nr =3D perf_cpu_map__max(evsel_list->core.all_cpus).cpu + 1; + nr =3D perf_cpu_map__max(evlist__core(evsel_list)->all_cpus).cpu + 1; stat_config.cpus_aggr_map =3D cpu_aggr_map__empty_new(nr); return stat_config.cpus_aggr_map ? 0 : -ENOMEM; } @@ -1896,7 +1900,7 @@ static int perf_stat_init_aggr_mode_file(struct perf_= stat *st) bool needs_sort =3D stat_config.aggr_mode !=3D AGGR_NONE; =20 if (stat_config.aggr_mode =3D=3D AGGR_THREAD) { - int nr =3D perf_thread_map__nr(evsel_list->core.threads); + int nr =3D perf_thread_map__nr(evlist__core(evsel_list)->threads); =20 stat_config.aggr_map =3D cpu_aggr_map__empty_new(nr); if (stat_config.aggr_map =3D=3D NULL) @@ -1914,7 +1918,7 @@ static int perf_stat_init_aggr_mode_file(struct perf_= stat *st) if (!get_id) return 0; =20 - stat_config.aggr_map =3D cpu_aggr_map__new(evsel_list->core.user_requeste= d_cpus, + stat_config.aggr_map =3D cpu_aggr_map__new(evlist__core(evsel_list)->user= _requested_cpus, get_id, env, needs_sort); if (!stat_config.aggr_map) { pr_err("cannot build %s map\n", aggr_mode__string[stat_config.aggr_mode]= ); @@ -2082,7 +2086,7 @@ static int add_default_events(void) if (!stat_config.topdown_level) stat_config.topdown_level =3D 1; =20 - if (!evlist->core.nr_entries && !evsel_list->core.nr_entries) { + if (!evlist__nr_entries(evlist) && !evlist__nr_entries(evsel_list)) { /* * Add Default metrics. To minimize multiplexing, don't request * threshold computation, but it will be computed if the events @@ -2121,13 +2125,13 @@ static int add_default_events(void) evlist__for_each_entry(metric_evlist, evsel) evsel->default_metricgroup =3D true; =20 - evlist__splice_list_tail(evlist, &metric_evlist->core.entries); + evlist__splice_list_tail(evlist, &evlist__core(metric_evlist)->entries); metricgroup__copy_metric_events(evlist, /*cgrp=3D*/NULL, - &evlist->metric_events, - &metric_evlist->metric_events); + evlist__metric_events(evlist), + evlist__metric_events(metric_evlist)); evlist__put(metric_evlist); } - list_sort(/*priv=3D*/NULL, &evlist->core.entries, default_evlist_evsel_c= mp); + list_sort(/*priv=3D*/NULL, &evlist__core(evlist)->entries, default_evlis= t_evsel_cmp); =20 } out: @@ -2142,10 +2146,10 @@ static int add_default_events(void) } } parse_events_error__exit(&err); - evlist__splice_list_tail(evsel_list, &evlist->core.entries); + evlist__splice_list_tail(evsel_list, &evlist__core(evlist)->entries); metricgroup__copy_metric_events(evsel_list, /*cgrp=3D*/NULL, - &evsel_list->metric_events, - &evlist->metric_events); + evlist__metric_events(evsel_list), + evlist__metric_events(evlist)); evlist__put(evlist); return ret; } @@ -2266,7 +2270,7 @@ static int set_maps(struct perf_stat *st) if (WARN_ONCE(st->maps_allocated, "stats double allocation\n")) return -EINVAL; =20 - perf_evlist__set_maps(&evsel_list->core, st->cpus, st->threads); + perf_evlist__set_maps(evlist__core(evsel_list), st->cpus, st->threads); =20 if (evlist__alloc_stats(&stat_config, evsel_list, /*alloc_raw=3D*/true)) return -ENOMEM; @@ -2418,7 +2422,7 @@ static void setup_system_wide(int forks) } } =20 - if (evsel_list->core.nr_entries) + if (evlist__nr_entries(evsel_list)) target.system_wide =3D true; } } @@ -2645,7 +2649,7 @@ int cmd_stat(int argc, const char **argv) stat_config.csv_sep =3D DEFAULT_SEPARATOR; =20 if (affinity_set) - evsel_list->no_affinity =3D !affinity; + evlist__set_no_affinity(evsel_list, !affinity); =20 if (argc && strlen(argv[0]) > 2 && strstarts("record", argv[0])) { argc =3D __cmd_record(stat_options, &opt_mode, argc, argv); @@ -2876,9 +2880,10 @@ int cmd_stat(int argc, const char **argv) } #ifdef HAVE_BPF_SKEL if (target.use_bpf && nr_cgroups && - (evsel_list->core.nr_entries / nr_cgroups) > BPERF_CGROUP__MAX_EVENTS= ) { + (evlist__nr_entries(evsel_list) / nr_cgroups) > BPERF_CGROUP__MAX_EVE= NTS) { pr_warning("Disabling BPF counters due to more events (%d) than the max = (%d)\n", - evsel_list->core.nr_entries / nr_cgroups, BPERF_CGROUP__MAX_EVENTS); + evlist__nr_entries(evsel_list) / nr_cgroups, + BPERF_CGROUP__MAX_EVENTS); target.use_bpf =3D false; } #endif // HAVE_BPF_SKEL @@ -2916,7 +2921,7 @@ int cmd_stat(int argc, const char **argv) * so we could print it out on output. */ if (stat_config.aggr_mode =3D=3D AGGR_THREAD) { - thread_map__read_comms(evsel_list->core.threads); + thread_map__read_comms(evlist__core(evsel_list)->threads); } =20 if (stat_config.aggr_mode =3D=3D AGGR_NODE) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index c509cfef8285..fe8a73dd2000 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -141,7 +141,7 @@ static int perf_top__parse_source(struct perf_top *top,= struct hist_entry *he) notes =3D symbol__annotation(sym); annotation__lock(notes); =20 - if (!symbol__hists(sym, top->evlist->core.nr_entries)) { + if (!symbol__hists(sym, evlist__nr_entries(top->evlist))) { annotation__unlock(notes); pr_err("Not enough memory for annotating '%s' symbol!\n", sym->name); @@ -267,7 +267,7 @@ static void perf_top__show_details(struct perf_top *top) =20 more =3D hist_entry__annotate_printf(he, top->sym_evsel); =20 - if (top->evlist->enabled) { + if (evlist__enabled(top->evlist)) { if (top->zero) symbol__annotate_zero_histogram(symbol, top->sym_evsel); else @@ -293,7 +293,7 @@ static void perf_top__resort_hists(struct perf_top *t) */ hists__unlink(hists); =20 - if (evlist->enabled) { + if (evlist__enabled(evlist)) { if (t->zero) { hists__delete_entries(hists); } else { @@ -334,13 +334,13 @@ static void perf_top__print_sym_table(struct perf_top= *top) printf("%-*.*s\n", win_width, win_width, graph_dotted_line); =20 if (!top->record_opts.overwrite && - (top->evlist->stats.nr_lost_warned !=3D - top->evlist->stats.nr_events[PERF_RECORD_LOST])) { - top->evlist->stats.nr_lost_warned =3D - top->evlist->stats.nr_events[PERF_RECORD_LOST]; + (evlist__stats(top->evlist)->nr_lost_warned !=3D + evlist__stats(top->evlist)->nr_events[PERF_RECORD_LOST])) { + evlist__stats(top->evlist)->nr_lost_warned =3D + evlist__stats(top->evlist)->nr_events[PERF_RECORD_LOST]; color_fprintf(stdout, PERF_COLOR_RED, "WARNING: LOST %d chunks, Check IO/CPU overload", - top->evlist->stats.nr_lost_warned); + evlist__stats(top->evlist)->nr_lost_warned); ++printed; } =20 @@ -447,7 +447,7 @@ static void perf_top__print_mapped_keys(struct perf_top= *top) fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", = top->delay_secs); fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", = top->print_entries); =20 - if (top->evlist->core.nr_entries > 1) + if (evlist__nr_entries(top->evlist) > 1) fprintf(stdout, "\t[E] active event counter. \t(%s)\n",= evsel__name(top->sym_evsel)); =20 fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", = top->count_filter); @@ -482,7 +482,7 @@ static int perf_top__key_mapped(struct perf_top *top, i= nt c) case 'S': return 1; case 'E': - return top->evlist->core.nr_entries > 1 ? 1 : 0; + return evlist__nr_entries(top->evlist) > 1 ? 1 : 0; default: break; } @@ -528,7 +528,7 @@ static bool perf_top__handle_keypress(struct perf_top *= top, int c) } break; case 'E': - if (top->evlist->core.nr_entries > 1) { + if (evlist__nr_entries(top->evlist) > 1) { /* Select 0 as the default event: */ int counter =3D 0; =20 @@ -539,7 +539,7 @@ static bool perf_top__handle_keypress(struct perf_top *= top, int c) =20 prompt_integer(&counter, "Enter details event counter"); =20 - if (counter >=3D top->evlist->core.nr_entries) { + if (counter >=3D evlist__nr_entries(top->evlist)) { top->sym_evsel =3D evlist__first(top->evlist); fprintf(stderr, "Sorry, no such event, using %s.\n", evsel__name(top-= >sym_evsel)); sleep(1); @@ -598,8 +598,8 @@ static void perf_top__sort_new_samples(void *arg) { struct perf_top *t =3D arg; =20 - if (t->evlist->selected !=3D NULL) - t->sym_evsel =3D t->evlist->selected; + if (evlist__selected(t->evlist) !=3D NULL) + t->sym_evsel =3D evlist__selected(t->evlist); =20 perf_top__resort_hists(t); =20 @@ -768,7 +768,7 @@ static void perf_event__process_sample(const struct per= f_tool *tool, =20 if (!machine) { pr_err("%u unprocessable samples recorded.\r", - top->session->evlist->stats.nr_unprocessable_samples++); + evlist__stats(top->session->evlist)->nr_unprocessable_samples++); return; } =20 @@ -861,7 +861,7 @@ perf_top__process_lost(struct perf_top *top, union perf= _event *event, { top->lost +=3D event->lost.lost; top->lost_total +=3D event->lost.lost; - evsel->evlist->stats.total_lost +=3D event->lost.lost; + evlist__stats(evsel->evlist)->total_lost +=3D event->lost.lost; } =20 static void @@ -871,7 +871,7 @@ perf_top__process_lost_samples(struct perf_top *top, { top->lost +=3D event->lost_samples.lost; top->lost_total +=3D event->lost_samples.lost; - evsel->evlist->stats.total_lost_samples +=3D event->lost_samples.lost; + evlist__stats(evsel->evlist)->total_lost_samples +=3D event->lost_samples= .lost; } =20 static u64 last_timestamp; @@ -883,7 +883,7 @@ static void perf_top__mmap_read_idx(struct perf_top *to= p, int idx) struct mmap *md; union perf_event *event; =20 - md =3D opts->overwrite ? &evlist->overwrite_mmap[idx] : &evlist->mmap[idx= ]; + md =3D opts->overwrite ? &evlist__overwrite_mmap(evlist)[idx] : &evlist__= mmap(evlist)[idx]; if (perf_mmap__read_init(&md->core) < 0) return; =20 @@ -920,7 +920,7 @@ static void perf_top__mmap_read(struct perf_top *top) if (overwrite) evlist__toggle_bkw_mmap(evlist, BKW_MMAP_DATA_PENDING); =20 - for (i =3D 0; i < top->evlist->core.nr_mmaps; i++) + for (i =3D 0; i < evlist__core(top->evlist)->nr_mmaps; i++) perf_top__mmap_read_idx(top, i); =20 if (overwrite) { @@ -1065,7 +1065,7 @@ static int perf_top__start_counters(struct perf_top *= top) goto out_err; } =20 - if (evlist__mmap(evlist, opts->mmap_pages) < 0) { + if (evlist__do_mmap(evlist, opts->mmap_pages) < 0) { ui__error("Failed to mmap with %d (%s)\n", errno, str_error_r(errno, msg, sizeof(msg))); goto out_err; @@ -1218,10 +1218,10 @@ static int deliver_event(struct ordered_events *qe, } else if (event->header.type =3D=3D PERF_RECORD_LOST_SAMPLES) { perf_top__process_lost_samples(top, event, evsel); } else if (event->header.type < PERF_RECORD_MAX) { - events_stats__inc(&session->evlist->stats, event->header.type); + events_stats__inc(evlist__stats(session->evlist), event->header.type); machine__process_event(machine, event, &sample); } else - ++session->evlist->stats.nr_unknown_events; + ++evlist__stats(session->evlist)->nr_unknown_events; =20 ret =3D 0; next_event: @@ -1296,7 +1296,7 @@ static int __cmd_top(struct perf_top *top) pr_debug("Couldn't synthesize cgroup events.\n"); =20 machine__synthesize_threads(&top->session->machines.host, &opts->target, - top->evlist->core.threads, true, false, + evlist__core(top->evlist)->threads, true, false, top->nr_threads_synthesize); =20 perf_set_multithreaded(); @@ -1714,13 +1714,13 @@ int cmd_top(int argc, const char **argv) if (target__none(target)) target->system_wide =3D true; =20 - if (!top.evlist->core.nr_entries) { + if (!evlist__nr_entries(top.evlist)) { struct evlist *def_evlist =3D evlist__new_default(target, callchain_para= m.enabled); =20 if (!def_evlist) goto out_put_evlist; =20 - evlist__splice_list_tail(top.evlist, &def_evlist->core.entries); + evlist__splice_list_tail(top.evlist, &evlist__core(def_evlist)->entries); evlist__put(def_evlist); } =20 @@ -1797,7 +1797,7 @@ int cmd_top(int argc, const char **argv) top.session =3D NULL; goto out_put_evlist; } - top.evlist->session =3D top.session; + evlist__set_session(top.evlist, top.session); =20 if (setup_sorting(top.evlist, perf_session__env(top.session)) < 0) { if (sort_order) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 6ea935c13538..edd3eb408dd4 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -2008,7 +2008,7 @@ static int trace__symbols_init(struct trace *trace, i= nt argc, const char **argv, goto out; =20 err =3D __machine__synthesize_threads(trace->host, &trace->tool, &trace->= opts.target, - evlist->core.threads, trace__tool_process, + evlist__core(evlist)->threads, trace__tool_process, /*needs_mmap=3D*/callchain_param.enabled && !trace->summary_only, /*mmap_data=3D*/false, @@ -4165,7 +4165,7 @@ static int trace__set_filter_pids(struct trace *trace) err =3D augmented_syscalls__set_filter_pids(trace->filter_pids.nr, trace->filter_pids.entries); } - } else if (perf_thread_map__pid(trace->evlist->core.threads, 0) =3D=3D -1= ) { + } else if (perf_thread_map__pid(evlist__core(trace->evlist)->threads, 0) = =3D=3D -1) { err =3D trace__set_filter_loop_pids(trace); } =20 @@ -4479,7 +4479,7 @@ static int trace__run(struct trace *trace, int argc, = const char **argv) fprintf(trace->output, "Couldn't run the workload!\n"); goto out_put_evlist; } - workload_pid =3D evlist->workload.pid; + workload_pid =3D evlist__workload_pid(evlist); } =20 err =3D evlist__open(evlist); @@ -4531,7 +4531,7 @@ static int trace__run(struct trace *trace, int argc, = const char **argv) goto out_error_apply_filters; =20 if (!trace->summary_only || !trace->summary_bpf) { - err =3D evlist__mmap(evlist, trace->opts.mmap_pages); + err =3D evlist__do_mmap(evlist, trace->opts.mmap_pages); if (err < 0) goto out_error_mmap; } @@ -4550,8 +4550,8 @@ static int trace__run(struct trace *trace, int argc, = const char **argv) if (trace->summary_bpf) trace_start_bpf_summary(); =20 - trace->multiple_threads =3D perf_thread_map__pid(evlist->core.threads, 0)= =3D=3D -1 || - perf_thread_map__nr(evlist->core.threads) > 1 || + trace->multiple_threads =3D perf_thread_map__pid(evlist__core(evlist)->th= reads, 0) =3D=3D -1 || + perf_thread_map__nr(evlist__core(evlist)->threads) > 1 || evlist__first(evlist)->core.attr.inherit; =20 /* @@ -4568,11 +4568,11 @@ static int trace__run(struct trace *trace, int argc= , const char **argv) again: before =3D trace->nr_events; =20 - for (i =3D 0; i < evlist->core.nr_mmaps; i++) { + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) { union perf_event *event; struct mmap *md; =20 - md =3D &evlist->mmap[i]; + md =3D &evlist__mmap(evlist)[i]; if (perf_mmap__read_init(&md->core) < 0) continue; =20 @@ -5272,7 +5272,7 @@ static int trace__parse_cgroups(const struct option *= opt, const char *str, int u { struct trace *trace =3D opt->value; =20 - if (!list_empty(&trace->evlist->core.entries)) { + if (!list_empty(&evlist__core(trace->evlist)->entries)) { struct option o =3D { .value =3D &trace->evlist, }; @@ -5545,7 +5545,7 @@ int cmd_trace(int argc, const char **argv) * .perfconfig trace.add_events, and filter those out. */ if (!trace.trace_syscalls && !trace.trace_pgfaults && - trace.evlist->core.nr_entries =3D=3D 0 /* Was --events used? */) { + evlist__nr_entries(trace.evlist) =3D=3D 0 /* Was --events used? */) { trace.trace_syscalls =3D true; } /* @@ -5628,7 +5628,7 @@ int cmd_trace(int argc, const char **argv) symbol_conf.use_callchain =3D true; } =20 - if (trace.evlist->core.nr_entries > 0) { + if (evlist__nr_entries(trace.evlist) > 0) { bool use_btf =3D false; =20 evlist__set_default_evsel_handler(trace.evlist, trace__event_handler); diff --git a/tools/perf/tests/backward-ring-buffer.c b/tools/perf/tests/bac= kward-ring-buffer.c index 2b49b002d749..2735cc26d7ee 100644 --- a/tools/perf/tests/backward-ring-buffer.c +++ b/tools/perf/tests/backward-ring-buffer.c @@ -34,8 +34,8 @@ static int count_samples(struct evlist *evlist, int *samp= le_count, { int i; =20 - for (i =3D 0; i < evlist->core.nr_mmaps; i++) { - struct mmap *map =3D &evlist->overwrite_mmap[i]; + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) { + struct mmap *map =3D &evlist__overwrite_mmap(evlist)[i]; union perf_event *event; =20 perf_mmap__read_init(&map->core); @@ -65,7 +65,7 @@ static int do_test(struct evlist *evlist, int mmap_pages, int err; char sbuf[STRERR_BUFSIZE]; =20 - err =3D evlist__mmap(evlist, mmap_pages); + err =3D evlist__do_mmap(evlist, mmap_pages); if (err < 0) { pr_debug("evlist__mmap: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); @@ -77,7 +77,7 @@ static int do_test(struct evlist *evlist, int mmap_pages, evlist__disable(evlist); =20 err =3D count_samples(evlist, sample_count, comm_count); - evlist__munmap(evlist); + evlist__do_munmap(evlist); return err; } =20 diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-readin= g.c index fc65a17f67f7..28c068a35ada 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -589,8 +589,8 @@ static int process_events(struct machine *machine, stru= ct evlist *evlist, struct mmap *md; int i, ret; =20 - for (i =3D 0; i < evlist->core.nr_mmaps; i++) { - md =3D &evlist->mmap[i]; + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) { + md =3D &evlist__mmap(evlist)[i]; if (perf_mmap__read_init(&md->core) < 0) continue; =20 @@ -778,7 +778,7 @@ static int do_test_code_reading(bool try_kcore) goto out_put; } =20 - perf_evlist__set_maps(&evlist->core, cpus, threads); + perf_evlist__set_maps(evlist__core(evlist), cpus, threads); =20 str =3D events[evidx]; pr_debug("Parsing event '%s'\n", str); @@ -806,7 +806,7 @@ static int do_test_code_reading(bool try_kcore) pr_debug("perf_evlist__open() failed!\n%s\n", errbuf); } =20 - perf_evlist__set_maps(&evlist->core, NULL, NULL); + perf_evlist__set_maps(evlist__core(evlist), NULL, NULL); evlist__put(evlist); evlist =3D NULL; continue; @@ -817,7 +817,7 @@ static int do_test_code_reading(bool try_kcore) if (events[evidx] =3D=3D NULL) goto out_put; =20 - ret =3D evlist__mmap(evlist, UINT_MAX); + ret =3D evlist__do_mmap(evlist, UINT_MAX); if (ret < 0) { pr_debug("evlist__mmap failed\n"); goto out_put; diff --git a/tools/perf/tests/event-times.c b/tools/perf/tests/event-times.c index 94ab54ecd3f9..56dd37ca760e 100644 --- a/tools/perf/tests/event-times.c +++ b/tools/perf/tests/event-times.c @@ -50,7 +50,7 @@ static int attach__enable_on_exec(struct evlist *evlist) =20 static int detach__enable_on_exec(struct evlist *evlist) { - waitpid(evlist->workload.pid, NULL, 0); + waitpid(evlist__workload_pid(evlist), NULL, 0); return 0; } =20 diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_updat= e.c index 73141b122d2f..220cc0347747 100644 --- a/tools/perf/tests/event_update.c +++ b/tools/perf/tests/event_update.c @@ -92,7 +92,7 @@ static int test__event_update(struct test_suite *test __m= aybe_unused, int subtes TEST_ASSERT_VAL("failed to allocate ids", !perf_evsel__alloc_id(&evsel->core, 1, 1)); =20 - perf_evlist__id_add(&evlist->core, &evsel->core, 0, 0, 123); + perf_evlist__id_add(evlist__core(evlist), &evsel->core, 0, 0, 123); =20 free((char *)evsel->unit); evsel->unit =3D strdup("KRAVA"); diff --git a/tools/perf/tests/expand-cgroup.c b/tools/perf/tests/expand-cgr= oup.c index a7a445f12693..549fbd473ab7 100644 --- a/tools/perf/tests/expand-cgroup.c +++ b/tools/perf/tests/expand-cgroup.c @@ -28,7 +28,7 @@ static int test_expand_events(struct evlist *evlist) =20 TEST_ASSERT_VAL("evlist is empty", !evlist__empty(evlist)); =20 - nr_events =3D evlist->core.nr_entries; + nr_events =3D evlist__nr_entries(evlist); ev_name =3D calloc(nr_events, sizeof(*ev_name)); if (ev_name =3D=3D NULL) { pr_debug("memory allocation failure\n"); @@ -54,7 +54,7 @@ static int test_expand_events(struct evlist *evlist) } =20 ret =3D TEST_FAIL; - if (evlist->core.nr_entries !=3D nr_events * nr_cgrps) { + if (evlist__nr_entries(evlist) !=3D nr_events * nr_cgrps) { pr_debug("event count doesn't match\n"); goto out; } diff --git a/tools/perf/tests/hwmon_pmu.c b/tools/perf/tests/hwmon_pmu.c index 1b60c3a900f1..9dfc890841bf 100644 --- a/tools/perf/tests/hwmon_pmu.c +++ b/tools/perf/tests/hwmon_pmu.c @@ -183,9 +183,10 @@ static int do_test(size_t i, bool with_pmu, bool with_= alias) } =20 ret =3D TEST_OK; - if (with_pmu ? (evlist->core.nr_entries !=3D 1) : (evlist->core.nr_entrie= s < 1)) { + if (with_pmu ? (evlist__nr_entries(evlist) !=3D 1) + : (evlist__nr_entries(evlist) < 1)) { pr_debug("FAILED %s:%d Unexpected number of events for '%s' of %d\n", - __FILE__, __LINE__, str, evlist->core.nr_entries); + __FILE__, __LINE__, str, evlist__nr_entries(evlist)); ret =3D TEST_FAIL; goto out; } diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-track= ing.c index 51cfd6522867..b760041bed30 100644 --- a/tools/perf/tests/keep-tracking.c +++ b/tools/perf/tests/keep-tracking.c @@ -37,8 +37,8 @@ static int find_comm(struct evlist *evlist, const char *c= omm) int i, found; =20 found =3D 0; - for (i =3D 0; i < evlist->core.nr_mmaps; i++) { - md =3D &evlist->mmap[i]; + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) { + md =3D &evlist__mmap(evlist)[i]; if (perf_mmap__read_init(&md->core) < 0) continue; while ((event =3D perf_mmap__read_event(&md->core)) !=3D NULL) { @@ -87,7 +87,7 @@ static int test__keep_tracking(struct test_suite *test __= maybe_unused, int subte evlist =3D evlist__new(); CHECK_NOT_NULL__(evlist); =20 - perf_evlist__set_maps(&evlist->core, cpus, threads); + perf_evlist__set_maps(evlist__core(evlist), cpus, threads); =20 CHECK__(parse_event(evlist, "dummy:u")); CHECK__(parse_event(evlist, "cpu-cycles:u")); @@ -106,7 +106,7 @@ static int test__keep_tracking(struct test_suite *test = __maybe_unused, int subte goto out_err; } =20 - CHECK__(evlist__mmap(evlist, UINT_MAX)); + CHECK__(evlist__do_mmap(evlist, UINT_MAX)); =20 /* * First, test that a 'comm' event can be found when the event is diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index e6501791c505..e2e65f344c72 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c @@ -81,7 +81,7 @@ static int test__basic_mmap(struct test_suite *test __may= be_unused, int subtest goto out_free_cpus; } =20 - perf_evlist__set_maps(&evlist->core, cpus, threads); + perf_evlist__set_maps(evlist__core(evlist), cpus, threads); =20 for (i =3D 0; i < nsyscalls; ++i) { char name[64]; @@ -113,7 +113,7 @@ static int test__basic_mmap(struct test_suite *test __m= aybe_unused, int subtest expected_nr_events[i] =3D 1 + rand() % 127; } =20 - if (evlist__mmap(evlist, 128) < 0) { + if (evlist__do_mmap(evlist, 128) < 0) { pr_debug("failed to mmap events: %d (%s)\n", errno, str_error_r(errno, sbuf, sizeof(sbuf))); goto out_put_evlist; @@ -124,7 +124,7 @@ static int test__basic_mmap(struct test_suite *test __m= aybe_unused, int subtest syscalls[i](); } =20 - md =3D &evlist->mmap[0]; + md =3D &evlist__mmap(evlist)[0]; if (perf_mmap__read_init(&md->core) < 0) goto out_init; =20 diff --git a/tools/perf/tests/openat-syscall-tp-fields.c b/tools/perf/tests= /openat-syscall-tp-fields.c index 3ff595c7a86a..7f5eaa492bab 100644 --- a/tools/perf/tests/openat-syscall-tp-fields.c +++ b/tools/perf/tests/openat-syscall-tp-fields.c @@ -64,7 +64,7 @@ static int test__syscall_openat_tp_fields(struct test_sui= te *test __maybe_unused =20 evsel__config(evsel, &opts, NULL); =20 - perf_thread_map__set_pid(evlist->core.threads, 0, getpid()); + perf_thread_map__set_pid(evlist__core(evlist)->threads, 0, getpid()); =20 err =3D evlist__open(evlist); if (err < 0) { @@ -73,7 +73,7 @@ static int test__syscall_openat_tp_fields(struct test_sui= te *test __maybe_unused goto out_put_evlist; } =20 - err =3D evlist__mmap(evlist, UINT_MAX); + err =3D evlist__do_mmap(evlist, UINT_MAX); if (err < 0) { pr_debug("evlist__mmap: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); @@ -90,11 +90,11 @@ static int test__syscall_openat_tp_fields(struct test_s= uite *test __maybe_unused while (1) { int before =3D nr_events; =20 - for (i =3D 0; i < evlist->core.nr_mmaps; i++) { + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) { union perf_event *event; struct mmap *md; =20 - md =3D &evlist->mmap[i]; + md =3D &evlist__mmap(evlist)[i]; if (perf_mmap__read_init(&md->core) < 0) continue; =20 diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-event= s.c index 19dc7b7475d2..0ad0273da923 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -109,7 +109,7 @@ static int test__checkevent_tracepoint(struct evlist *e= vlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVLIST("wrong number of groups", 0 =3D=3D evlist__nr_groups(e= vlist), evlist); TEST_ASSERT_EVSEL("wrong type", PERF_TYPE_TRACEPOINT =3D=3D evsel->core.a= ttr.type, evsel); TEST_ASSERT_EVSEL("wrong sample_type", @@ -122,7 +122,7 @@ static int test__checkevent_tracepoint_multi(struct evl= ist *evlist) { struct evsel *evsel; =20 - TEST_ASSERT_EVLIST("wrong number of entries", evlist->core.nr_entries > 1= , evlist); + TEST_ASSERT_EVLIST("wrong number of entries", evlist__nr_entries(evlist) = > 1, evlist); TEST_ASSERT_EVLIST("wrong number of groups", 0 =3D=3D evlist__nr_groups(e= vlist), evlist); =20 evlist__for_each_entry(evlist, evsel) { @@ -144,7 +144,7 @@ static int test__checkevent_raw(struct evlist *evlist) struct evsel *evsel; bool raw_type_match =3D false; =20 - TEST_ASSERT_EVLIST("wrong number of entries", 0 !=3D evlist->core.nr_entr= ies, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 0 !=3D evlist__nr_entries(e= vlist), evlist); =20 evlist__for_each_entry(evlist, evsel) { struct perf_pmu *pmu __maybe_unused =3D NULL; @@ -182,7 +182,7 @@ static int test__checkevent_numeric(struct evlist *evli= st) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVSEL("wrong type", 1 =3D=3D evsel->core.attr.type, evsel); TEST_ASSERT_EVSEL("wrong config", 1 =3D=3D evsel->core.attr.config, evsel= ); return TEST_OK; @@ -193,7 +193,7 @@ static int test__checkevent_symbolic_name(struct evlist= *evlist) { struct evsel *evsel; =20 - TEST_ASSERT_EVLIST("wrong number of entries", 0 !=3D evlist->core.nr_entr= ies, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 0 !=3D evlist__nr_entries(e= vlist), evlist); =20 evlist__for_each_entry(evlist, evsel) { TEST_ASSERT_EVSEL("unexpected event", @@ -207,7 +207,7 @@ static int test__checkevent_symbolic_name_config(struct= evlist *evlist) { struct evsel *evsel; =20 - TEST_ASSERT_EVLIST("wrong number of entries", 0 !=3D evlist->core.nr_entr= ies, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 0 !=3D evlist__nr_entries(e= vlist), evlist); =20 evlist__for_each_entry(evlist, evsel) { TEST_ASSERT_EVSEL("unexpected event", @@ -228,7 +228,7 @@ static int test__checkevent_symbolic_alias(struct evlis= t *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVSEL("wrong type/config", evsel__match(evsel, SOFTWARE, SW_P= AGE_FAULTS), evsel); return TEST_OK; @@ -238,7 +238,7 @@ static int test__checkevent_genhw(struct evlist *evlist) { struct evsel *evsel; =20 - TEST_ASSERT_EVLIST("wrong number of entries", 0 !=3D evlist->core.nr_entr= ies, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 0 !=3D evlist__nr_entries(e= vlist), evlist); =20 evlist__for_each_entry(evlist, evsel) { TEST_ASSERT_EVSEL("wrong type", PERF_TYPE_HW_CACHE =3D=3D evsel->core.at= tr.type, evsel); @@ -251,7 +251,7 @@ static int test__checkevent_breakpoint(struct evlist *e= vlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVSEL("wrong type", PERF_TYPE_BREAKPOINT =3D=3D evsel->core.a= ttr.type, evsel); TEST_ASSERT_EVSEL("wrong config", 0 =3D=3D evsel->core.attr.config, evsel= ); TEST_ASSERT_EVSEL("wrong bp_type", @@ -265,7 +265,7 @@ static int test__checkevent_breakpoint_x(struct evlist = *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVSEL("wrong type", PERF_TYPE_BREAKPOINT =3D=3D evsel->core.a= ttr.type, evsel); TEST_ASSERT_EVSEL("wrong config", 0 =3D=3D evsel->core.attr.config, evsel= ); TEST_ASSERT_EVSEL("wrong bp_type", HW_BREAKPOINT_X =3D=3D evsel->core.att= r.bp_type, evsel); @@ -278,7 +278,7 @@ static int test__checkevent_breakpoint_r(struct evlist = *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVSEL("wrong type", PERF_TYPE_BREAKPOINT =3D=3D evsel->core.a= ttr.type, evsel); TEST_ASSERT_EVSEL("wrong config", 0 =3D=3D evsel->core.attr.config, evsel= ); TEST_ASSERT_EVSEL("wrong bp_type", HW_BREAKPOINT_R =3D=3D evsel->core.att= r.bp_type, evsel); @@ -290,7 +290,7 @@ static int test__checkevent_breakpoint_w(struct evlist = *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVSEL("wrong type", PERF_TYPE_BREAKPOINT =3D=3D evsel->core.a= ttr.type, evsel); TEST_ASSERT_EVSEL("wrong config", 0 =3D=3D evsel->core.attr.config, evsel= ); TEST_ASSERT_EVSEL("wrong bp_type", HW_BREAKPOINT_W =3D=3D evsel->core.att= r.bp_type, evsel); @@ -302,7 +302,7 @@ static int test__checkevent_breakpoint_rw(struct evlist= *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVSEL("wrong type", PERF_TYPE_BREAKPOINT =3D=3D evsel->core.a= ttr.type, evsel); TEST_ASSERT_EVSEL("wrong config", 0 =3D=3D evsel->core.attr.config, evsel= ); TEST_ASSERT_EVSEL("wrong bp_type", @@ -316,7 +316,7 @@ static int test__checkevent_tracepoint_modifier(struct = evlist *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVSEL("wrong exclude_user", evsel->core.attr.exclude_user, ev= sel); TEST_ASSERT_EVSEL("wrong exclude_kernel", !evsel->core.attr.exclude_kerne= l, evsel); TEST_ASSERT_EVSEL("wrong exclude_hv", evsel->core.attr.exclude_hv, evsel); @@ -330,7 +330,7 @@ test__checkevent_tracepoint_multi_modifier(struct evlis= t *evlist) { struct evsel *evsel; =20 - TEST_ASSERT_EVLIST("wrong number of entries", evlist->core.nr_entries > 1= , evlist); + TEST_ASSERT_EVLIST("wrong number of entries", evlist__nr_entries(evlist) = > 1, evlist); =20 evlist__for_each_entry(evlist, evsel) { TEST_ASSERT_EVSEL("wrong exclude_user", !evsel->core.attr.exclude_user, = evsel); @@ -346,7 +346,7 @@ static int test__checkevent_raw_modifier(struct evlist = *evlist) { struct evsel *evsel; =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); =20 evlist__for_each_entry(evlist, evsel) { TEST_ASSERT_EVSEL("wrong exclude_user", evsel->core.attr.exclude_user, e= vsel); @@ -361,7 +361,7 @@ static int test__checkevent_numeric_modifier(struct evl= ist *evlist) { struct evsel *evsel; =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); =20 evlist__for_each_entry(evlist, evsel) { TEST_ASSERT_EVSEL("wrong exclude_user", evsel->core.attr.exclude_user, e= vsel); @@ -377,7 +377,7 @@ static int test__checkevent_symbolic_name_modifier(stru= ct evlist *evlist) struct evsel *evsel; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D num_core_entries(evlist), evlist); =20 evlist__for_each_entry(evlist, evsel) { @@ -394,7 +394,7 @@ static int test__checkevent_exclude_host_modifier(struc= t evlist *evlist) struct evsel *evsel; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D num_core_entries(evlist), evlist); =20 evlist__for_each_entry(evlist, evsel) { @@ -409,7 +409,7 @@ static int test__checkevent_exclude_guest_modifier(stru= ct evlist *evlist) struct evsel *evsel; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D num_core_entries(evlist), evlist); =20 evlist__for_each_entry(evlist, evsel) { @@ -423,7 +423,8 @@ static int test__checkevent_symbolic_alias_modifier(str= uct evlist *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", + 1 =3D=3D evlist__nr_entries(evlist), evlist); TEST_ASSERT_EVSEL("wrong exclude_user", !evsel->core.attr.exclude_user, e= vsel); TEST_ASSERT_EVSEL("wrong exclude_kernel", evsel->core.attr.exclude_kernel= , evsel); TEST_ASSERT_EVSEL("wrong exclude_hv", evsel->core.attr.exclude_hv, evsel); @@ -437,7 +438,7 @@ static int test__checkevent_genhw_modifier(struct evlis= t *evlist) struct evsel *evsel; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D num_core_entries(evlist), evlist); =20 evlist__for_each_entry(evlist, evsel) { @@ -454,7 +455,7 @@ static int test__checkevent_exclude_idle_modifier(struc= t evlist *evlist) struct evsel *evsel =3D evlist__first(evlist); =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D num_core_entries(evlist), evlist); =20 TEST_ASSERT_EVSEL("wrong exclude idle", evsel->core.attr.exclude_idle, ev= sel); @@ -473,7 +474,7 @@ static int test__checkevent_exclude_idle_modifier_1(str= uct evlist *evlist) struct evsel *evsel =3D evlist__first(evlist); =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D num_core_entries(evlist), evlist); =20 TEST_ASSERT_EVSEL("wrong exclude idle", evsel->core.attr.exclude_idle, ev= sel); @@ -622,7 +623,7 @@ static int test__checkevent_breakpoint_2_events(struct = evlist *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVSEL("wrong number of entries", 2 =3D=3D evlist->core.nr_ent= ries, evsel); + TEST_ASSERT_EVSEL("wrong number of entries", 2 =3D=3D evlist__nr_entries(= evlist), evsel); =20 TEST_ASSERT_EVSEL("wrong type", PERF_TYPE_BREAKPOINT =3D=3D evsel->core.a= ttr.type, evsel); TEST_ASSERT_EVSEL("wrong name", evsel__name_is(evsel, "breakpoint1"), evs= el); @@ -641,7 +642,7 @@ static int test__checkevent_pmu(struct evlist *evlist) struct evsel *evsel =3D evlist__first(evlist); struct perf_pmu *core_pmu =3D perf_pmus__find_core_pmu(); =20 - TEST_ASSERT_EVSEL("wrong number of entries", 1 =3D=3D evlist->core.nr_ent= ries, evsel); + TEST_ASSERT_EVSEL("wrong number of entries", 1 =3D=3D evlist__nr_entries(= evlist), evsel); TEST_ASSERT_EVSEL("wrong type", core_pmu->type =3D=3D evsel->core.attr.ty= pe, evsel); TEST_ASSERT_EVSEL("wrong config", test_hw_config(evsel, 10), evsel); TEST_ASSERT_EVSEL("wrong config1", 1 =3D=3D evsel->core.attr.config1, = evsel); @@ -661,7 +662,7 @@ static int test__checkevent_list(struct evlist *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVSEL("wrong number of entries", 3 <=3D evlist->core.nr_entri= es, evsel); + TEST_ASSERT_EVSEL("wrong number of entries", 3 <=3D evlist__nr_entries(ev= list), evsel); =20 /* r1 */ TEST_ASSERT_EVSEL("wrong type", PERF_TYPE_TRACEPOINT !=3D evsel->core.att= r.type, evsel); @@ -707,14 +708,15 @@ static int test__checkevent_pmu_name(struct evlist *e= vlist) char buf[256]; =20 /* default_core/config=3D1,name=3Dkrava/u */ - TEST_ASSERT_EVLIST("wrong number of entries", 2 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", + 2 =3D=3D evlist__nr_entries(evlist), evlist); TEST_ASSERT_EVSEL("wrong type", core_pmu->type =3D=3D evsel->core.attr.ty= pe, evsel); TEST_ASSERT_EVSEL("wrong config", 1 =3D=3D evsel->core.attr.config, evsel= ); TEST_ASSERT_EVSEL("wrong name", evsel__name_is(evsel, "krava"), evsel); =20 /* default_core/config=3D2/u" */ evsel =3D evsel__next(evsel); - TEST_ASSERT_EVSEL("wrong number of entries", 2 =3D=3D evlist->core.nr_ent= ries, evsel); + TEST_ASSERT_EVSEL("wrong number of entries", 2 =3D=3D evlist__nr_entries(= evlist), evsel); TEST_ASSERT_EVSEL("wrong type", core_pmu->type =3D=3D evsel->core.attr.ty= pe, evsel); TEST_ASSERT_EVSEL("wrong config", 2 =3D=3D evsel->core.attr.config, evsel= ); snprintf(buf, sizeof(buf), "%s/config=3D2/u", core_pmu->name); @@ -729,7 +731,8 @@ static int test__checkevent_pmu_partial_time_callgraph(= struct evlist *evlist) struct perf_pmu *core_pmu =3D perf_pmus__find_core_pmu(); =20 /* default_core/config=3D1,call-graph=3Dfp,time,period=3D100000/ */ - TEST_ASSERT_EVLIST("wrong number of entries", 2 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", + 2 =3D=3D evlist__nr_entries(evlist), evlist); TEST_ASSERT_EVSEL("wrong type", core_pmu->type =3D=3D evsel->core.attr.ty= pe, evsel); TEST_ASSERT_EVSEL("wrong config", 1 =3D=3D evsel->core.attr.config, evsel= ); /* @@ -760,7 +763,7 @@ static int test__checkevent_pmu_events(struct evlist *e= vlist) struct evsel *evsel; struct perf_pmu *core_pmu =3D perf_pmus__find_core_pmu(); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 <=3D evlist->core.nr_entr= ies, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 <=3D evlist__nr_entries(e= vlist), evlist); =20 evlist__for_each_entry(evlist, evsel) { TEST_ASSERT_EVSEL("wrong type", @@ -787,8 +790,9 @@ static int test__checkevent_pmu_events_mix(struct evlis= t *evlist) * The wild card event will be opened at least once, but it may be * opened on each core PMU. */ - TEST_ASSERT_EVLIST("wrong number of entries", evlist->core.nr_entries >= =3D 2, evlist); - for (int i =3D 0; i < evlist->core.nr_entries - 1; i++) { + TEST_ASSERT_EVLIST("wrong number of entries", + evlist__nr_entries(evlist) >=3D 2, evlist); + for (int i =3D 0; i < evlist__nr_entries(evlist) - 1; i++) { evsel =3D (i =3D=3D 0 ? evlist__first(evlist) : evsel__next(evsel)); /* pmu-event:u */ TEST_ASSERT_EVSEL("wrong exclude_user", !evsel->core.attr.exclude_user, = evsel); @@ -905,7 +909,7 @@ static int test__group1(struct evlist *evlist) struct evsel *evsel =3D NULL, *leader; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D (num_core_entries(evlist) * 2), + evlist__nr_entries(evlist) =3D=3D (num_core_entries(evlist) * 2), evlist); TEST_ASSERT_EVLIST("wrong number of groups", evlist__nr_groups(evlist) =3D=3D num_core_entries(evlist), @@ -950,7 +954,7 @@ static int test__group2(struct evlist *evlist) struct evsel *evsel, *leader =3D NULL; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D (2 * num_core_entries(evlist) + 1), + evlist__nr_entries(evlist) =3D=3D (2 * num_core_entries(evlist) + 1), evlist); /* * TODO: Currently the software event won't be grouped with the hardware @@ -1018,7 +1022,7 @@ static int test__group3(struct evlist *evlist __maybe= _unused) struct evsel *evsel, *group1_leader =3D NULL, *group2_leader =3D NULL; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D (3 * perf_pmus__num_core_pmus() + 2), + evlist__nr_entries(evlist) =3D=3D (3 * perf_pmus__num_core_pmus() + = 2), evlist); /* * Currently the software event won't be grouped with the hardware event @@ -1144,7 +1148,7 @@ static int test__group4(struct evlist *evlist __maybe= _unused) struct evsel *evsel =3D NULL, *leader; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D (num_core_entries(evlist) * 2), + evlist__nr_entries(evlist) =3D=3D (num_core_entries(evlist) * 2), evlist); TEST_ASSERT_EVLIST("wrong number of groups", num_core_entries(evlist) =3D=3D evlist__nr_groups(evlist), @@ -1191,7 +1195,7 @@ static int test__group5(struct evlist *evlist __maybe= _unused) struct evsel *evsel =3D NULL, *leader; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D (5 * num_core_entries(evlist)), + evlist__nr_entries(evlist) =3D=3D (5 * num_core_entries(evlist)), evlist); TEST_ASSERT_EVLIST("wrong number of groups", evlist__nr_groups(evlist) =3D=3D (2 * num_core_entries(evlist)), @@ -1284,7 +1288,7 @@ static int test__group_gh1(struct evlist *evlist) struct evsel *evsel =3D NULL, *leader; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D (2 * num_core_entries(evlist)), + evlist__nr_entries(evlist) =3D=3D (2 * num_core_entries(evlist)), evlist); TEST_ASSERT_EVLIST("wrong number of groups", evlist__nr_groups(evlist) =3D=3D num_core_entries(evlist), @@ -1329,7 +1333,7 @@ static int test__group_gh2(struct evlist *evlist) struct evsel *evsel =3D NULL, *leader; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D (2 * num_core_entries(evlist)), + evlist__nr_entries(evlist) =3D=3D (2 * num_core_entries(evlist)), evlist); TEST_ASSERT_EVLIST("wrong number of groups", evlist__nr_groups(evlist) =3D=3D num_core_entries(evlist), @@ -1374,7 +1378,7 @@ static int test__group_gh3(struct evlist *evlist) struct evsel *evsel =3D NULL, *leader; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D (2 * num_core_entries(evlist)), + evlist__nr_entries(evlist) =3D=3D (2 * num_core_entries(evlist)), evlist); TEST_ASSERT_EVLIST("wrong number of groups", evlist__nr_groups(evlist) =3D=3D num_core_entries(evlist), @@ -1419,7 +1423,7 @@ static int test__group_gh4(struct evlist *evlist) struct evsel *evsel =3D NULL, *leader; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D (2 * num_core_entries(evlist)), + evlist__nr_entries(evlist) =3D=3D (2 * num_core_entries(evlist)), evlist); TEST_ASSERT_EVLIST("wrong number of groups", evlist__nr_groups(evlist) =3D=3D num_core_entries(evlist), @@ -1464,7 +1468,7 @@ static int test__leader_sample1(struct evlist *evlist) struct evsel *evsel =3D NULL, *leader; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D (3 * num_core_entries(evlist)), + evlist__nr_entries(evlist) =3D=3D (3 * num_core_entries(evlist)), evlist); =20 for (int i =3D 0; i < num_core_entries(evlist); i++) { @@ -1520,7 +1524,7 @@ static int test__leader_sample2(struct evlist *evlist= __maybe_unused) struct evsel *evsel =3D NULL, *leader; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D (2 * num_core_entries(evlist)), + evlist__nr_entries(evlist) =3D=3D (2 * num_core_entries(evlist)), evlist); =20 for (int i =3D 0; i < num_core_entries(evlist); i++) { @@ -1562,7 +1566,7 @@ static int test__checkevent_pinned_modifier(struct ev= list *evlist) struct evsel *evsel =3D NULL; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D num_core_entries(evlist), evlist); =20 for (int i =3D 0; i < num_core_entries(evlist); i++) { @@ -1581,7 +1585,7 @@ static int test__pinned_group(struct evlist *evlist) struct evsel *evsel =3D NULL, *leader; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D (3 * num_core_entries(evlist)), + evlist__nr_entries(evlist) =3D=3D (3 * num_core_entries(evlist)), evlist); =20 for (int i =3D 0; i < num_core_entries(evlist); i++) { @@ -1618,7 +1622,7 @@ static int test__checkevent_exclusive_modifier(struct= evlist *evlist) struct evsel *evsel =3D evlist__first(evlist); =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D num_core_entries(evlist), evlist); TEST_ASSERT_EVSEL("wrong exclude_user", !evsel->core.attr.exclude_user, e= vsel); TEST_ASSERT_EVSEL("wrong exclude_kernel", evsel->core.attr.exclude_kernel= , evsel); @@ -1634,7 +1638,7 @@ static int test__exclusive_group(struct evlist *evlis= t) struct evsel *evsel =3D NULL, *leader; =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D 3 * num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D 3 * num_core_entries(evlist), evlist); =20 for (int i =3D 0; i < num_core_entries(evlist); i++) { @@ -1669,7 +1673,7 @@ static int test__checkevent_breakpoint_len(struct evl= ist *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVSEL("wrong type", PERF_TYPE_BREAKPOINT =3D=3D evsel->core.a= ttr.type, evsel); TEST_ASSERT_EVSEL("wrong config", 0 =3D=3D evsel->core.attr.config, evsel= ); TEST_ASSERT_EVSEL("wrong bp_type", @@ -1684,7 +1688,7 @@ static int test__checkevent_breakpoint_len_w(struct e= vlist *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVSEL("wrong type", PERF_TYPE_BREAKPOINT =3D=3D evsel->core.a= ttr.type, evsel); TEST_ASSERT_EVSEL("wrong config", 0 =3D=3D evsel->core.attr.config, evsel= ); TEST_ASSERT_EVSEL("wrong bp_type", HW_BREAKPOINT_W =3D=3D evsel->core.att= r.bp_type, evsel); @@ -1698,7 +1702,7 @@ test__checkevent_breakpoint_len_rw_modifier(struct ev= list *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVSEL("wrong exclude_user", !evsel->core.attr.exclude_user, e= vsel); TEST_ASSERT_EVSEL("wrong exclude_kernel", evsel->core.attr.exclude_kernel= , evsel); TEST_ASSERT_EVSEL("wrong exclude_hv", evsel->core.attr.exclude_hv, evsel); @@ -1712,7 +1716,7 @@ static int test__checkevent_precise_max_modifier(stru= ct evlist *evlist) struct evsel *evsel =3D evlist__first(evlist); =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D 1 + num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D 1 + num_core_entries(evlist), evlist); TEST_ASSERT_EVSEL("wrong type/config", evsel__match(evsel, SOFTWARE, SW_T= ASK_CLOCK), evsel); return TEST_OK; @@ -1723,7 +1727,7 @@ static int test__checkevent_config_symbol(struct evli= st *evlist) struct evsel *evsel =3D evlist__first(evlist); =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D num_core_entries(evlist), evlist); TEST_ASSERT_EVSEL("wrong name setting", evsel__name_is(evsel, "insn"), ev= sel); return TEST_OK; @@ -1733,7 +1737,7 @@ static int test__checkevent_config_raw(struct evlist = *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVSEL("wrong name setting", evsel__name_is(evsel, "rawpmu"), = evsel); return TEST_OK; } @@ -1742,7 +1746,7 @@ static int test__checkevent_config_num(struct evlist = *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVSEL("wrong name setting", evsel__name_is(evsel, "numpmu"), = evsel); return TEST_OK; } @@ -1752,7 +1756,7 @@ static int test__checkevent_config_cache(struct evlis= t *evlist) struct evsel *evsel =3D evlist__first(evlist); =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D num_core_entries(evlist), evlist); TEST_ASSERT_EVSEL("wrong name setting", evsel__name_is(evsel, "cachepmu")= , evsel); return test__checkevent_genhw(evlist); @@ -1777,7 +1781,7 @@ static int test__intel_pt(struct evlist *evlist) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVSEL("wrong name setting", evsel__name_is(evsel, "intel_pt//= u"), evsel); return TEST_OK; } @@ -1798,7 +1802,8 @@ static int test__ratio_to_prev(struct evlist *evlist) { struct evsel *evsel, *leader; =20 - TEST_ASSERT_VAL("wrong number of entries", 2 * perf_pmus__num_core_pmus()= =3D=3D evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong number of entries", + 2 * perf_pmus__num_core_pmus() =3D=3D evlist__nr_entries(evlist)); =20 evlist__for_each_entry(evlist, evsel) { if (evsel !=3D evsel__leader(evsel) || @@ -1842,7 +1847,7 @@ static int test__checkevent_complex_name(struct evlis= t *evlist) struct evsel *evsel =3D evlist__first(evlist); =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D num_core_entries(evlist), evlist); TEST_ASSERT_EVSEL("wrong complex name parsing", evsel__name_is(evsel, @@ -1855,7 +1860,7 @@ static int test__checkevent_raw_pmu(struct evlist *ev= list) { struct evsel *evsel =3D evlist__first(evlist); =20 - TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist->core.nr_en= tries, evlist); + TEST_ASSERT_EVLIST("wrong number of entries", 1 =3D=3D evlist__nr_entries= (evlist), evlist); TEST_ASSERT_EVSEL("wrong type", PERF_TYPE_SOFTWARE =3D=3D evsel->core.att= r.type, evsel); TEST_ASSERT_EVSEL("wrong config", 0x1a =3D=3D evsel->core.attr.config, ev= sel); return TEST_OK; @@ -1866,7 +1871,7 @@ static int test__sym_event_slash(struct evlist *evlis= t) struct evsel *evsel =3D evlist__first(evlist); =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D num_core_entries(evlist), evlist); TEST_ASSERT_EVSEL("unexpected event", evsel__match(evsel, HARDWARE, HW_CP= U_CYCLES), evsel); TEST_ASSERT_EVSEL("wrong exclude_kernel", evsel->core.attr.exclude_kernel= , evsel); @@ -1878,7 +1883,7 @@ static int test__sym_event_dc(struct evlist *evlist) struct evsel *evsel =3D evlist__first(evlist); =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D num_core_entries(evlist), evlist); TEST_ASSERT_EVSEL("unexpected event", evsel__match(evsel, HARDWARE, HW_CP= U_CYCLES), evsel); TEST_ASSERT_EVSEL("wrong exclude_user", evsel->core.attr.exclude_user, ev= sel); @@ -1890,7 +1895,7 @@ static int test__term_equal_term(struct evlist *evlis= t) struct evsel *evsel =3D evlist__first(evlist); =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D num_core_entries(evlist), evlist); TEST_ASSERT_EVSEL("unexpected event", evsel__match(evsel, HARDWARE, HW_CP= U_CYCLES), evsel); TEST_ASSERT_EVSEL("wrong name setting", strcmp(evsel->name, "name") =3D= =3D 0, evsel); @@ -1902,7 +1907,7 @@ static int test__term_equal_legacy(struct evlist *evl= ist) struct evsel *evsel =3D evlist__first(evlist); =20 TEST_ASSERT_EVLIST("wrong number of entries", - evlist->core.nr_entries =3D=3D num_core_entries(evlist), + evlist__nr_entries(evlist) =3D=3D num_core_entries(evlist), evlist); TEST_ASSERT_EVSEL("unexpected event", evsel__match(evsel, HARDWARE, HW_CP= U_CYCLES), evsel); TEST_ASSERT_EVSEL("wrong name setting", strcmp(evsel->name, "l1d") =3D=3D= 0, evsel); @@ -1958,7 +1963,7 @@ static int count_tracepoints(void) static int test__all_tracepoints(struct evlist *evlist) { TEST_ASSERT_VAL("wrong events count", - count_tracepoints() =3D=3D evlist->core.nr_entries); + count_tracepoints() =3D=3D evlist__nr_entries(evlist)); =20 return test__checkevent_tracepoint_multi(evlist); } diff --git a/tools/perf/tests/parse-metric.c b/tools/perf/tests/parse-metri= c.c index 3f0ec839c056..8f9211eaf341 100644 --- a/tools/perf/tests/parse-metric.c +++ b/tools/perf/tests/parse-metric.c @@ -53,7 +53,7 @@ static double compute_single(struct evlist *evlist, const= char *name) struct evsel *evsel; =20 evlist__for_each_entry(evlist, evsel) { - me =3D metricgroup__lookup(&evlist->metric_events, evsel, false); + me =3D metricgroup__lookup(evlist__metric_events(evlist), evsel, false); if (me !=3D NULL) { list_for_each_entry (mexp, &me->head, nd) { if (strcmp(mexp->metric_name, name)) @@ -88,7 +88,7 @@ static int __compute_metric(const char *name, struct valu= e *vals, return -ENOMEM; } =20 - perf_evlist__set_maps(&evlist->core, cpus, NULL); + perf_evlist__set_maps(evlist__core(evlist), cpus, NULL); =20 /* Parse the metric into metric_events list. */ pme_test =3D find_core_metrics_table("testarch", "testcpu"); diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index f95752b2ed1c..0bd418e1cdc6 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -129,7 +129,7 @@ static int test__PERF_RECORD(struct test_suite *test __= maybe_unused, int subtest evsel__set_sample_bit(evsel, TIME); evlist__config(evlist, &opts, NULL); =20 - err =3D sched__get_first_possible_cpu(evlist->workload.pid, cpu_mask); + err =3D sched__get_first_possible_cpu(evlist__workload_pid(evlist), cpu_m= ask); if (err < 0) { pr_debug("sched__get_first_possible_cpu: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); @@ -142,7 +142,7 @@ static int test__PERF_RECORD(struct test_suite *test __= maybe_unused, int subtest /* * So that we can check perf_sample.cpu on all the samples. */ - if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, cpu_mask) < 0)= { + if (sched_setaffinity(evlist__workload_pid(evlist), cpu_mask_size, cpu_ma= sk) < 0) { pr_debug("sched_setaffinity: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); evlist__cancel_workload(evlist); @@ -166,7 +166,7 @@ static int test__PERF_RECORD(struct test_suite *test __= maybe_unused, int subtest * fds in the same CPU to be injected in the same mmap ring buffer * (using ioctl(PERF_EVENT_IOC_SET_OUTPUT)). */ - err =3D evlist__mmap(evlist, opts.mmap_pages); + err =3D evlist__do_mmap(evlist, opts.mmap_pages); if (err < 0) { pr_debug("evlist__mmap: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); @@ -188,11 +188,11 @@ static int test__PERF_RECORD(struct test_suite *test = __maybe_unused, int subtest while (1) { int before =3D total_events; =20 - for (i =3D 0; i < evlist->core.nr_mmaps; i++) { + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) { union perf_event *event; struct mmap *md; =20 - md =3D &evlist->mmap[i]; + md =3D &evlist__mmap(evlist)[i]; if (perf_mmap__read_init(&md->core) < 0) continue; =20 @@ -231,15 +231,15 @@ static int test__PERF_RECORD(struct test_suite *test = __maybe_unused, int subtest ++errs; } =20 - if ((pid_t)sample.pid !=3D evlist->workload.pid) { + if ((pid_t)sample.pid !=3D evlist__workload_pid(evlist)) { pr_debug("%s with unexpected pid, expected %d, got %d\n", - name, evlist->workload.pid, sample.pid); + name, evlist__workload_pid(evlist), sample.pid); ++errs; } =20 - if ((pid_t)sample.tid !=3D evlist->workload.pid) { + if ((pid_t)sample.tid !=3D evlist__workload_pid(evlist)) { pr_debug("%s with unexpected tid, expected %d, got %d\n", - name, evlist->workload.pid, sample.tid); + name, evlist__workload_pid(evlist), sample.tid); ++errs; } =20 @@ -248,7 +248,7 @@ static int test__PERF_RECORD(struct test_suite *test __= maybe_unused, int subtest type =3D=3D PERF_RECORD_MMAP2 || type =3D=3D PERF_RECORD_FORK || type =3D=3D PERF_RECORD_EXIT) && - (pid_t)event->comm.pid !=3D evlist->workload.pid) { + (pid_t)event->comm.pid !=3D evlist__workload_pid(evlist)) { pr_debug("%s with unexpected pid/tid\n", name); ++errs; } diff --git a/tools/perf/tests/perf-time-to-tsc.c b/tools/perf/tests/perf-ti= me-to-tsc.c index d3538fa20af3..f8f71fdd32b1 100644 --- a/tools/perf/tests/perf-time-to-tsc.c +++ b/tools/perf/tests/perf-time-to-tsc.c @@ -99,7 +99,7 @@ static int test__perf_time_to_tsc(struct test_suite *test= __maybe_unused, int su evlist =3D evlist__new(); CHECK_NOT_NULL__(evlist); =20 - perf_evlist__set_maps(&evlist->core, cpus, threads); + perf_evlist__set_maps(evlist__core(evlist), cpus, threads); =20 CHECK__(parse_event(evlist, "cpu-cycles:u")); =20 @@ -121,9 +121,9 @@ static int test__perf_time_to_tsc(struct test_suite *te= st __maybe_unused, int su goto out_err; } =20 - CHECK__(evlist__mmap(evlist, UINT_MAX)); + CHECK__(evlist__do_mmap(evlist, UINT_MAX)); =20 - pc =3D evlist->mmap[0].core.base; + pc =3D evlist__mmap(evlist)[0].core.base; ret =3D perf_read_tsc_conversion(pc, &tc); if (ret) { if (ret =3D=3D -EOPNOTSUPP) { @@ -145,8 +145,8 @@ static int test__perf_time_to_tsc(struct test_suite *te= st __maybe_unused, int su =20 evlist__disable(evlist); =20 - for (i =3D 0; i < evlist->core.nr_mmaps; i++) { - md =3D &evlist->mmap[i]; + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) { + md =3D &evlist__mmap(evlist)[i]; if (perf_mmap__read_init(&md->core) < 0) continue; =20 diff --git a/tools/perf/tests/pfm.c b/tools/perf/tests/pfm.c index 8d19b1bfecbc..f7bf55be5e6e 100644 --- a/tools/perf/tests/pfm.c +++ b/tools/perf/tests/pfm.c @@ -69,12 +69,12 @@ static int test__pfm_events(struct test_suite *test __m= aybe_unused, if (evlist =3D=3D NULL) return -ENOMEM; =20 - opt.value =3D evlist; + opt.value =3D &evlist; parse_libpfm_events_option(&opt, table[i].events, 0); TEST_ASSERT_EQUAL(table[i].events, - count_pfm_events(&evlist->core), + count_pfm_events(evlist__core(evlist)), table[i].nr_events); TEST_ASSERT_EQUAL(table[i].events, evlist__nr_groups(evlist), @@ -154,12 +154,12 @@ static int test__pfm_group(struct test_suite *test __= maybe_unused, if (evlist =3D=3D NULL) return -ENOMEM; =20 - opt.value =3D evlist; + opt.value =3D &evlist; parse_libpfm_events_option(&opt, table[i].events, 0); TEST_ASSERT_EQUAL(table[i].events, - count_pfm_events(&evlist->core), + count_pfm_events(evlist__core(evlist)), table[i].nr_events); TEST_ASSERT_EQUAL(table[i].events, evlist__nr_groups(evlist), diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index 236bbbad5773..a66976ee093f 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -848,7 +848,7 @@ static int test__parsing_callback(const struct pmu_metr= ic *pm, return -ENOMEM; } =20 - perf_evlist__set_maps(&evlist->core, cpus, NULL); + perf_evlist__set_maps(evlist__core(evlist), cpus, NULL); =20 err =3D metricgroup__parse_groups_test(evlist, table, pm->metric_name); if (err) { @@ -875,7 +875,8 @@ static int test__parsing_callback(const struct pmu_metr= ic *pm, k++; } evlist__for_each_entry(evlist, evsel) { - struct metric_event *me =3D metricgroup__lookup(&evlist->metric_events, = evsel, false); + struct metric_event *me =3D metricgroup__lookup(evlist__metric_events(ev= list), + evsel, false); =20 if (me !=3D NULL) { struct metric_expr *mexp; diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-pa= rsing.c index 55f0b73ca20e..6db717e562d5 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c @@ -205,15 +205,11 @@ static bool samples_same(struct perf_sample *s1, =20 static int do_test(u64 sample_type, u64 sample_regs, u64 read_format) { - struct evsel evsel =3D { - .needs_swap =3D false, - .core =3D { - . attr =3D { - .sample_type =3D sample_type, - .read_format =3D read_format, - }, - }, + struct perf_event_attr attr =3D{ + .sample_type =3D sample_type, + .read_format =3D read_format, }; + struct evsel *evsel; union perf_event *event; union { struct ip_callchain callchain; @@ -287,16 +283,17 @@ static int do_test(u64 sample_type, u64 sample_regs, = u64 read_format) size_t i, sz, bufsz; int err, ret =3D -1; =20 + evsel =3D evsel__new(&attr); perf_sample__init(&sample_out, /*all=3D*/false); perf_sample__init(&sample_out_endian, /*all=3D*/false); if (sample_type & PERF_SAMPLE_REGS_USER) - evsel.core.attr.sample_regs_user =3D sample_regs; + evsel->core.attr.sample_regs_user =3D sample_regs; =20 if (sample_type & PERF_SAMPLE_REGS_INTR) - evsel.core.attr.sample_regs_intr =3D sample_regs; + evsel->core.attr.sample_regs_intr =3D sample_regs; =20 if (sample_type & PERF_SAMPLE_BRANCH_STACK) - evsel.core.attr.branch_sample_type |=3D PERF_SAMPLE_BRANCH_HW_INDEX; + evsel->core.attr.branch_sample_type |=3D PERF_SAMPLE_BRANCH_HW_INDEX; =20 for (i =3D 0; i < sizeof(regs); i++) *(i + (u8 *)regs) =3D i & 0xfe; @@ -311,7 +308,7 @@ static int do_test(u64 sample_type, u64 sample_regs, u6= 4 read_format) } =20 sz =3D perf_event__sample_event_size(&sample, sample_type, read_format, - evsel.core.attr.branch_sample_type); + evsel->core.attr.branch_sample_type); bufsz =3D sz + 4096; /* Add a bit for overrun checking */ event =3D malloc(bufsz); if (!event) { @@ -325,7 +322,7 @@ static int do_test(u64 sample_type, u64 sample_regs, u6= 4 read_format) event->header.size =3D sz; =20 err =3D perf_event__synthesize_sample(event, sample_type, read_format, - evsel.core.attr.branch_sample_type, &sample); + evsel->core.attr.branch_sample_type, &sample); if (err) { pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", "perf_event__synthesize_sample", sample_type, err); @@ -343,32 +340,32 @@ static int do_test(u64 sample_type, u64 sample_regs, = u64 read_format) goto out_free; } =20 - evsel.sample_size =3D __evsel__sample_size(sample_type); + evsel->sample_size =3D __evsel__sample_size(sample_type); =20 - err =3D evsel__parse_sample(&evsel, event, &sample_out); + err =3D evsel__parse_sample(evsel, event, &sample_out); if (err) { pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", "evsel__parse_sample", sample_type, err); goto out_free; } =20 - if (!samples_same(&sample, &sample_out, sample_type, read_format, evsel.n= eeds_swap)) { + if (!samples_same(&sample, &sample_out, sample_type, read_format, evsel->= needs_swap)) { pr_debug("parsing failed for sample_type %#"PRIx64"\n", sample_type); goto out_free; } =20 if (sample_type =3D=3D PERF_SAMPLE_BRANCH_STACK) { - evsel.needs_swap =3D true; - evsel.sample_size =3D __evsel__sample_size(sample_type); - err =3D evsel__parse_sample(&evsel, event, &sample_out_endian); + evsel->needs_swap =3D true; + evsel->sample_size =3D __evsel__sample_size(sample_type); + err =3D evsel__parse_sample(evsel, event, &sample_out_endian); if (err) { pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", "evsel__parse_sample", sample_type, err); goto out_free; } =20 - if (!samples_same(&sample, &sample_out_endian, sample_type, read_format,= evsel.needs_swap)) { + if (!samples_same(&sample, &sample_out_endian, sample_type, read_format,= evsel->needs_swap)) { pr_debug("parsing failed for sample_type %#"PRIx64"\n", sample_type); goto out_free; @@ -380,6 +377,7 @@ static int do_test(u64 sample_type, u64 sample_regs, u6= 4 read_format) free(event); perf_sample__exit(&sample_out_endian); perf_sample__exit(&sample_out); + evsel__put(evsel); if (ret && read_format) pr_debug("read_format %#"PRIx64"\n", read_format); return ret; diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c index bb6b62cf51d1..d18185881635 100644 --- a/tools/perf/tests/sw-clock.c +++ b/tools/perf/tests/sw-clock.c @@ -71,7 +71,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_i= d) goto out_put_evlist; } =20 - perf_evlist__set_maps(&evlist->core, cpus, threads); + perf_evlist__set_maps(evlist__core(evlist), cpus, threads); =20 if (evlist__open(evlist)) { const char *knob =3D "/proc/sys/kernel/perf_event_max_sample_rate"; @@ -83,7 +83,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_i= d) goto out_put_evlist; } =20 - err =3D evlist__mmap(evlist, 128); + err =3D evlist__do_mmap(evlist, 128); if (err < 0) { pr_debug("failed to mmap event: %d (%s)\n", errno, str_error_r(errno, sbuf, sizeof(sbuf))); @@ -98,7 +98,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_i= d) =20 evlist__disable(evlist); =20 - md =3D &evlist->mmap[0]; + md =3D &evlist__mmap(evlist)[0]; if (perf_mmap__read_init(&md->core) < 0) goto out_init; =20 diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-t= racking.c index 306151c83af8..2b1694be8a06 100644 --- a/tools/perf/tests/switch-tracking.c +++ b/tools/perf/tests/switch-tracking.c @@ -279,8 +279,8 @@ static int process_events(struct evlist *evlist, struct mmap *md; int i, ret; =20 - for (i =3D 0; i < evlist->core.nr_mmaps; i++) { - md =3D &evlist->mmap[i]; + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) { + md =3D &evlist__mmap(evlist)[i]; if (perf_mmap__read_init(&md->core) < 0) continue; =20 @@ -371,7 +371,7 @@ static int test__switch_tracking(struct test_suite *tes= t __maybe_unused, int sub goto out_err; } =20 - perf_evlist__set_maps(&evlist->core, cpus, threads); + perf_evlist__set_maps(evlist__core(evlist), cpus, threads); =20 /* First event */ err =3D parse_event(evlist, "cpu-clock:u"); @@ -468,7 +468,7 @@ static int test__switch_tracking(struct test_suite *tes= t __maybe_unused, int sub goto out; } =20 - err =3D evlist__mmap(evlist, UINT_MAX); + err =3D evlist__do_mmap(evlist, UINT_MAX); if (err) { pr_debug("evlist__mmap failed!\n"); goto out_err; diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index a46650b10689..95393edbfe36 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c @@ -77,7 +77,7 @@ static int test__task_exit(struct test_suite *test __mayb= e_unused, int subtest _ goto out_put_evlist; } =20 - perf_evlist__set_maps(&evlist->core, cpus, threads); + perf_evlist__set_maps(evlist__core(evlist), cpus, threads); =20 err =3D evlist__prepare_workload(evlist, &target, argv, false, workload_e= xec_failed_signal); if (err < 0) { @@ -104,7 +104,7 @@ static int test__task_exit(struct test_suite *test __ma= ybe_unused, int subtest _ goto out_put_evlist; } =20 - if (evlist__mmap(evlist, 128) < 0) { + if (evlist__do_mmap(evlist, 128) < 0) { pr_debug("failed to mmap events: %d (%s)\n", errno, str_error_r(errno, sbuf, sizeof(sbuf))); err =3D -1; @@ -114,7 +114,7 @@ static int test__task_exit(struct test_suite *test __ma= ybe_unused, int subtest _ evlist__start_workload(evlist); =20 retry: - md =3D &evlist->mmap[0]; + md =3D &evlist__mmap(evlist)[0]; if (perf_mmap__read_init(&md->core) < 0) goto out_init; =20 diff --git a/tools/perf/tests/time-utils-test.c b/tools/perf/tests/time-uti= ls-test.c index 38df10373c1e..90a9a4b4f178 100644 --- a/tools/perf/tests/time-utils-test.c +++ b/tools/perf/tests/time-utils-test.c @@ -69,16 +69,19 @@ struct test_data { =20 static bool test__perf_time__parse_for_ranges(struct test_data *d) { - struct evlist evlist =3D { - .first_sample_time =3D d->first, - .last_sample_time =3D d->last, - }; - struct perf_session session =3D { .evlist =3D &evlist }; + struct evlist *evlist =3D evlist__new(); + struct perf_session session =3D { .evlist =3D evlist }; struct perf_time_interval *ptime =3D NULL; int range_size, range_num; bool pass =3D false; int i, err; =20 + if (!evlist) { + pr_debug("Missing evlist\n"); + return false; + } + evlist__set_first_sample_time(evlist, d->first); + evlist__set_last_sample_time(evlist, d->last); pr_debug("\nperf_time__parse_for_ranges(\"%s\")\n", d->str); =20 if (strchr(d->str, '%')) @@ -127,6 +130,7 @@ static bool test__perf_time__parse_for_ranges(struct te= st_data *d) =20 pass =3D true; out: + evlist__put(evlist); free(ptime); return pass; } diff --git a/tools/perf/tests/tool_pmu.c b/tools/perf/tests/tool_pmu.c index e78ff9dcea97..c6c5ebf0e935 100644 --- a/tools/perf/tests/tool_pmu.c +++ b/tools/perf/tests/tool_pmu.c @@ -40,9 +40,10 @@ static int do_test(enum tool_pmu_event ev, bool with_pmu) } =20 ret =3D TEST_OK; - if (with_pmu ? (evlist->core.nr_entries !=3D 1) : (evlist->core.nr_entrie= s < 1)) { + if (with_pmu ? (evlist__nr_entries(evlist) !=3D 1) + : (evlist__nr_entries(evlist) < 1)) { pr_debug("FAILED %s:%d Unexpected number of events for '%s' of %d\n", - __FILE__, __LINE__, str, evlist->core.nr_entries); + __FILE__, __LINE__, str, evlist__nr_entries(evlist)); ret =3D TEST_FAIL; goto out; } diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c index 4ecf5d750313..b3ca73b2d8fc 100644 --- a/tools/perf/tests/topology.c +++ b/tools/perf/tests/topology.c @@ -45,7 +45,7 @@ static int session_write_header(char *path) =20 session->evlist =3D evlist__new_default(&target, /*sample_callchains=3D*/= false); TEST_ASSERT_VAL("can't get evlist", session->evlist); - session->evlist->session =3D session; + evlist__set_session(session->evlist, session); =20 perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY); perf_header__set_feat(&session->header, HEADER_NRCPUS); diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/ann= otate.c index ea17e6d29a7e..99f143a52b5f 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -594,7 +594,7 @@ static bool annotate_browser__callq(struct annotate_bro= wser *browser, notes =3D symbol__annotation(dl->ops.target.sym); annotation__lock(notes); =20 - if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) { + if (!symbol__hists(dl->ops.target.sym, evlist__nr_entries(evsel->evlist))= ) { annotation__unlock(notes); ui__warning("Not enough memory for annotating '%s' symbol!\n", dl->ops.target.sym->name); diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index cfa6386e6e1d..da7cc195b9f4 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -688,10 +688,10 @@ static int hist_browser__handle_hotkey(struct hist_br= owser *browser, bool warn_l ui_browser__update_nr_entries(&browser->b, nr_entries); =20 if (warn_lost_event && - (evsel->evlist->stats.nr_lost_warned !=3D - evsel->evlist->stats.nr_events[PERF_RECORD_LOST])) { - evsel->evlist->stats.nr_lost_warned =3D - evsel->evlist->stats.nr_events[PERF_RECORD_LOST]; + (evlist__stats(evsel->evlist)->nr_lost_warned !=3D + evlist__stats(evsel->evlist)->nr_events[PERF_RECORD_LOST])) { + evlist__stats(evsel->evlist)->nr_lost_warned =3D + evlist__stats(evsel->evlist)->nr_events[PERF_RECORD_LOST]; ui_browser__warn_lost_events(&browser->b); } =20 @@ -3321,7 +3321,7 @@ static int evsel__hists_browse(struct evsel *evsel, i= nt nr_events, const char *h * No need to refresh, resort/decay histogram * entries if we are not collecting samples: */ - if (top->evlist->enabled) { + if (evlist__enabled(top->evlist)) { helpline =3D "Press 'f' to disable the events or 'h' to see other hot= keys"; hbt->refresh =3D delay_secs; } else { @@ -3493,7 +3493,7 @@ static void perf_evsel_menu__write(struct ui_browser = *browser, unit, unit =3D=3D ' ' ? "" : " ", ev_name); ui_browser__printf(browser, "%s", bf); =20 - nr_events =3D evsel->evlist->stats.nr_events[PERF_RECORD_LOST]; + nr_events =3D evlist__stats(evsel->evlist)->nr_events[PERF_RECORD_LOST]; if (nr_events !=3D 0) { menu->lost_events =3D true; if (!current_entry) @@ -3559,13 +3559,13 @@ static int perf_evsel_menu__run(struct evsel_menu *= menu, ui_browser__show_title(&menu->b, title); switch (key) { case K_TAB: - if (pos->core.node.next =3D=3D &evlist->core.entries) + if (pos->core.node.next =3D=3D &evlist__core(evlist)->entries) pos =3D evlist__first(evlist); else pos =3D evsel__next(pos); goto browse_hists; case K_UNTAB: - if (pos->core.node.prev =3D=3D &evlist->core.entries) + if (pos->core.node.prev =3D=3D &evlist__core(evlist)->entries) pos =3D evlist__last(evlist); else pos =3D evsel__prev(pos); @@ -3618,7 +3618,7 @@ static int __evlist__tui_browse_hists(struct evlist *= evlist, int nr_entries, con struct evsel *pos; struct evsel_menu menu =3D { .b =3D { - .entries =3D &evlist->core.entries, + .entries =3D &evlist__core(evlist)->entries, .refresh =3D ui_browser__list_head_refresh, .seek =3D ui_browser__list_head_seek, .write =3D perf_evsel_menu__write, @@ -3646,7 +3646,7 @@ static int __evlist__tui_browse_hists(struct evlist *= evlist, int nr_entries, con =20 static bool evlist__single_entry(struct evlist *evlist) { - int nr_entries =3D evlist->core.nr_entries; + int nr_entries =3D evlist__nr_entries(evlist); =20 if (nr_entries =3D=3D 1) return true; @@ -3664,7 +3664,7 @@ static bool evlist__single_entry(struct evlist *evlis= t) int evlist__tui_browse_hists(struct evlist *evlist, const char *help, stru= ct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env, bool warn_lost_event) { - int nr_entries =3D evlist->core.nr_entries; + int nr_entries =3D evlist__nr_entries(evlist); =20 if (evlist__single_entry(evlist)) { single_entry: { diff --git a/tools/perf/util/amd-sample-raw.c b/tools/perf/util/amd-sample-= raw.c index b084dee76b1a..c64584b0f794 100644 --- a/tools/perf/util/amd-sample-raw.c +++ b/tools/perf/util/amd-sample-raw.c @@ -354,7 +354,7 @@ static void parse_cpuid(struct perf_env *env) */ bool evlist__has_amd_ibs(struct evlist *evlist) { - struct perf_env *env =3D perf_session__env(evlist->session); + struct perf_env *env =3D perf_session__env(evlist__session(evlist)); int ret, nr_pmu_mappings =3D perf_env__nr_pmu_mappings(env); const char *pmu_mapping =3D perf_env__pmu_mappings(env); char name[sizeof("ibs_fetch")]; diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-dat= a.c index 1eff0a27237d..e8949dce37a9 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -1822,7 +1822,7 @@ int annotated_data_type__update_samples(struct annota= ted_data_type *adt, return 0; =20 if (adt->histograms =3D=3D NULL) { - int nr =3D evsel->evlist->core.nr_entries; + int nr =3D evlist__nr_entries(evsel->evlist); =20 if (alloc_data_type_histograms(adt, nr) < 0) return -1; diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index e745f3034a0e..02c1b8deda6b 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -326,7 +326,7 @@ static int symbol__inc_addr_samples(struct map_symbol *= ms, =20 if (sym =3D=3D NULL) return 0; - src =3D symbol__hists(sym, evsel->evlist->core.nr_entries); + src =3D symbol__hists(sym, evlist__nr_entries(evsel->evlist)); return src ? __symbol__inc_addr_samples(ms, src, evsel, addr, sample) : 0; } =20 @@ -337,7 +337,7 @@ static int symbol__account_br_cntr(struct annotated_bra= nch *branch, { unsigned int br_cntr_nr =3D evsel__leader(evsel)->br_cntr_nr; unsigned int base =3D evsel__leader(evsel)->br_cntr_idx; - unsigned int off =3D offset * evsel->evlist->nr_br_cntr; + unsigned int off =3D offset * evlist__nr_br_cntr(evsel->evlist); u64 *branch_br_cntr =3D branch->br_cntr; unsigned int i, mask, width; =20 @@ -367,7 +367,7 @@ static int symbol__account_cycles(u64 addr, u64 start, = struct symbol *sym, =20 if (sym =3D=3D NULL) return 0; - branch =3D symbol__find_branch_hist(sym, evsel->evlist->nr_br_cntr); + branch =3D symbol__find_branch_hist(sym, evlist__nr_br_cntr(evsel->evlist= )); if (!branch) return -ENOMEM; if (addr < sym->start || addr >=3D sym->end) @@ -509,7 +509,7 @@ static void annotation__count_and_fill(struct annotatio= n *notes, u64 start, u64 static int annotation__compute_ipc(struct annotation *notes, size_t size, struct evsel *evsel) { - unsigned int br_cntr_nr =3D evsel->evlist->nr_br_cntr; + unsigned int br_cntr_nr =3D evlist__nr_br_cntr(evsel->evlist); int err =3D 0; s64 offset; =20 @@ -1813,7 +1813,7 @@ int annotation_br_cntr_abbr_list(char **str, struct e= vsel *evsel, bool header) struct evsel *pos; struct strbuf sb; =20 - if (evsel->evlist->nr_br_cntr <=3D 0) + if (evlist__nr_br_cntr(evsel->evlist) <=3D 0) return -ENOTSUP; =20 strbuf_init(&sb, /*hint=3D*/ 0); diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index a224687ffbc1..4d9dfbde7f78 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -191,7 +191,7 @@ void auxtrace_mmap_params__set_idx(struct auxtrace_mmap= _params *mp, struct evlist *evlist, struct evsel *evsel, int idx) { - bool per_cpu =3D !perf_cpu_map__has_any_cpu(evlist->core.user_requested_c= pus); + bool per_cpu =3D !perf_cpu_map__has_any_cpu(evlist__core(evlist)->user_re= quested_cpus); =20 mp->mmap_needed =3D evsel->needs_auxtrace_mmap; =20 @@ -201,11 +201,11 @@ void auxtrace_mmap_params__set_idx(struct auxtrace_mm= ap_params *mp, mp->idx =3D idx; =20 if (per_cpu) { - mp->cpu =3D perf_cpu_map__cpu(evlist->core.all_cpus, idx); - mp->tid =3D perf_thread_map__pid(evlist->core.threads, 0); + mp->cpu =3D perf_cpu_map__cpu(evlist__core(evlist)->all_cpus, idx); + mp->tid =3D perf_thread_map__pid(evlist__core(evlist)->threads, 0); } else { mp->cpu.cpu =3D -1; - mp->tid =3D perf_thread_map__pid(evlist->core.threads, idx); + mp->tid =3D perf_thread_map__pid(evlist__core(evlist)->threads, idx); } } =20 @@ -667,10 +667,10 @@ int auxtrace_parse_snapshot_options(struct auxtrace_r= ecord *itr, =20 static int evlist__enable_event_idx(struct evlist *evlist, struct evsel *e= vsel, int idx) { - bool per_cpu_mmaps =3D !perf_cpu_map__has_any_cpu(evlist->core.user_reque= sted_cpus); + bool per_cpu_mmaps =3D !perf_cpu_map__has_any_cpu(evlist__core(evlist)->u= ser_requested_cpus); =20 if (per_cpu_mmaps) { - struct perf_cpu evlist_cpu =3D perf_cpu_map__cpu(evlist->core.all_cpus, = idx); + struct perf_cpu evlist_cpu =3D perf_cpu_map__cpu(evlist__core(evlist)->a= ll_cpus, idx); int cpu_map_idx =3D perf_cpu_map__idx(evsel->core.cpus, evlist_cpu); =20 if (cpu_map_idx =3D=3D -1) @@ -1806,7 +1806,7 @@ void perf_session__auxtrace_error_inc(struct perf_ses= sion *session, struct perf_record_auxtrace_error *e =3D &event->auxtrace_error; =20 if (e->type < PERF_AUXTRACE_ERROR_MAX) - session->evlist->stats.nr_auxtrace_errors[e->type] +=3D 1; + evlist__stats(session->evlist)->nr_auxtrace_errors[e->type] +=3D 1; } =20 void events_stats__auxtrace_error_warn(const struct events_stats *stats) diff --git a/tools/perf/util/block-info.c b/tools/perf/util/block-info.c index 8d3a9a661f26..1135e54f4c7f 100644 --- a/tools/perf/util/block-info.c +++ b/tools/perf/util/block-info.c @@ -472,7 +472,7 @@ struct block_report *block_info__create_report(struct e= vlist *evlist, int *nr_reps) { struct block_report *block_reports; - int nr_hists =3D evlist->core.nr_entries, i =3D 0; + int nr_hists =3D evlist__nr_entries(evlist), i =3D 0; struct evsel *pos; =20 block_reports =3D calloc(nr_hists, sizeof(struct block_report)); @@ -483,7 +483,7 @@ struct block_report *block_info__create_report(struct e= vlist *evlist, struct hists *hists =3D evsel__hists(pos); =20 process_block_report(hists, &block_reports[i], total_cycles, - block_hpps, nr_hpps, evlist->nr_br_cntr); + block_hpps, nr_hpps, evlist__nr_br_cntr(evlist)); i++; } =20 diff --git a/tools/perf/util/bpf_counter.c b/tools/perf/util/bpf_counter.c index 34b6b0da18b7..9362e45e17ce 100644 --- a/tools/perf/util/bpf_counter.c +++ b/tools/perf/util/bpf_counter.c @@ -443,7 +443,7 @@ static int bperf_check_target(struct evsel *evsel, } else if (target->tid) { *filter_type =3D BPERF_FILTER_PID; *filter_entry_cnt =3D perf_thread_map__nr(evsel->core.threads); - } else if (target->pid || evsel->evlist->workload.pid !=3D -1) { + } else if (target->pid || evlist__workload_pid(evsel->evlist) !=3D -1) { *filter_type =3D BPERF_FILTER_TGID; *filter_entry_cnt =3D perf_thread_map__nr(evsel->core.threads); } else { diff --git a/tools/perf/util/bpf_counter_cgroup.c b/tools/perf/util/bpf_cou= nter_cgroup.c index 339df94ef438..27bb1a41ae4f 100644 --- a/tools/perf/util/bpf_counter_cgroup.c +++ b/tools/perf/util/bpf_counter_cgroup.c @@ -111,7 +111,7 @@ static int bperf_load_program(struct evlist *evlist) pr_err("Failed to open cgroup skeleton\n"); return -1; } - setup_rodata(skel, evlist->core.nr_entries); + setup_rodata(skel, evlist__nr_entries(evlist)); =20 err =3D bperf_cgroup_bpf__load(skel); if (err) { @@ -122,12 +122,12 @@ static int bperf_load_program(struct evlist *evlist) err =3D -1; =20 cgrp_switch =3D evsel__new(&cgrp_switch_attr); - if (evsel__open_per_cpu(cgrp_switch, evlist->core.all_cpus, -1) < 0) { + if (evsel__open_per_cpu(cgrp_switch, evlist__core(evlist)->all_cpus, -1) = < 0) { pr_err("Failed to open cgroup switches event\n"); goto out; } =20 - perf_cpu_map__for_each_cpu(cpu, i, evlist->core.all_cpus) { + perf_cpu_map__for_each_cpu(cpu, i, evlist__core(evlist)->all_cpus) { link =3D bpf_program__attach_perf_event(skel->progs.on_cgrp_switch, FD(cgrp_switch, i)); if (IS_ERR(link)) { @@ -238,7 +238,7 @@ static int bperf_cgrp__sync_counters(struct evlist *evl= ist) unsigned int idx; int prog_fd =3D bpf_program__fd(skel->progs.trigger_read); =20 - perf_cpu_map__for_each_cpu(cpu, idx, evlist->core.all_cpus) + perf_cpu_map__for_each_cpu(cpu, idx, evlist__core(evlist)->all_cpus) bperf_trigger_reading(prog_fd, cpu.cpu); =20 return 0; diff --git a/tools/perf/util/bpf_ftrace.c b/tools/perf/util/bpf_ftrace.c index c456d24efa30..abeafd406e8e 100644 --- a/tools/perf/util/bpf_ftrace.c +++ b/tools/perf/util/bpf_ftrace.c @@ -59,13 +59,13 @@ int perf_ftrace__latency_prepare_bpf(struct perf_ftrace= *ftrace) =20 /* don't need to set cpu filter for system-wide mode */ if (ftrace->target.cpu_list) { - ncpus =3D perf_cpu_map__nr(ftrace->evlist->core.user_requested_cpus); + ncpus =3D perf_cpu_map__nr(evlist__core(ftrace->evlist)->user_requested_= cpus); bpf_map__set_max_entries(skel->maps.cpu_filter, ncpus); skel->rodata->has_cpu =3D 1; } =20 if (target__has_task(&ftrace->target) || target__none(&ftrace->target)) { - ntasks =3D perf_thread_map__nr(ftrace->evlist->core.threads); + ntasks =3D perf_thread_map__nr(evlist__core(ftrace->evlist)->threads); bpf_map__set_max_entries(skel->maps.task_filter, ntasks); skel->rodata->has_task =3D 1; } @@ -87,7 +87,8 @@ int perf_ftrace__latency_prepare_bpf(struct perf_ftrace *= ftrace) fd =3D bpf_map__fd(skel->maps.cpu_filter); =20 for (i =3D 0; i < ncpus; i++) { - cpu =3D perf_cpu_map__cpu(ftrace->evlist->core.user_requested_cpus, i).= cpu; + cpu =3D perf_cpu_map__cpu( + evlist__core(ftrace->evlist)->user_requested_cpus, i).cpu; bpf_map_update_elem(fd, &cpu, &val, BPF_ANY); } } @@ -99,7 +100,7 @@ int perf_ftrace__latency_prepare_bpf(struct perf_ftrace = *ftrace) fd =3D bpf_map__fd(skel->maps.task_filter); =20 for (i =3D 0; i < ntasks; i++) { - pid =3D perf_thread_map__pid(ftrace->evlist->core.threads, i); + pid =3D perf_thread_map__pid(evlist__core(ftrace->evlist)->threads, i); bpf_map_update_elem(fd, &pid, &val, BPF_ANY); } } diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lo= ck_contention.c index cbd7435579fe..85727d154d9c 100644 --- a/tools/perf/util/bpf_lock_contention.c +++ b/tools/perf/util/bpf_lock_contention.c @@ -222,11 +222,11 @@ int lock_contention_prepare(struct lock_contention *c= on) =20 if (target__has_cpu(target)) { skel->rodata->has_cpu =3D 1; - ncpus =3D perf_cpu_map__nr(evlist->core.user_requested_cpus); + ncpus =3D perf_cpu_map__nr(evlist__core(evlist)->user_requested_cpus); } if (target__has_task(target)) { skel->rodata->has_task =3D 1; - ntasks =3D perf_thread_map__nr(evlist->core.threads); + ntasks =3D perf_thread_map__nr(evlist__core(evlist)->threads); } if (con->filters->nr_types) { skel->rodata->has_type =3D 1; @@ -327,7 +327,7 @@ int lock_contention_prepare(struct lock_contention *con) fd =3D bpf_map__fd(skel->maps.cpu_filter); =20 for (i =3D 0; i < ncpus; i++) { - cpu =3D perf_cpu_map__cpu(evlist->core.user_requested_cpus, i).cpu; + cpu =3D perf_cpu_map__cpu(evlist__core(evlist)->user_requested_cpus, i)= .cpu; bpf_map_update_elem(fd, &cpu, &val, BPF_ANY); } } @@ -339,13 +339,13 @@ int lock_contention_prepare(struct lock_contention *c= on) fd =3D bpf_map__fd(skel->maps.task_filter); =20 for (i =3D 0; i < ntasks; i++) { - pid =3D perf_thread_map__pid(evlist->core.threads, i); + pid =3D perf_thread_map__pid(evlist__core(evlist)->threads, i); bpf_map_update_elem(fd, &pid, &val, BPF_ANY); } } =20 - if (target__none(target) && evlist->workload.pid > 0) { - u32 pid =3D evlist->workload.pid; + if (target__none(target) && evlist__workload_pid(evlist) > 0) { + u32 pid =3D evlist__workload_pid(evlist); u8 val =3D 1; =20 fd =3D bpf_map__fd(skel->maps.task_filter); diff --git a/tools/perf/util/bpf_off_cpu.c b/tools/perf/util/bpf_off_cpu.c index 48cb930cdd2e..c4639f6a5776 100644 --- a/tools/perf/util/bpf_off_cpu.c +++ b/tools/perf/util/bpf_off_cpu.c @@ -73,13 +73,13 @@ static void off_cpu_start(void *arg) =20 /* update task filter for the given workload */ if (skel->rodata->has_task && skel->rodata->uses_tgid && - perf_thread_map__pid(evlist->core.threads, 0) !=3D -1) { + perf_thread_map__pid(evlist__core(evlist)->threads, 0) !=3D -1) { int fd; u32 pid; u8 val =3D 1; =20 fd =3D bpf_map__fd(skel->maps.task_filter); - pid =3D perf_thread_map__pid(evlist->core.threads, 0); + pid =3D perf_thread_map__pid(evlist__core(evlist)->threads, 0); bpf_map_update_elem(fd, &pid, &val, BPF_ANY); } =20 @@ -168,7 +168,7 @@ int off_cpu_prepare(struct evlist *evlist, struct targe= t *target, =20 /* don't need to set cpu filter for system-wide mode */ if (target->cpu_list) { - ncpus =3D perf_cpu_map__nr(evlist->core.user_requested_cpus); + ncpus =3D perf_cpu_map__nr(evlist__core(evlist)->user_requested_cpus); bpf_map__set_max_entries(skel->maps.cpu_filter, ncpus); skel->rodata->has_cpu =3D 1; } @@ -199,7 +199,7 @@ int off_cpu_prepare(struct evlist *evlist, struct targe= t *target, skel->rodata->has_task =3D 1; skel->rodata->uses_tgid =3D 1; } else if (target__has_task(target)) { - ntasks =3D perf_thread_map__nr(evlist->core.threads); + ntasks =3D perf_thread_map__nr(evlist__core(evlist)->threads); bpf_map__set_max_entries(skel->maps.task_filter, ntasks); skel->rodata->has_task =3D 1; } else if (target__none(target)) { @@ -209,7 +209,7 @@ int off_cpu_prepare(struct evlist *evlist, struct targe= t *target, } =20 if (evlist__first(evlist)->cgrp) { - ncgrps =3D evlist->core.nr_entries - 1; /* excluding a dummy */ + ncgrps =3D evlist__nr_entries(evlist) - 1; /* excluding a dummy */ bpf_map__set_max_entries(skel->maps.cgroup_filter, ncgrps); =20 if (!cgroup_is_v2("perf_event")) @@ -240,7 +240,7 @@ int off_cpu_prepare(struct evlist *evlist, struct targe= t *target, fd =3D bpf_map__fd(skel->maps.cpu_filter); =20 for (i =3D 0; i < ncpus; i++) { - cpu =3D perf_cpu_map__cpu(evlist->core.user_requested_cpus, i).cpu; + cpu =3D perf_cpu_map__cpu(evlist__core(evlist)->user_requested_cpus, i)= .cpu; bpf_map_update_elem(fd, &cpu, &val, BPF_ANY); } } @@ -269,7 +269,7 @@ int off_cpu_prepare(struct evlist *evlist, struct targe= t *target, fd =3D bpf_map__fd(skel->maps.task_filter); =20 for (i =3D 0; i < ntasks; i++) { - pid =3D perf_thread_map__pid(evlist->core.threads, i); + pid =3D perf_thread_map__pid(evlist__core(evlist)->threads, i); bpf_map_update_elem(fd, &pid, &val, BPF_ANY); } } diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index 914744724467..c7be16a7915e 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -367,7 +367,7 @@ int parse_cgroups(const struct option *opt, const char = *str, char *s; int ret, i; =20 - if (list_empty(&evlist->core.entries)) { + if (list_empty(&evlist__core(evlist)->entries)) { fprintf(stderr, "must define events before cgroups\n"); return -1; } @@ -423,7 +423,7 @@ int evlist__expand_cgroup(struct evlist *evlist, const = char *str, bool open_cgro int ret =3D -1; int prefix_len; =20 - if (evlist->core.nr_entries =3D=3D 0) { + if (evlist__nr_entries(evlist) =3D=3D 0) { fprintf(stderr, "must define events before cgroups\n"); return -EINVAL; } @@ -436,11 +436,11 @@ int evlist__expand_cgroup(struct evlist *evlist, cons= t char *str, bool open_cgro } =20 /* save original events and init evlist */ - evlist__splice_list_tail(orig_list, &evlist->core.entries); - evlist->core.nr_entries =3D 0; + evlist__splice_list_tail(orig_list, &evlist__core(evlist)->entries); + evlist__core(evlist)->nr_entries =3D 0; =20 - orig_metric_events =3D evlist->metric_events; - metricgroup__rblist_init(&evlist->metric_events); + orig_metric_events =3D *evlist__metric_events(evlist); + metricgroup__rblist_init(evlist__metric_events(evlist)); =20 if (has_pattern_string(str)) prefix_len =3D match_cgroups(str); @@ -503,15 +503,15 @@ int evlist__expand_cgroup(struct evlist *evlist, cons= t char *str, bool open_cgro nr_cgroups++; =20 if (metricgroup__copy_metric_events(tmp_list, cgrp, - &evlist->metric_events, + evlist__metric_events(evlist), &orig_metric_events) < 0) goto out_err; =20 - evlist__splice_list_tail(evlist, &tmp_list->core.entries); - tmp_list->core.nr_entries =3D 0; + evlist__splice_list_tail(evlist, &evlist__core(tmp_list)->entries); + evlist__core(tmp_list)->nr_entries =3D 0; } =20 - if (list_empty(&evlist->core.entries)) { + if (list_empty(&evlist__core(evlist)->entries)) { fprintf(stderr, "no cgroup matched: %s\n", str); goto out_err; } diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index a362f338f104..29588af735e5 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -31,6 +31,7 @@ =20 #include #include // page_size +#include #include #include #include @@ -75,30 +76,31 @@ int sigqueue(pid_t pid, int sig, const union sigval val= ue); #define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y)) #define SID(e, x, y) xyarray__entry(e->core.sample_id, x, y) =20 -static void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus, - struct perf_thread_map *threads) -{ - perf_evlist__init(&evlist->core); - perf_evlist__set_maps(&evlist->core, cpus, threads); - evlist->workload.pid =3D -1; - evlist->bkw_mmap_state =3D BKW_MMAP_NOTREADY; - evlist->ctl_fd.fd =3D -1; - evlist->ctl_fd.ack =3D -1; - evlist->ctl_fd.pos =3D -1; - evlist->nr_br_cntr =3D -1; - metricgroup__rblist_init(&evlist->metric_events); - INIT_LIST_HEAD(&evlist->deferred_samples); - refcount_set(&evlist->refcnt, 1); -} +static void event_enable_timer__exit(struct event_enable_timer **ep); =20 struct evlist *evlist__new(void) { - struct evlist *evlist =3D zalloc(sizeof(*evlist)); - - if (evlist !=3D NULL) - evlist__init(evlist, NULL, NULL); - - return evlist; + struct evlist *result; + RC_STRUCT(evlist) *evlist; + + evlist =3D zalloc(sizeof(*evlist)); + if (ADD_RC_CHK(result, evlist)) { + perf_evlist__init(evlist__core(result)); + perf_evlist__set_maps(evlist__core(result), /*cpus=3D*/NULL, /*threads= =3D*/NULL); + evlist__set_workload_pid(result, -1); + evlist__set_bkw_mmap_state(result, BKW_MMAP_NOTREADY); + evlist__set_ctl_fd_fd(result, -1); + evlist__set_ctl_fd_ack(result, -1); + evlist__set_ctl_fd_pos(result, -1); + evlist__set_nr_br_cntr(result, -1); + metricgroup__rblist_init(evlist__metric_events(result)); + INIT_LIST_HEAD(&evlist->deferred_samples); + refcount_set(evlist__refcnt(result), 1); + } else { + free(evlist); + result =3D NULL; + } + return result; } =20 struct evlist *evlist__new_default(const struct target *target, bool sampl= e_callchains) @@ -106,7 +108,6 @@ struct evlist *evlist__new_default(const struct target = *target, bool sample_call struct evlist *evlist =3D evlist__new(); bool can_profile_kernel; struct perf_pmu *pmu =3D NULL; - struct evsel *evsel; char buf[256]; int err; =20 @@ -133,7 +134,9 @@ struct evlist *evlist__new_default(const struct target = *target, bool sample_call } =20 /* If there is only 1 event a sample identifier isn't necessary. */ - if (evlist->core.nr_entries > 1) { + if (evlist__nr_entries(evlist) > 1) { + struct evsel *evsel; + evlist__for_each_entry(evlist, evsel) evsel__set_sample_id(evsel, /*can_sample_identifier=3D*/false); } @@ -158,8 +161,12 @@ struct evlist *evlist__new_dummy(void) =20 struct evlist *evlist__get(struct evlist *evlist) { - refcount_inc(&evlist->refcnt); - return evlist; + struct evlist *result; + + if (RC_CHK_GET(result, evlist)) + refcount_inc(evlist__refcnt(evlist)); + + return result; } =20 /** @@ -173,8 +180,8 @@ void evlist__set_id_pos(struct evlist *evlist) { struct evsel *first =3D evlist__first(evlist); =20 - evlist->id_pos =3D first->id_pos; - evlist->is_pos =3D first->is_pos; + RC_CHK_ACCESS(evlist)->id_pos =3D first->id_pos; + RC_CHK_ACCESS(evlist)->is_pos =3D first->is_pos; } =20 static void evlist__update_id_pos(struct evlist *evlist) @@ -193,52 +200,76 @@ static void evlist__purge(struct evlist *evlist) =20 evlist__for_each_entry_safe(evlist, n, pos) { list_del_init(&pos->core.node); + if (pos->evlist) { + /* Minimal evlist__put. */ + refcount_dec_and_test(evlist__refcnt(pos->evlist)); + RC_CHK_PUT(pos->evlist); + } pos->evlist =3D NULL; evsel__put(pos); } =20 - evlist->core.nr_entries =3D 0; + evlist__core(evlist)->nr_entries =3D 0; } =20 static void evlist__exit(struct evlist *evlist) { - metricgroup__rblist_exit(&evlist->metric_events); - event_enable_timer__exit(&evlist->eet); - zfree(&evlist->mmap); - zfree(&evlist->overwrite_mmap); - perf_evlist__exit(&evlist->core); + metricgroup__rblist_exit(evlist__metric_events(evlist)); + event_enable_timer__exit(&RC_CHK_ACCESS(evlist)->eet); + free(evlist__mmap(evlist)); + free(evlist__overwrite_mmap(evlist)); + perf_evlist__exit(evlist__core(evlist)); } =20 void evlist__put(struct evlist *evlist) { + struct evsel *evsel; + unsigned int count; + if (evlist =3D=3D NULL) return; =20 - if (!refcount_dec_and_test(&evlist->refcnt)) - return; + if (refcount_dec_and_test(evlist__refcnt(evlist))) + goto out_delete; =20 + count =3D refcount_read(evlist__refcnt(evlist)); + evlist__for_each_entry(evlist, evsel) { + if (RC_CHK_EQUAL(evsel->evlist, evlist) && count) + count--; + } + if (count !=3D 0) { + /* + * Not the last reference except for back references from + * evsels. + */ + RC_CHK_PUT(evlist); + return; + } +out_delete: evlist__free_stats(evlist); - evlist__munmap(evlist); + evlist__do_munmap(evlist); evlist__close(evlist); evlist__purge(evlist); evlist__exit(evlist); - free(evlist); + RC_CHK_FREE(evlist); } =20 void evlist__add(struct evlist *evlist, struct evsel *entry) { - perf_evlist__add(&evlist->core, &entry->core); - entry->evlist =3D evlist; + perf_evlist__add(evlist__core(evlist), &entry->core); + evlist__put(entry->evlist); + entry->evlist =3D evlist__get(evlist); entry->tracking =3D !entry->core.idx; =20 - if (evlist->core.nr_entries =3D=3D 1) + if (evlist__nr_entries(evlist) =3D=3D 1) evlist__set_id_pos(evlist); } =20 void evlist__remove(struct evlist *evlist, struct evsel *evsel) { + perf_evlist__remove(evlist__core(evlist), &evsel->core); + evlist__put(evsel->evlist); evsel->evlist =3D NULL; - perf_evlist__remove(&evlist->core, &evsel->core); } =20 void evlist__splice_list_tail(struct evlist *evlist, struct list_head *lis= t) @@ -287,7 +318,7 @@ int __evlist__set_tracepoints_handlers(struct evlist *e= vlist, =20 static void evlist__set_leader(struct evlist *evlist) { - perf_evlist__set_leader(&evlist->core); + perf_evlist__set_leader(evlist__core(evlist)); } =20 static struct evsel *evlist__dummy_event(struct evlist *evlist) @@ -301,7 +332,7 @@ static struct evsel *evlist__dummy_event(struct evlist = *evlist) .sample_period =3D 1, }; =20 - return evsel__new_idx(&attr, evlist->core.nr_entries); + return evsel__new_idx(&attr, evlist__nr_entries(evlist)); } =20 int evlist__add_dummy(struct evlist *evlist) @@ -390,8 +421,8 @@ static bool evlist__use_affinity(struct evlist *evlist) struct perf_cpu_map *used_cpus =3D NULL; bool ret =3D false; =20 - if (evlist->no_affinity || !evlist->core.user_requested_cpus || - cpu_map__is_dummy(evlist->core.user_requested_cpus)) + if (evlist__no_affinity(evlist) || !evlist__core(evlist)->user_requested_= cpus || + cpu_map__is_dummy(evlist__core(evlist)->user_requested_cpus)) return false; =20 evlist__for_each_entry(evlist, pos) { @@ -446,7 +477,7 @@ void evlist_cpu_iterator__init(struct evlist_cpu_iterat= or *itr, struct evlist *e .evsel =3D NULL, .cpu_map_idx =3D 0, .evlist_cpu_map_idx =3D 0, - .evlist_cpu_map_nr =3D perf_cpu_map__nr(evlist->core.all_cpus), + .evlist_cpu_map_nr =3D perf_cpu_map__nr(evlist__core(evlist)->all_cpus), .cpu =3D (struct perf_cpu){ .cpu =3D -1}, .affinity =3D NULL, }; @@ -462,7 +493,7 @@ void evlist_cpu_iterator__init(struct evlist_cpu_iterat= or *itr, struct evlist *e itr->affinity =3D &itr->saved_affinity; } itr->evsel =3D evlist__first(evlist); - itr->cpu =3D perf_cpu_map__cpu(evlist->core.all_cpus, 0); + itr->cpu =3D perf_cpu_map__cpu(evlist__core(evlist)->all_cpus, 0); if (itr->affinity) affinity__set(itr->affinity, itr->cpu.cpu); itr->cpu_map_idx =3D perf_cpu_map__idx(itr->evsel->core.cpus, itr->cpu); @@ -497,7 +528,7 @@ void evlist_cpu_iterator__next(struct evlist_cpu_iterat= or *evlist_cpu_itr) if (evlist_cpu_itr->evlist_cpu_map_idx < evlist_cpu_itr->evlist_cpu_map_n= r) { evlist_cpu_itr->evsel =3D evlist__first(evlist_cpu_itr->container); evlist_cpu_itr->cpu =3D - perf_cpu_map__cpu(evlist_cpu_itr->container->core.all_cpus, + perf_cpu_map__cpu(evlist__core(evlist_cpu_itr->container)->all_cpus, evlist_cpu_itr->evlist_cpu_map_idx); if (evlist_cpu_itr->affinity) affinity__set(evlist_cpu_itr->affinity, evlist_cpu_itr->cpu.cpu); @@ -524,7 +555,7 @@ static int evsel__strcmp(struct evsel *pos, char *evsel= _name) return !evsel__name_is(pos, evsel_name); } =20 -static int evlist__is_enabled(struct evlist *evlist) +static bool evlist__is_enabled(struct evlist *evlist) { struct evsel *pos; =20 @@ -578,10 +609,7 @@ static void __evlist__disable(struct evlist *evlist, c= har *evsel_name, bool excl * If we disabled only single event, we need to check * the enabled state of the evlist manually. */ - if (evsel_name) - evlist->enabled =3D evlist__is_enabled(evlist); - else - evlist->enabled =3D false; + evlist__set_enabled(evlist, evsel_name ? evlist__is_enabled(evlist) : fal= se); } =20 void evlist__disable(struct evlist *evlist) @@ -629,7 +657,7 @@ static void __evlist__enable(struct evlist *evlist, cha= r *evsel_name, bool excl_ * so the toggle can work properly and toggle to * 'disabled' state. */ - evlist->enabled =3D true; + evlist__set_enabled(evlist, true); } =20 void evlist__enable(struct evlist *evlist) @@ -649,23 +677,24 @@ void evlist__enable_evsel(struct evlist *evlist, char= *evsel_name) =20 void evlist__toggle_enable(struct evlist *evlist) { - (evlist->enabled ? evlist__disable : evlist__enable)(evlist); + (evlist__enabled(evlist) ? evlist__disable : evlist__enable)(evlist); } =20 int evlist__add_pollfd(struct evlist *evlist, int fd) { - return perf_evlist__add_pollfd(&evlist->core, fd, NULL, POLLIN, fdarray_f= lag__default); + return perf_evlist__add_pollfd(evlist__core(evlist), fd, NULL, POLLIN, + fdarray_flag__default); } =20 int evlist__filter_pollfd(struct evlist *evlist, short revents_and_mask) { - return perf_evlist__filter_pollfd(&evlist->core, revents_and_mask); + return perf_evlist__filter_pollfd(evlist__core(evlist), revents_and_mask); } =20 #ifdef HAVE_EVENTFD_SUPPORT int evlist__add_wakeup_eventfd(struct evlist *evlist, int fd) { - return perf_evlist__add_pollfd(&evlist->core, fd, NULL, POLLIN, + return perf_evlist__add_pollfd(evlist__core(evlist), fd, NULL, POLLIN, fdarray_flag__nonfilterable | fdarray_flag__non_perf_event); } @@ -673,7 +702,7 @@ int evlist__add_wakeup_eventfd(struct evlist *evlist, i= nt fd) =20 int evlist__poll(struct evlist *evlist, int timeout) { - return perf_evlist__poll(&evlist->core, timeout); + return perf_evlist__poll(evlist__core(evlist), timeout); } =20 struct perf_sample_id *evlist__id2sid(struct evlist *evlist, u64 id) @@ -683,7 +712,7 @@ struct perf_sample_id *evlist__id2sid(struct evlist *ev= list, u64 id) int hash; =20 hash =3D hash_64(id, PERF_EVLIST__HLIST_BITS); - head =3D &evlist->core.heads[hash]; + head =3D &evlist__core(evlist)->heads[hash]; =20 hlist_for_each_entry(sid, head, node) if (sid->id =3D=3D id) @@ -696,7 +725,7 @@ struct evsel *evlist__id2evsel(struct evlist *evlist, u= 64 id) { struct perf_sample_id *sid; =20 - if (evlist->core.nr_entries =3D=3D 1 || !id) + if (evlist__nr_entries(evlist) =3D=3D 1 || !id) return evlist__first(evlist); =20 sid =3D evlist__id2sid(evlist, id); @@ -731,13 +760,13 @@ static int evlist__event2id(struct evlist *evlist, un= ion perf_event *event, u64 n =3D (event->header.size - sizeof(event->header)) >> 3; =20 if (event->header.type =3D=3D PERF_RECORD_SAMPLE) { - if (evlist->id_pos >=3D n) + if (evlist__id_pos(evlist) >=3D n) return -1; - *id =3D array[evlist->id_pos]; + *id =3D array[evlist__id_pos(evlist)]; } else { - if (evlist->is_pos > n) + if (evlist__is_pos(evlist) > n) return -1; - n -=3D evlist->is_pos; + n -=3D evlist__is_pos(evlist); *id =3D array[n]; } return 0; @@ -751,7 +780,7 @@ struct evsel *evlist__event2evsel(struct evlist *evlist= , union perf_event *event int hash; u64 id; =20 - if (evlist->core.nr_entries =3D=3D 1) + if (evlist__nr_entries(evlist) =3D=3D 1) return first; =20 if (!first->core.attr.sample_id_all && @@ -766,7 +795,7 @@ struct evsel *evlist__event2evsel(struct evlist *evlist= , union perf_event *event return first; =20 hash =3D hash_64(id, PERF_EVLIST__HLIST_BITS); - head =3D &evlist->core.heads[hash]; + head =3D &evlist__core(evlist)->heads[hash]; =20 hlist_for_each_entry(sid, head, node) { if (sid->id =3D=3D id) @@ -779,11 +808,11 @@ static int evlist__set_paused(struct evlist *evlist, = bool value) { int i; =20 - if (!evlist->overwrite_mmap) + if (!evlist__overwrite_mmap(evlist)) return 0; =20 - for (i =3D 0; i < evlist->core.nr_mmaps; i++) { - int fd =3D evlist->overwrite_mmap[i].core.fd; + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) { + int fd =3D evlist__overwrite_mmap(evlist)[i].core.fd; int err; =20 if (fd < 0) @@ -809,20 +838,20 @@ static void evlist__munmap_nofree(struct evlist *evli= st) { int i; =20 - if (evlist->mmap) - for (i =3D 0; i < evlist->core.nr_mmaps; i++) - perf_mmap__munmap(&evlist->mmap[i].core); + if (evlist__mmap(evlist)) + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) + perf_mmap__munmap(&evlist__mmap(evlist)[i].core); =20 - if (evlist->overwrite_mmap) - for (i =3D 0; i < evlist->core.nr_mmaps; i++) - perf_mmap__munmap(&evlist->overwrite_mmap[i].core); + if (evlist__overwrite_mmap(evlist)) + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) + perf_mmap__munmap(&evlist__overwrite_mmap(evlist)[i].core); } =20 -void evlist__munmap(struct evlist *evlist) +void evlist__do_munmap(struct evlist *evlist) { evlist__munmap_nofree(evlist); - zfree(&evlist->mmap); - zfree(&evlist->overwrite_mmap); + zfree(&RC_CHK_ACCESS(evlist)->mmap); + zfree(&RC_CHK_ACCESS(evlist)->overwrite_mmap); } =20 static void perf_mmap__unmap_cb(struct perf_mmap *map) @@ -836,12 +865,12 @@ static struct mmap *evlist__alloc_mmap(struct evlist = *evlist, bool overwrite) { int i; - struct mmap *map =3D calloc(evlist->core.nr_mmaps, sizeof(struct mmap)); + struct mmap *map =3D calloc(evlist__core(evlist)->nr_mmaps, sizeof(struct= mmap)); =20 if (!map) return NULL; =20 - for (i =3D 0; i < evlist->core.nr_mmaps; i++) { + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) { struct perf_mmap *prev =3D i ? &map[i - 1].core : NULL; =20 /* @@ -859,41 +888,73 @@ static struct mmap *evlist__alloc_mmap(struct evlist = *evlist, return map; } =20 +static struct evlist *from_list_start(struct perf_evlist *core) +{ +#ifdef REFCNT_CHECKING + RC_STRUCT(evlist) *core_evlist =3D container_of(core, RC_STRUCT(evlist), = core); + struct evlist *evlist; + + if (ADD_RC_CHK(evlist, core_evlist)) + refcount_inc(evlist__refcnt(evlist)); + + return evlist; +#else + return container_of(core, struct evlist, core); +#endif +} + +static void from_list_end(struct evlist *evlist __maybe_unused) +{ +#ifdef REFCNT_CHECKING + evlist__put(evlist); +#endif +} + static void perf_evlist__mmap_cb_idx(struct perf_evlist *_evlist, struct perf_evsel *_evsel, struct perf_mmap_param *_mp, int idx) { - struct evlist *evlist =3D container_of(_evlist, struct evlist, core); + struct evlist *evlist =3D from_list_start(_evlist); struct mmap_params *mp =3D container_of(_mp, struct mmap_params, core); struct evsel *evsel =3D container_of(_evsel, struct evsel, core); =20 + if (!evlist) + return; + auxtrace_mmap_params__set_idx(&mp->auxtrace_mp, evlist, evsel, idx); + + from_list_end(evlist); } =20 static struct perf_mmap* perf_evlist__mmap_cb_get(struct perf_evlist *_evlist, bool overwrite, int = idx) { - struct evlist *evlist =3D container_of(_evlist, struct evlist, core); + struct evlist *evlist =3D from_list_start(_evlist); struct mmap *maps; =20 - maps =3D overwrite ? evlist->overwrite_mmap : evlist->mmap; + if (!evlist) + return NULL; + + maps =3D overwrite ? evlist__overwrite_mmap(evlist) : evlist__mmap(evlist= ); =20 if (!maps) { maps =3D evlist__alloc_mmap(evlist, overwrite); - if (!maps) + if (!maps) { + from_list_end(evlist); return NULL; + } =20 if (overwrite) { - evlist->overwrite_mmap =3D maps; - if (evlist->bkw_mmap_state =3D=3D BKW_MMAP_NOTREADY) + RC_CHK_ACCESS(evlist)->overwrite_mmap =3D maps; + if (evlist__bkw_mmap_state(evlist) =3D=3D BKW_MMAP_NOTREADY) evlist__toggle_bkw_mmap(evlist, BKW_MMAP_RUNNING); } else { - evlist->mmap =3D maps; + RC_CHK_ACCESS(evlist)->mmap =3D maps; } } - + from_list_end(evlist); return &maps[idx].core; } =20 @@ -1050,16 +1111,16 @@ int evlist__mmap_ex(struct evlist *evlist, unsigned= int pages, .mmap =3D perf_evlist__mmap_cb_mmap, }; =20 - evlist->core.mmap_len =3D evlist__mmap_size(pages); - pr_debug("mmap size %zuB\n", evlist->core.mmap_len); + evlist__core(evlist)->mmap_len =3D evlist__mmap_size(pages); + pr_debug("mmap size %zuB\n", evlist__core(evlist)->mmap_len); =20 - auxtrace_mmap_params__init(&mp.auxtrace_mp, evlist->core.mmap_len, + auxtrace_mmap_params__init(&mp.auxtrace_mp, evlist__core(evlist)->mmap_le= n, auxtrace_pages, auxtrace_overwrite); =20 - return perf_evlist__mmap_ops(&evlist->core, &ops, &mp.core); + return perf_evlist__mmap_ops(evlist__core(evlist), &ops, &mp.core); } =20 -int evlist__mmap(struct evlist *evlist, unsigned int pages) +int evlist__do_mmap(struct evlist *evlist, unsigned int pages) { return evlist__mmap_ex(evlist, pages, 0, false, 0, PERF_AFFINITY_SYS, 1, = 0); } @@ -1101,9 +1162,9 @@ int evlist__create_maps(struct evlist *evlist, struct= target *target) if (!cpus) goto out_delete_threads; =20 - evlist->core.has_user_cpus =3D !!target->cpu_list; + evlist__core(evlist)->has_user_cpus =3D !!target->cpu_list; =20 - perf_evlist__set_maps(&evlist->core, cpus, threads); + perf_evlist__set_maps(evlist__core(evlist), cpus, threads); =20 /* as evlist now has references, put count here */ perf_cpu_map__put(cpus); @@ -1243,15 +1304,15 @@ bool evlist__valid_sample_type(struct evlist *evlis= t) { struct evsel *pos; =20 - if (evlist->core.nr_entries =3D=3D 1) + if (evlist__nr_entries(evlist) =3D=3D 1) return true; =20 - if (evlist->id_pos < 0 || evlist->is_pos < 0) + if (evlist__id_pos(evlist) < 0 || evlist__is_pos(evlist) < 0) return false; =20 evlist__for_each_entry(evlist, pos) { - if (pos->id_pos !=3D evlist->id_pos || - pos->is_pos !=3D evlist->is_pos) + if (pos->id_pos !=3D evlist__id_pos(evlist) || + pos->is_pos !=3D evlist__is_pos(evlist)) return false; } =20 @@ -1262,18 +1323,18 @@ u64 __evlist__combined_sample_type(struct evlist *e= vlist) { struct evsel *evsel; =20 - if (evlist->combined_sample_type) - return evlist->combined_sample_type; + if (RC_CHK_ACCESS(evlist)->combined_sample_type) + return RC_CHK_ACCESS(evlist)->combined_sample_type; =20 evlist__for_each_entry(evlist, evsel) - evlist->combined_sample_type |=3D evsel->core.attr.sample_type; + RC_CHK_ACCESS(evlist)->combined_sample_type |=3D evsel->core.attr.sample= _type; =20 - return evlist->combined_sample_type; + return RC_CHK_ACCESS(evlist)->combined_sample_type; } =20 u64 evlist__combined_sample_type(struct evlist *evlist) { - evlist->combined_sample_type =3D 0; + RC_CHK_ACCESS(evlist)->combined_sample_type =3D 0; return __evlist__combined_sample_type(evlist); } =20 @@ -1350,7 +1411,7 @@ void evlist__update_br_cntr(struct evlist *evlist) evlist__new_abbr_name(evsel->abbr_name); } } - evlist->nr_br_cntr =3D i; + evlist__set_nr_br_cntr(evlist, i); } =20 bool evlist__valid_read_format(struct evlist *evlist) @@ -1400,11 +1461,6 @@ bool evlist__sample_id_all(struct evlist *evlist) return first->core.attr.sample_id_all; } =20 -void evlist__set_selected(struct evlist *evlist, struct evsel *evsel) -{ - evlist->selected =3D evsel; -} - void evlist__close(struct evlist *evlist) { struct evsel *evsel; @@ -1421,7 +1477,7 @@ void evlist__close(struct evlist *evlist) perf_evsel__free_fd(&evsel->core); perf_evsel__free_id(&evsel->core); } - perf_evlist__reset_id_hash(&evlist->core); + perf_evlist__reset_id_hash(evlist__core(evlist)); } =20 static int evlist__create_syswide_maps(struct evlist *evlist) @@ -1448,7 +1504,7 @@ static int evlist__create_syswide_maps(struct evlist = *evlist) return -ENOMEM; } =20 - perf_evlist__set_maps(&evlist->core, cpus, threads); + perf_evlist__set_maps(evlist__core(evlist), cpus, threads); perf_thread_map__put(threads); perf_cpu_map__put(cpus); return 0; @@ -1463,7 +1519,8 @@ int evlist__open(struct evlist *evlist) * Default: one fd per CPU, all threads, aka systemwide * as sys_perf_event_open(cpu =3D -1, thread =3D -1) is EINVAL */ - if (evlist->core.threads =3D=3D NULL && evlist->core.user_requested_cpus = =3D=3D NULL) { + if (evlist__core(evlist)->threads =3D=3D NULL && + evlist__core(evlist)->user_requested_cpus =3D=3D NULL) { err =3D evlist__create_syswide_maps(evlist); if (err < 0) goto out_err; @@ -1490,7 +1547,7 @@ int evlist__prepare_workload(struct evlist *evlist, s= truct target *target, const int child_ready_pipe[2], go_pipe[2]; char bf; =20 - evlist->workload.cork_fd =3D -1; + evlist__set_workload_cork_fd(evlist, -1); =20 if (pipe(child_ready_pipe) < 0) { perror("failed to create 'ready' pipe"); @@ -1502,13 +1559,13 @@ int evlist__prepare_workload(struct evlist *evlist,= struct target *target, const goto out_close_ready_pipe; } =20 - evlist->workload.pid =3D fork(); - if (evlist->workload.pid < 0) { + evlist__set_workload_pid(evlist, fork()); + if (evlist__workload_pid(evlist) < 0) { perror("failed to fork"); goto out_close_pipes; } =20 - if (!evlist->workload.pid) { + if (!evlist__workload_pid(evlist)) { int ret; =20 if (pipe_output) @@ -1574,12 +1631,13 @@ int evlist__prepare_workload(struct evlist *evlist,= struct target *target, const } =20 if (target__none(target)) { - if (evlist->core.threads =3D=3D NULL) { + if (evlist__core(evlist)->threads =3D=3D NULL) { fprintf(stderr, "FATAL: evlist->threads need to be set at this point (%= s:%d).\n", __func__, __LINE__); goto out_close_pipes; } - perf_thread_map__set_pid(evlist->core.threads, 0, evlist->workload.pid); + perf_thread_map__set_pid(evlist__core(evlist)->threads, 0, + evlist__workload_pid(evlist)); } =20 close(child_ready_pipe[1]); @@ -1593,7 +1651,7 @@ int evlist__prepare_workload(struct evlist *evlist, s= truct target *target, const } =20 fcntl(go_pipe[1], F_SETFD, FD_CLOEXEC); - evlist->workload.cork_fd =3D go_pipe[1]; + evlist__set_workload_cork_fd(evlist, go_pipe[1]); close(child_ready_pipe[0]); return 0; =20 @@ -1608,18 +1666,18 @@ int evlist__prepare_workload(struct evlist *evlist,= struct target *target, const =20 int evlist__start_workload(struct evlist *evlist) { - if (evlist->workload.cork_fd >=3D 0) { + if (evlist__workload_cork_fd(evlist) >=3D 0) { char bf =3D 0; int ret; /* * Remove the cork, let it rip! */ - ret =3D write(evlist->workload.cork_fd, &bf, 1); + ret =3D write(evlist__workload_cork_fd(evlist), &bf, 1); if (ret < 0) perror("unable to write to pipe"); =20 - close(evlist->workload.cork_fd); - evlist->workload.cork_fd =3D -1; + close(evlist__workload_cork_fd(evlist)); + evlist__set_workload_cork_fd(evlist, -1); return ret; } =20 @@ -1630,10 +1688,10 @@ void evlist__cancel_workload(struct evlist *evlist) { int status; =20 - if (evlist->workload.cork_fd >=3D 0) { - close(evlist->workload.cork_fd); - evlist->workload.cork_fd =3D -1; - waitpid(evlist->workload.pid, &status, WNOHANG); + if (evlist__workload_cork_fd(evlist) >=3D 0) { + close(evlist__workload_cork_fd(evlist)); + evlist__set_workload_cork_fd(evlist, -1); + waitpid(evlist__workload_pid(evlist), &status, WNOHANG); } } =20 @@ -1727,7 +1785,8 @@ int evlist__strerror_open(struct evlist *evlist, int = err, char *buf, size_t size =20 int evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_= t size) { - int pages_attempted =3D evlist->core.mmap_len / 1024, pages_max_per_user,= printed =3D 0; + int pages_attempted =3D evlist__core(evlist)->mmap_len / 1024; + int pages_max_per_user, printed =3D 0; =20 switch (err) { case EPERM: @@ -1770,7 +1829,7 @@ void evlist__to_front(struct evlist *evlist, struct e= vsel *move_evsel) list_move_tail(&evsel->core.node, &move); } =20 - list_splice(&move, &evlist->core.entries); + list_splice(&move, &evlist__core(evlist)->entries); } =20 struct evsel *evlist__get_tracking_event(struct evlist *evlist) @@ -1812,7 +1871,7 @@ struct evsel *evlist__findnew_tracking_event(struct e= vlist *evlist, bool system_ =20 evlist__set_tracking_event(evlist, evsel); } else if (system_wide) { - perf_evlist__go_system_wide(&evlist->core, &evsel->core); + perf_evlist__go_system_wide(evlist__core(evlist), &evsel->core); } =20 return evsel; @@ -1834,14 +1893,14 @@ struct evsel *evlist__find_evsel_by_str(struct evli= st *evlist, const char *str) =20 void evlist__toggle_bkw_mmap(struct evlist *evlist, enum bkw_mmap_state st= ate) { - enum bkw_mmap_state old_state =3D evlist->bkw_mmap_state; + enum bkw_mmap_state old_state =3D evlist__bkw_mmap_state(evlist); enum action { NONE, PAUSE, RESUME, } action =3D NONE; =20 - if (!evlist->overwrite_mmap) + if (!evlist__overwrite_mmap(evlist)) return; =20 switch (old_state) { @@ -1871,7 +1930,7 @@ void evlist__toggle_bkw_mmap(struct evlist *evlist, e= num bkw_mmap_state state) WARN_ONCE(1, "Shouldn't get there\n"); } =20 - evlist->bkw_mmap_state =3D state; + evlist__set_bkw_mmap_state(evlist, state); =20 switch (action) { case PAUSE: @@ -2049,40 +2108,41 @@ int evlist__initialize_ctlfd(struct evlist *evlist,= int fd, int ack) return 0; } =20 - evlist->ctl_fd.pos =3D perf_evlist__add_pollfd(&evlist->core, fd, NULL, P= OLLIN, - fdarray_flag__nonfilterable | - fdarray_flag__non_perf_event); - if (evlist->ctl_fd.pos < 0) { - evlist->ctl_fd.pos =3D -1; + evlist__set_ctl_fd_pos(evlist, + perf_evlist__add_pollfd(evlist__core(evlist), fd, NULL, POLLIN, + fdarray_flag__nonfilterable | + fdarray_flag__non_perf_event)); + if (evlist__ctl_fd_pos(evlist) < 0) { + evlist__set_ctl_fd_pos(evlist, -1); pr_err("Failed to add ctl fd entry: %m\n"); return -1; } =20 - evlist->ctl_fd.fd =3D fd; - evlist->ctl_fd.ack =3D ack; + evlist__set_ctl_fd_fd(evlist, fd); + evlist__set_ctl_fd_ack(evlist, ack); =20 return 0; } =20 bool evlist__ctlfd_initialized(struct evlist *evlist) { - return evlist->ctl_fd.pos >=3D 0; + return evlist__ctl_fd_pos(evlist) >=3D 0; } =20 int evlist__finalize_ctlfd(struct evlist *evlist) { - struct pollfd *entries =3D evlist->core.pollfd.entries; + struct pollfd *entries =3D evlist__core(evlist)->pollfd.entries; =20 if (!evlist__ctlfd_initialized(evlist)) return 0; =20 - entries[evlist->ctl_fd.pos].fd =3D -1; - entries[evlist->ctl_fd.pos].events =3D 0; - entries[evlist->ctl_fd.pos].revents =3D 0; + entries[evlist__ctl_fd_pos(evlist)].fd =3D -1; + entries[evlist__ctl_fd_pos(evlist)].events =3D 0; + entries[evlist__ctl_fd_pos(evlist)].revents =3D 0; =20 - evlist->ctl_fd.pos =3D -1; - evlist->ctl_fd.ack =3D -1; - evlist->ctl_fd.fd =3D -1; + evlist__set_ctl_fd_pos(evlist, -1); + evlist__set_ctl_fd_ack(evlist, -1); + evlist__set_ctl_fd_fd(evlist, -1); =20 return 0; } @@ -2099,7 +2159,7 @@ static int evlist__ctlfd_recv(struct evlist *evlist, = enum evlist_ctl_cmd *cmd, data_size--; =20 do { - err =3D read(evlist->ctl_fd.fd, &c, 1); + err =3D read(evlist__ctl_fd_fd(evlist), &c, 1); if (err > 0) { if (c =3D=3D '\n' || c =3D=3D '\0') break; @@ -2113,7 +2173,8 @@ static int evlist__ctlfd_recv(struct evlist *evlist, = enum evlist_ctl_cmd *cmd, if (errno =3D=3D EAGAIN || errno =3D=3D EWOULDBLOCK) err =3D 0; else - pr_err("Failed to read from ctlfd %d: %m\n", evlist->ctl_fd.fd); + pr_err("Failed to read from ctlfd %d: %m\n", + evlist__ctl_fd_fd(evlist)); } break; } while (1); @@ -2151,13 +2212,13 @@ int evlist__ctlfd_ack(struct evlist *evlist) { int err; =20 - if (evlist->ctl_fd.ack =3D=3D -1) + if (evlist__ctl_fd_ack(evlist) =3D=3D -1) return 0; =20 - err =3D write(evlist->ctl_fd.ack, EVLIST_CTL_CMD_ACK_TAG, + err =3D write(evlist__ctl_fd_ack(evlist), EVLIST_CTL_CMD_ACK_TAG, sizeof(EVLIST_CTL_CMD_ACK_TAG)); if (err =3D=3D -1) - pr_err("failed to write to ctl_ack_fd %d: %m\n", evlist->ctl_fd.ack); + pr_err("failed to write to ctl_ack_fd %d: %m\n", evlist__ctl_fd_ack(evli= st)); =20 return err; } @@ -2258,8 +2319,8 @@ int evlist__ctlfd_process(struct evlist *evlist, enum= evlist_ctl_cmd *cmd) { int err =3D 0; char cmd_data[EVLIST_CTL_CMD_MAX_LEN]; - int ctlfd_pos =3D evlist->ctl_fd.pos; - struct pollfd *entries =3D evlist->core.pollfd.entries; + int ctlfd_pos =3D evlist__ctl_fd_pos(evlist); + struct pollfd *entries =3D evlist__core(evlist)->pollfd.entries; =20 if (!evlist__ctlfd_initialized(evlist) || !entries[ctlfd_pos].revents) return 0; @@ -2430,14 +2491,15 @@ int evlist__parse_event_enable_time(struct evlist *= evlist, struct record_opts *o goto free_eet_times; } =20 - eet->pollfd_pos =3D perf_evlist__add_pollfd(&evlist->core, eet->timerfd, = NULL, POLLIN, flags); + eet->pollfd_pos =3D perf_evlist__add_pollfd(evlist__core(evlist), eet->ti= merfd, + NULL, POLLIN, flags); if (eet->pollfd_pos < 0) { err =3D eet->pollfd_pos; goto close_timerfd; } =20 eet->evlist =3D evlist; - evlist->eet =3D eet; + RC_CHK_ACCESS(evlist)->eet =3D eet; opts->target.initial_delay =3D eet->times[0].start; =20 return 0; @@ -2487,7 +2549,7 @@ int event_enable_timer__process(struct event_enable_t= imer *eet) if (!eet) return 0; =20 - entries =3D eet->evlist->core.pollfd.entries; + entries =3D evlist__core(eet->evlist)->pollfd.entries; revents =3D entries[eet->pollfd_pos].revents; entries[eet->pollfd_pos].revents =3D 0; =20 @@ -2523,7 +2585,7 @@ int event_enable_timer__process(struct event_enable_t= imer *eet) return 0; } =20 -void event_enable_timer__exit(struct event_enable_timer **ep) +static void event_enable_timer__exit(struct event_enable_timer **ep) { if (!ep || !*ep) return; @@ -2627,7 +2689,7 @@ void evlist__warn_user_requested_cpus(struct evlist *= evlist, const char *cpu_lis } =20 /* Should uniquify be disabled for the evlist? */ -static bool evlist__disable_uniquify(const struct evlist *evlist) +static bool evlist__disable_uniquify(struct evlist *evlist) { struct evsel *counter; struct perf_pmu *last_pmu =3D NULL; diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index a9820a6aad5b..838e263b76f3 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -14,6 +14,7 @@ #include #include #include +#include #include =20 #include "affinity.h" @@ -59,7 +60,7 @@ enum bkw_mmap_state { =20 struct event_enable_timer; =20 -struct evlist { +DECLARE_RC_STRUCT(evlist) { struct perf_evlist core; refcount_t refcnt; bool enabled; @@ -86,7 +87,7 @@ struct evlist { struct { pthread_t th; volatile int done; - } thread; + } sb_thread; struct { int fd; /* control file descriptor */ int ack; /* ack file descriptor for control commands */ @@ -107,6 +108,227 @@ struct evsel_str_handler { void *handler; }; =20 +static inline struct perf_evlist *evlist__core(struct evlist *evlist) +{ + return &RC_CHK_ACCESS(evlist)->core; +} + +static inline const struct perf_evlist *evlist__const_core(const struct ev= list *evlist) +{ + return &RC_CHK_ACCESS(evlist)->core; +} + +static inline int evlist__nr_entries(const struct evlist *evlist) +{ + return evlist__const_core(evlist)->nr_entries; +} + +static inline bool evlist__enabled(const struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->enabled; +} + +static inline void evlist__set_enabled(struct evlist *evlist, bool enabled) +{ + RC_CHK_ACCESS(evlist)->enabled =3D enabled; +} + +static inline bool evlist__no_affinity(const struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->no_affinity; +} + +static inline void evlist__set_no_affinity(struct evlist *evlist, bool no_= affinity) +{ + RC_CHK_ACCESS(evlist)->no_affinity =3D no_affinity; +} + +static inline int evlist__sb_thread_done(const struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->sb_thread.done; +} + +static inline void evlist__set_sb_thread_done(struct evlist *evlist, int d= one) +{ + RC_CHK_ACCESS(evlist)->sb_thread.done =3D done; +} + +static inline pthread_t *evlist__sb_thread_th(struct evlist *evlist) +{ + return &RC_CHK_ACCESS(evlist)->sb_thread.th; +} + +static inline int evlist__id_pos(const struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->id_pos; +} + +static inline int evlist__is_pos(const struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->is_pos; +} + +static inline struct event_enable_timer *evlist__event_enable_timer(struct= evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->eet; +} + +static inline enum bkw_mmap_state evlist__bkw_mmap_state(const struct evli= st *evlist) +{ + return RC_CHK_ACCESS(evlist)->bkw_mmap_state; +} + +static inline void evlist__set_bkw_mmap_state(struct evlist *evlist, enum = bkw_mmap_state state) +{ + RC_CHK_ACCESS(evlist)->bkw_mmap_state =3D state; +} + +static inline struct mmap *evlist__mmap(struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->mmap; +} + +static inline struct mmap *evlist__overwrite_mmap(struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->overwrite_mmap; +} + +static inline struct events_stats *evlist__stats(struct evlist *evlist) +{ + return &RC_CHK_ACCESS(evlist)->stats; +} + +static inline u64 evlist__first_sample_time(const struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->first_sample_time; +} + +static inline void evlist__set_first_sample_time(struct evlist *evlist, u6= 4 first) +{ + RC_CHK_ACCESS(evlist)->first_sample_time =3D first; +} + +static inline u64 evlist__last_sample_time(const struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->last_sample_time; +} + +static inline void evlist__set_last_sample_time(struct evlist *evlist, u64= last) +{ + RC_CHK_ACCESS(evlist)->last_sample_time =3D last; +} + +static inline int evlist__nr_br_cntr(const struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->nr_br_cntr; +} + +static inline void evlist__set_nr_br_cntr(struct evlist *evlist, int nr) +{ + RC_CHK_ACCESS(evlist)->nr_br_cntr =3D nr; +} + +static inline struct perf_session *evlist__session(struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->session; +} + +static inline void evlist__set_session(struct evlist *evlist, struct perf_= session *session) +{ + RC_CHK_ACCESS(evlist)->session =3D session; +} + +static inline void (*evlist__trace_event_sample_raw(struct evlist *evlist)) + (struct evlist *evlist, + union perf_event *event, + struct perf_sample *sample) +{ + return RC_CHK_ACCESS(evlist)->trace_event_sample_raw; +} + +static inline void evlist__set_trace_event_sample_raw(struct evlist *evlis= t, + void (*fun)(struct evlist *evlist, + union perf_event *event, + struct perf_sample *sample)) +{ + RC_CHK_ACCESS(evlist)->trace_event_sample_raw =3D fun; +} + +static inline pid_t evlist__workload_pid(const struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->workload.pid; +} + +static inline void evlist__set_workload_pid(struct evlist *evlist, pid_t p= id) +{ + RC_CHK_ACCESS(evlist)->workload.pid =3D pid; +} + +static inline int evlist__workload_cork_fd(const struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->workload.cork_fd; +} + +static inline void evlist__set_workload_cork_fd(struct evlist *evlist, int= cork_fd) +{ + RC_CHK_ACCESS(evlist)->workload.cork_fd =3D cork_fd; +} + +static inline int evlist__ctl_fd_fd(const struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->ctl_fd.fd; +} + +static inline void evlist__set_ctl_fd_fd(struct evlist *evlist, int fd) +{ + RC_CHK_ACCESS(evlist)->ctl_fd.fd =3D fd; +} + +static inline int evlist__ctl_fd_ack(const struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->ctl_fd.ack; +} + +static inline void evlist__set_ctl_fd_ack(struct evlist *evlist, int ack) +{ + RC_CHK_ACCESS(evlist)->ctl_fd.ack =3D ack; +} + +static inline int evlist__ctl_fd_pos(const struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->ctl_fd.pos; +} + +static inline void evlist__set_ctl_fd_pos(struct evlist *evlist, int pos) +{ + RC_CHK_ACCESS(evlist)->ctl_fd.pos =3D pos; +} + +static inline refcount_t *evlist__refcnt(struct evlist *evlist) +{ + return &RC_CHK_ACCESS(evlist)->refcnt; +} + +static inline struct rblist *evlist__metric_events(struct evlist *evlist) +{ + return &RC_CHK_ACCESS(evlist)->metric_events; +} + +static inline struct list_head *evlist__deferred_samples(struct evlist *ev= list) +{ + return &RC_CHK_ACCESS(evlist)->deferred_samples; +} + +static inline struct evsel *evlist__selected(struct evlist *evlist) +{ + return RC_CHK_ACCESS(evlist)->selected; +} + +static inline void evlist__set_selected(struct evlist *evlist, struct evse= l *evsel) +{ + RC_CHK_ACCESS(evlist)->selected =3D evsel; +} + struct evlist *evlist__new(void); struct evlist *evlist__new_default(const struct target *target, bool sampl= e_callchains); struct evlist *evlist__new_dummy(void); @@ -200,8 +422,8 @@ int evlist__mmap_ex(struct evlist *evlist, unsigned int= pages, unsigned int auxtrace_pages, bool auxtrace_overwrite, int nr_cblocks, int affinity, int flush, int comp_level); -int evlist__mmap(struct evlist *evlist, unsigned int pages); -void evlist__munmap(struct evlist *evlist); +int evlist__do_mmap(struct evlist *evlist, unsigned int pages); +void evlist__do_munmap(struct evlist *evlist); =20 size_t evlist__mmap_size(unsigned long pages); =20 @@ -213,8 +435,6 @@ void evlist__enable_evsel(struct evlist *evlist, char *= evsel_name); void evlist__disable_non_dummy(struct evlist *evlist); void evlist__enable_non_dummy(struct evlist *evlist); =20 -void evlist__set_selected(struct evlist *evlist, struct evsel *evsel); - int evlist__create_maps(struct evlist *evlist, struct target *target); int evlist__apply_filters(struct evlist *evlist, struct evsel **err_evsel, struct target *target); @@ -237,26 +457,26 @@ void evlist__splice_list_tail(struct evlist *evlist, = struct list_head *list); =20 static inline bool evlist__empty(struct evlist *evlist) { - return list_empty(&evlist->core.entries); + return list_empty(&evlist__core(evlist)->entries); } =20 static inline struct evsel *evlist__first(struct evlist *evlist) { - struct perf_evsel *evsel =3D perf_evlist__first(&evlist->core); + struct perf_evsel *evsel =3D perf_evlist__first(evlist__core(evlist)); =20 return container_of(evsel, struct evsel, core); } =20 static inline struct evsel *evlist__last(struct evlist *evlist) { - struct perf_evsel *evsel =3D perf_evlist__last(&evlist->core); + struct perf_evsel *evsel =3D perf_evlist__last(evlist__core(evlist)); =20 return container_of(evsel, struct evsel, core); } =20 static inline int evlist__nr_groups(struct evlist *evlist) { - return perf_evlist__nr_groups(&evlist->core); + return perf_evlist__nr_groups(evlist__core(evlist)); } =20 int evlist__strerror_open(struct evlist *evlist, int err, char *buf, size_= t size); @@ -279,7 +499,7 @@ void evlist__to_front(struct evlist *evlist, struct evs= el *move_evsel); * @evsel: struct evsel iterator */ #define evlist__for_each_entry(evlist, evsel) \ - __evlist__for_each_entry(&(evlist)->core.entries, evsel) + __evlist__for_each_entry(&evlist__core(evlist)->entries, evsel) =20 /** * __evlist__for_each_entry_continue - continue iteration thru all the evs= els @@ -295,7 +515,7 @@ void evlist__to_front(struct evlist *evlist, struct evs= el *move_evsel); * @evsel: struct evsel iterator */ #define evlist__for_each_entry_continue(evlist, evsel) \ - __evlist__for_each_entry_continue(&(evlist)->core.entries, evsel) + __evlist__for_each_entry_continue(&evlist__core(evlist)->entries, evsel) =20 /** * __evlist__for_each_entry_from - continue iteration from @evsel (include= d) @@ -311,7 +531,7 @@ void evlist__to_front(struct evlist *evlist, struct evs= el *move_evsel); * @evsel: struct evsel iterator */ #define evlist__for_each_entry_from(evlist, evsel) \ - __evlist__for_each_entry_from(&(evlist)->core.entries, evsel) + __evlist__for_each_entry_from(&evlist__core(evlist)->entries, evsel) =20 /** * __evlist__for_each_entry_reverse - iterate thru all the evsels in rever= se order @@ -327,7 +547,7 @@ void evlist__to_front(struct evlist *evlist, struct evs= el *move_evsel); * @evsel: struct evsel iterator */ #define evlist__for_each_entry_reverse(evlist, evsel) \ - __evlist__for_each_entry_reverse(&(evlist)->core.entries, evsel) + __evlist__for_each_entry_reverse(&evlist__core(evlist)->entries, evsel) =20 /** * __evlist__for_each_entry_safe - safely iterate thru all the evsels @@ -345,7 +565,7 @@ void evlist__to_front(struct evlist *evlist, struct evs= el *move_evsel); * @tmp: struct evsel temp iterator */ #define evlist__for_each_entry_safe(evlist, tmp, evsel) \ - __evlist__for_each_entry_safe(&(evlist)->core.entries, tmp, evsel) + __evlist__for_each_entry_safe(&evlist__core(evlist)->entries, tmp, evsel) =20 /** Iterator state for evlist__for_each_cpu */ struct evlist_cpu_iterator { @@ -451,7 +671,6 @@ int evlist__ctlfd_ack(struct evlist *evlist); int evlist__parse_event_enable_time(struct evlist *evlist, struct record_o= pts *opts, const char *str, int unset); int event_enable_timer__start(struct event_enable_timer *eet); -void event_enable_timer__exit(struct event_enable_timer **ep); int event_enable_timer__process(struct event_enable_timer *eet); =20 struct evsel *evlist__find_evsel(struct evlist *evlist, int idx); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index a54aae079c22..3015b9b4b4da 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -3178,7 +3178,7 @@ static inline bool evsel__has_branch_counters(const s= truct evsel *evsel) if (!leader || !evsel->evlist) return false; =20 - if (evsel->evlist->nr_br_cntr < 0) + if (evlist__nr_br_cntr(evsel->evlist) < 0) evlist__update_br_cntr(evsel->evlist); =20 if (leader->br_cntr_nr > 0) @@ -4162,7 +4162,7 @@ int evsel__open_strerror(struct evsel *evsel, struct = target *target, =20 struct perf_session *evsel__session(struct evsel *evsel) { - return evsel && evsel->evlist ? evsel->evlist->session : NULL; + return evsel && evsel->evlist ? evlist__session(evsel->evlist) : NULL; } =20 struct perf_env *evsel__env(struct evsel *evsel) @@ -4187,7 +4187,7 @@ static int store_evsel_ids(struct evsel *evsel, struc= t evlist *evlist) thread++) { int fd =3D FD(evsel, cpu_map_idx, thread); =20 - if (perf_evlist__id_add_fd(&evlist->core, &evsel->core, + if (perf_evlist__id_add_fd(evlist__core(evlist), &evsel->core, cpu_map_idx, thread, fd) < 0) return -1; } diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 35b1bbca9036..acebd483b9e4 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -501,7 +501,7 @@ for ((_evsel) =3D list_entry((_leader)->core.node.next,= struct evsel, core.node); (_evsel) =3D list_entry((_evsel)->core.node.next, struct evsel, core.node= )) =20 #define for_each_group_member(_evsel, _leader) \ - for_each_group_member_head(_evsel, _leader, &(_leader)->evlist->core.entr= ies) + for_each_group_member_head(_evsel, _leader, &evlist__core((_leader)->evli= st)->entries) =20 /* Iterates group WITH the leader. */ #define for_each_group_evsel_head(_evsel, _leader, _head) \ @@ -511,7 +511,7 @@ for ((_evsel) =3D _leader; \ (_evsel) =3D list_entry((_evsel)->core.node.next, struct evsel, core.node= )) =20 #define for_each_group_evsel(_evsel, _leader) \ - for_each_group_evsel_head(_evsel, _leader, &(_leader)->evlist->core.entri= es) + for_each_group_evsel_head(_evsel, _leader, &evlist__core((_leader)->evlis= t)->entries) =20 static inline bool evsel__has_branch_callstack(const struct evsel *evsel) { diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f9887d2fc8ed..2469e2741bc4 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -323,7 +323,7 @@ static int write_tracing_data(struct feat_fd *ff, return -1; =20 #ifdef HAVE_LIBTRACEEVENT - return read_tracing_data(ff->fd, &evlist->core.entries); + return read_tracing_data(ff->fd, &evlist__core(evlist)->entries); #else pr_err("ERROR: Trying to write tracing data without libtraceevent support= .\n"); return -1; @@ -397,7 +397,7 @@ static int write_e_machine(struct feat_fd *ff, { /* e_machine expanded from 16 to 32-bits for alignment. */ uint32_t e_flags; - uint32_t e_machine =3D perf_session__e_machine(evlist->session, &e_flags); + uint32_t e_machine =3D perf_session__e_machine(evlist__session(evlist), &= e_flags); int ret; =20 ret =3D do_write(ff, &e_machine, sizeof(e_machine)); @@ -533,7 +533,7 @@ static int write_event_desc(struct feat_fd *ff, u32 nre, nri, sz; int ret; =20 - nre =3D evlist->core.nr_entries; + nre =3D evlist__nr_entries(evlist); =20 /* * write number of events @@ -915,7 +915,7 @@ int __weak get_cpuid(char *buffer __maybe_unused, size_= t sz __maybe_unused, =20 static int write_cpuid(struct feat_fd *ff, struct evlist *evlist) { - struct perf_cpu cpu =3D perf_cpu_map__min(evlist->core.all_cpus); + struct perf_cpu cpu =3D perf_cpu_map__min(evlist__core(evlist)->all_cpus); char buffer[64]; int ret; =20 @@ -1348,14 +1348,14 @@ static int write_sample_time(struct feat_fd *ff, struct evlist *evlist) { int ret; + u64 data =3D evlist__first_sample_time(evlist); =20 - ret =3D do_write(ff, &evlist->first_sample_time, - sizeof(evlist->first_sample_time)); + ret =3D do_write(ff, &data, sizeof(data)); if (ret < 0) return ret; =20 - return do_write(ff, &evlist->last_sample_time, - sizeof(evlist->last_sample_time)); + data =3D evlist__last_sample_time(evlist); + return do_write(ff, &data, sizeof(data)); } =20 =20 @@ -2425,16 +2425,16 @@ static void print_sample_time(struct feat_fd *ff, F= ILE *fp) =20 session =3D container_of(ff->ph, struct perf_session, header); =20 - timestamp__scnprintf_usec(session->evlist->first_sample_time, + timestamp__scnprintf_usec(evlist__first_sample_time(session->evlist), time_buf, sizeof(time_buf)); fprintf(fp, "# time of first sample : %s\n", time_buf); =20 - timestamp__scnprintf_usec(session->evlist->last_sample_time, + timestamp__scnprintf_usec(evlist__last_sample_time(session->evlist), time_buf, sizeof(time_buf)); fprintf(fp, "# time of last sample : %s\n", time_buf); =20 - d =3D (double)(session->evlist->last_sample_time - - session->evlist->first_sample_time) / NSEC_PER_MSEC; + d =3D (double)(evlist__last_sample_time(session->evlist) - + evlist__first_sample_time(session->evlist)) / NSEC_PER_MSEC; =20 fprintf(fp, "# sample duration : %10.3f ms\n", d); } @@ -3326,8 +3326,8 @@ static int process_sample_time(struct feat_fd *ff, vo= id *data __maybe_unused) if (ret) return -1; =20 - session->evlist->first_sample_time =3D first_sample_time; - session->evlist->last_sample_time =3D last_sample_time; + evlist__set_first_sample_time(session->evlist, first_sample_time); + evlist__set_last_sample_time(session->evlist, last_sample_time); return 0; } =20 @@ -4396,7 +4396,7 @@ int perf_session__write_header(struct perf_session *s= ession, /*write_attrs_after_data=3D*/false); } =20 -size_t perf_session__data_offset(const struct evlist *evlist) +size_t perf_session__data_offset(struct evlist *evlist) { struct evsel *evsel; size_t data_offset; @@ -4405,7 +4405,7 @@ size_t perf_session__data_offset(const struct evlist = *evlist) evlist__for_each_entry(evlist, evsel) { data_offset +=3D evsel->core.ids * sizeof(u64); } - data_offset +=3D evlist->core.nr_entries * sizeof(struct perf_file_attr); + data_offset +=3D evlist__nr_entries(evlist) * sizeof(struct perf_file_att= r); =20 return data_offset; } @@ -4849,7 +4849,7 @@ int perf_session__read_header(struct perf_session *se= ssion) if (session->evlist =3D=3D NULL) return -ENOMEM; =20 - session->evlist->session =3D session; + evlist__set_session(session->evlist, session); session->machines.host.env =3D &header->env; =20 /* @@ -4933,7 +4933,8 @@ int perf_session__read_header(struct perf_session *se= ssion) if (perf_header__getbuffer64(header, fd, &f_id, sizeof(f_id))) goto out_errno; =20 - perf_evlist__id_add(&session->evlist->core, &evsel->core, 0, j, f_id); + perf_evlist__id_add(evlist__core(session->evlist), + &evsel->core, 0, j, f_id); } =20 lseek(fd, tmp, SEEK_SET); @@ -5126,7 +5127,7 @@ int perf_event__process_attr(const struct perf_tool *= tool __maybe_unused, =20 ids =3D perf_record_header_attr_id(event); for (i =3D 0; i < n_ids; i++) { - perf_evlist__id_add(&evlist->core, &evsel->core, 0, i, ids[i]); + perf_evlist__id_add(evlist__core(evlist), &evsel->core, 0, i, ids[i]); } =20 return 0; diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 86b1a72026d3..5e03f884b7cc 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -158,7 +158,7 @@ int perf_session__inject_header(struct perf_session *se= ssion, struct feat_copier *fc, bool write_attrs_after_data); =20 -size_t perf_session__data_offset(const struct evlist *evlist); +size_t perf_session__data_offset(struct evlist *evlist); =20 void perf_header__set_feat(struct perf_header *header, int feat); void perf_header__clear_feat(struct perf_header *header, int feat); diff --git a/tools/perf/util/intel-tpebs.c b/tools/perf/util/intel-tpebs.c index 8b615dc94e9e..4c1096ba9dcd 100644 --- a/tools/perf/util/intel-tpebs.c +++ b/tools/perf/util/intel-tpebs.c @@ -95,8 +95,9 @@ static int evsel__tpebs_start_perf_record(struct evsel *e= vsel) record_argv[i++] =3D "-o"; record_argv[i++] =3D PERF_DATA; =20 - if (!perf_cpu_map__is_any_cpu_or_is_empty(evsel->evlist->core.user_reques= ted_cpus)) { - cpu_map__snprint(evsel->evlist->core.user_requested_cpus, cpumap_buf, + if (!perf_cpu_map__is_any_cpu_or_is_empty( + evlist__core(evsel->evlist)->user_requested_cpus)) { + cpu_map__snprint(evlist__core(evsel->evlist)->user_requested_cpus, cpuma= p_buf, sizeof(cpumap_buf)); record_argv[i++] =3D "-C"; record_argv[i++] =3D cpumap_buf; @@ -172,7 +173,7 @@ static bool should_ignore_sample(const struct perf_samp= le *sample, const struct if (t->evsel->evlist =3D=3D NULL) return true; =20 - workload_pid =3D t->evsel->evlist->workload.pid; + workload_pid =3D evlist__workload_pid(t->evsel->evlist); if (workload_pid < 0 || workload_pid =3D=3D sample_pid) return false; =20 diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index 191ec2d8a250..26306d5fc72e 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -1494,7 +1494,7 @@ static int parse_groups(struct evlist *perf_evlist, goto out; } =20 - me =3D metricgroup__lookup(&perf_evlist->metric_events, + me =3D metricgroup__lookup(evlist__metric_events(perf_evlist), pick_display_evsel(&metric_list, metric_events), /*create=3D*/true); =20 @@ -1545,13 +1545,13 @@ static int parse_groups(struct evlist *perf_evlist, =20 =20 if (combined_evlist) { - evlist__splice_list_tail(perf_evlist, &combined_evlist->core.entries); + evlist__splice_list_tail(perf_evlist, &evlist__core(combined_evlist)->en= tries); evlist__put(combined_evlist); } =20 list_for_each_entry(m, &metric_list, nd) { if (m->evlist) - evlist__splice_list_tail(perf_evlist, &m->evlist->core.entries); + evlist__splice_list_tail(perf_evlist, &evlist__core(m->evlist)->entries= ); } =20 out: diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index f0809be63ad8..3682053b23cb 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -2267,7 +2267,7 @@ int __parse_events(struct evlist *evlist, const char = *str, const char *pmu_filte { struct parse_events_state parse_state =3D { .list =3D LIST_HEAD_INIT(parse_state.list), - .idx =3D evlist->core.nr_entries, + .idx =3D evlist__nr_entries(evlist), .error =3D err, .stoken =3D PE_START_EVENTS, .fake_pmu =3D fake_pmu, @@ -2541,7 +2541,7 @@ foreach_evsel_in_last_glob(struct evlist *evlist, * * So no need to WARN here, let *func do this. */ - if (evlist->core.nr_entries > 0) + if (evlist__nr_entries(evlist) > 0) last =3D evlist__last(evlist); =20 do { @@ -2551,7 +2551,7 @@ foreach_evsel_in_last_glob(struct evlist *evlist, if (!last) return 0; =20 - if (last->core.node.prev =3D=3D &evlist->core.entries) + if (last->core.node.prev =3D=3D &evlist__core(evlist)->entries) return 0; last =3D list_entry(last->core.node.prev, struct evsel, core.node); } while (!last->cmdline_group_boundary); diff --git a/tools/perf/util/pfm.c b/tools/perf/util/pfm.c index 5f53c2f68a96..f80d6b0df47a 100644 --- a/tools/perf/util/pfm.c +++ b/tools/perf/util/pfm.c @@ -85,7 +85,7 @@ int parse_libpfm_events_option(const struct option *opt, = const char *str, } =20 pmu =3D perf_pmus__find_by_type((unsigned int)attr.type); - evsel =3D parse_events__add_event(evlist->core.nr_entries, + evsel =3D parse_events__add_event(evlist__nr_entries(evlist), &attr, q, /*metric_id=3D*/NULL, pmu); if (evsel =3D=3D NULL) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index a0ec63d39969..22cc57914ad4 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -1465,7 +1465,7 @@ static int pyrf_evlist__init(struct pyrf_evlist *pevl= ist, } threads =3D ((struct pyrf_thread_map *)pthreads)->threads; cpus =3D ((struct pyrf_cpu_map *)pcpus)->cpus; - perf_evlist__set_maps(&pevlist->evlist->core, cpus, threads); + perf_evlist__set_maps(evlist__core(pevlist->evlist), cpus, threads); =20 return 0; } @@ -1481,7 +1481,7 @@ static PyObject *pyrf_evlist__all_cpus(struct pyrf_ev= list *pevlist) struct pyrf_cpu_map *pcpu_map =3D PyObject_New(struct pyrf_cpu_map, &pyrf= _cpu_map__type); =20 if (pcpu_map) - pcpu_map->cpus =3D perf_cpu_map__get(pevlist->evlist->core.all_cpus); + pcpu_map->cpus =3D perf_cpu_map__get(evlist__core(pevlist->evlist)->all_= cpus); =20 return (PyObject *)pcpu_map; } @@ -1494,7 +1494,7 @@ static PyObject *pyrf_evlist__metrics(struct pyrf_evl= ist *pevlist) if (!list) return NULL; =20 - for (node =3D rb_first_cached(&pevlist->evlist->metric_events.entries); n= ode; + for (node =3D rb_first_cached(&evlist__metric_events(pevlist->evlist)->en= tries); node; node =3D rb_next(node)) { struct metric_event *me =3D container_of(node, struct metric_event, nd); struct list_head *pos; @@ -1600,7 +1600,7 @@ static PyObject *pyrf_evlist__compute_metric(struct p= yrf_evlist *pevlist, if (!PyArg_ParseTuple(args, "sii", &metric, &cpu, &thread)) return NULL; =20 - for (node =3D rb_first_cached(&pevlist->evlist->metric_events.entries); + for (node =3D rb_first_cached(&evlist__metric_events(pevlist->evlist)->en= tries); mexp =3D=3D NULL && node; node =3D rb_next(node)) { struct metric_event *me =3D container_of(node, struct metric_event, nd); @@ -1669,7 +1669,7 @@ static PyObject *pyrf_evlist__mmap(struct pyrf_evlist= *pevlist, &pages, &overwrite)) return NULL; =20 - if (evlist__mmap(evlist, pages) < 0) { + if (evlist__do_mmap(evlist, pages) < 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -1705,9 +1705,9 @@ static PyObject *pyrf_evlist__get_pollfd(struct pyrf_= evlist *pevlist, PyObject *list =3D PyList_New(0); int i; =20 - for (i =3D 0; i < evlist->core.pollfd.nr; ++i) { + for (i =3D 0; i < evlist__core(evlist)->pollfd.nr; ++i) { PyObject *file; - file =3D PyFile_FromFd(evlist->core.pollfd.entries[i].fd, "perf", "r", -= 1, + file =3D PyFile_FromFd(evlist__core(evlist)->pollfd.entries[i].fd, "perf= ", "r", -1, NULL, NULL, NULL, 0); if (file =3D=3D NULL) goto free_list; @@ -1739,18 +1739,18 @@ static PyObject *pyrf_evlist__add(struct pyrf_evlis= t *pevlist, =20 Py_INCREF(pevsel); evsel =3D ((struct pyrf_evsel *)pevsel)->evsel; - evsel->core.idx =3D evlist->core.nr_entries; + evsel->core.idx =3D evlist__nr_entries(evlist); evlist__add(evlist, evsel__get(evsel)); =20 - return Py_BuildValue("i", evlist->core.nr_entries); + return Py_BuildValue("i", evlist__nr_entries(evlist)); } =20 static struct mmap *get_md(struct evlist *evlist, int cpu) { int i; =20 - for (i =3D 0; i < evlist->core.nr_mmaps; i++) { - struct mmap *md =3D &evlist->mmap[i]; + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) { + struct mmap *md =3D &evlist__mmap(evlist)[i]; =20 if (md->core.cpu.cpu =3D=3D cpu) return md; @@ -1965,7 +1965,7 @@ static Py_ssize_t pyrf_evlist__length(PyObject *obj) { struct pyrf_evlist *pevlist =3D (void *)obj; =20 - return pevlist->evlist->core.nr_entries; + return evlist__nr_entries(pevlist->evlist); } =20 static PyObject *pyrf_evsel__from_evsel(struct evsel *evsel) @@ -1984,7 +1984,7 @@ static PyObject *pyrf_evlist__item(PyObject *obj, Py_= ssize_t i) struct pyrf_evlist *pevlist =3D (void *)obj; struct evsel *pos; =20 - if (i >=3D pevlist->evlist->core.nr_entries) { + if (i >=3D evlist__nr_entries(pevlist->evlist)) { PyErr_SetString(PyExc_IndexError, "Index out of range"); return NULL; } @@ -2189,7 +2189,7 @@ static PyObject *pyrf__parse_events(PyObject *self, P= yObject *args) cpus =3D pcpus ? ((struct pyrf_cpu_map *)pcpus)->cpus : NULL; =20 parse_events_error__init(&err); - perf_evlist__set_maps(&evlist->core, cpus, threads); + perf_evlist__set_maps(evlist__core(evlist), cpus, threads); if (parse_events(evlist, input, &err)) { parse_events_error__print(&err, input); PyErr_SetFromErrno(PyExc_OSError); @@ -2222,7 +2222,7 @@ static PyObject *pyrf__parse_metrics(PyObject *self, = PyObject *args) threads =3D pthreads ? ((struct pyrf_thread_map *)pthreads)->threads : NU= LL; cpus =3D pcpus ? ((struct pyrf_cpu_map *)pcpus)->cpus : NULL; =20 - perf_evlist__set_maps(&evlist->core, cpus, threads); + perf_evlist__set_maps(evlist__core(evlist), cpus, threads); ret =3D metricgroup__parse_groups(evlist, pmu ?: "all", input, /*metric_no_group=3D*/ false, /*metric_no_merge=3D*/ false, diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index 8a5fc7d5e43c..38e8aee3106b 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c @@ -99,7 +99,7 @@ void evlist__config(struct evlist *evlist, struct record_= opts *opts, struct call bool use_comm_exec; bool sample_id =3D opts->sample_id; =20 - if (perf_cpu_map__cpu(evlist->core.user_requested_cpus, 0).cpu < 0) + if (perf_cpu_map__cpu(evlist__core(evlist)->user_requested_cpus, 0).cpu <= 0) opts->no_inherit =3D true; =20 use_comm_exec =3D perf_can_comm_exec(); @@ -122,7 +122,7 @@ void evlist__config(struct evlist *evlist, struct recor= d_opts *opts, struct call */ use_sample_identifier =3D perf_can_sample_identifier(); sample_id =3D true; - } else if (evlist->core.nr_entries > 1) { + } else if (evlist__nr_entries(evlist) > 1) { struct evsel *first =3D evlist__first(evlist); =20 evlist__for_each_entry(evlist, evsel) { @@ -237,7 +237,8 @@ bool evlist__can_select_event(struct evlist *evlist, co= nst char *str) =20 evsel =3D evlist__last(temp_evlist); =20 - if (!evlist || perf_cpu_map__is_any_cpu_or_is_empty(evlist->core.user_req= uested_cpus)) { + if (!evlist || + perf_cpu_map__is_any_cpu_or_is_empty(evlist__core(evlist)->user_reque= sted_cpus)) { struct perf_cpu_map *cpus =3D perf_cpu_map__new_online_cpus(); =20 if (cpus) @@ -245,7 +246,7 @@ bool evlist__can_select_event(struct evlist *evlist, co= nst char *str) =20 perf_cpu_map__put(cpus); } else { - cpu =3D perf_cpu_map__cpu(evlist->core.user_requested_cpus, 0); + cpu =3D perf_cpu_map__cpu(evlist__core(evlist)->user_requested_cpus, 0); } =20 while (1) { diff --git a/tools/perf/util/sample-raw.c b/tools/perf/util/sample-raw.c index bcf442574d6e..ec33b864431c 100644 --- a/tools/perf/util/sample-raw.c +++ b/tools/perf/util/sample-raw.c @@ -18,10 +18,10 @@ void evlist__init_trace_event_sample_raw(struct evlist = *evlist, struct perf_env const char *cpuid =3D perf_env__cpuid(env); =20 if (arch_pf && !strcmp("s390", arch_pf)) - evlist->trace_event_sample_raw =3D evlist__s390_sample_raw; + evlist__set_trace_event_sample_raw(evlist, evlist__s390_sample_raw); else if (arch_pf && !strcmp("x86", arch_pf) && cpuid && strstarts(cpuid, "AuthenticAMD") && evlist__has_amd_ibs(evlist)) { - evlist->trace_event_sample_raw =3D evlist__amd_sample_raw; + evlist__set_trace_event_sample_raw(evlist, evlist__amd_sample_raw); } } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index deb5b9dfe44c..f9fafbb80a9d 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -204,7 +204,7 @@ struct perf_session *__perf_session__new(struct perf_da= ta *data, session->machines.host.env =3D host_env; } if (session->evlist) - session->evlist->session =3D session; + evlist__set_session(session->evlist, session); =20 session->machines.host.single_address_space =3D perf_env__single_address_space(session->machines.host.env); @@ -1099,8 +1099,8 @@ static void dump_event(struct evlist *evlist, union p= erf_event *event, file_offset, file_path, event->header.size, event->header.type); =20 trace_event(event); - if (event->header.type =3D=3D PERF_RECORD_SAMPLE && evlist->trace_event_s= ample_raw) - evlist->trace_event_sample_raw(evlist, event, sample); + if (event->header.type =3D=3D PERF_RECORD_SAMPLE && evlist__trace_event_s= ample_raw(evlist)) + evlist__trace_event_sample_raw(evlist)(evlist, event, sample); =20 if (sample) evlist__print_tstamp(evlist, event, sample); @@ -1279,7 +1279,7 @@ static int deliver_sample_value(struct evlist *evlist, } =20 if (!storage || sid->evsel =3D=3D NULL) { - ++evlist->stats.nr_unknown_id; + ++evlist__stats(evlist)->nr_unknown_id; return 0; } =20 @@ -1371,6 +1371,8 @@ static int evlist__deliver_deferred_callchain(struct = evlist *evlist, struct evsel *saved_evsel =3D sample->evsel; =20 sample->evsel =3D evlist__id2evsel(evlist, sample->id); + if (sample->evsel) + sample->evsel =3D evsel__get(sample->evsel); ret =3D tool->callchain_deferred(tool, event, sample, sample->evsel, machine); evsel__put(sample->evsel); @@ -1378,7 +1380,7 @@ static int evlist__deliver_deferred_callchain(struct = evlist *evlist, return ret; } =20 - list_for_each_entry_safe(de, tmp, &evlist->deferred_samples, list) { + list_for_each_entry_safe(de, tmp, evlist__deferred_samples(evlist), list)= { struct perf_sample orig_sample; =20 perf_sample__init(&orig_sample, /*all=3D*/false); @@ -1400,6 +1402,8 @@ static int evlist__deliver_deferred_callchain(struct = evlist *evlist, orig_sample.deferred_callchain =3D false; =20 orig_sample.evsel =3D evlist__id2evsel(evlist, orig_sample.id); + if (orig_sample.evsel) + orig_sample.evsel =3D evsel__get(orig_sample.evsel); ret =3D evlist__deliver_sample(evlist, tool, de->event, &orig_sample, orig_sample.evsel, machine); =20 @@ -1426,7 +1430,7 @@ static int session__flush_deferred_samples(struct per= f_session *session, struct deferred_event *de, *tmp; int ret =3D 0; =20 - list_for_each_entry_safe(de, tmp, &evlist->deferred_samples, list) { + list_for_each_entry_safe(de, tmp, evlist__deferred_samples(evlist), list)= { struct perf_sample sample; =20 perf_sample__init(&sample, /*all=3D*/false); @@ -1438,6 +1442,8 @@ static int session__flush_deferred_samples(struct per= f_session *session, } =20 sample.evsel =3D evlist__id2evsel(evlist, sample.id); + if (sample.evsel) + sample.evsel =3D evsel__get(sample.evsel); ret =3D evlist__deliver_sample(evlist, tool, de->event, &sample, sample.evsel, machine); =20 @@ -1464,22 +1470,24 @@ static int machines__deliver_event(struct machines = *machines, =20 dump_event(evlist, event, file_offset, sample, file_path); =20 - if (!sample->evsel) + if (!sample->evsel) { sample->evsel =3D evlist__id2evsel(evlist, sample->id); - else + if (sample->evsel) + sample->evsel =3D evsel__get(sample->evsel); + } else { assert(sample->evsel =3D=3D evlist__id2evsel(evlist, sample->id)); - + } evsel =3D sample->evsel; machine =3D machines__find_for_cpumode(machines, event, sample); =20 switch (event->header.type) { case PERF_RECORD_SAMPLE: if (evsel =3D=3D NULL) { - ++evlist->stats.nr_unknown_id; + ++evlist__stats(evlist)->nr_unknown_id; return 0; } if (machine =3D=3D NULL) { - ++evlist->stats.nr_unprocessable_samples; + ++evlist__stats(evlist)->nr_unprocessable_samples; dump_sample(machine, evsel, event, sample); return 0; } @@ -1497,7 +1505,7 @@ static int machines__deliver_event(struct machines *m= achines, return -ENOMEM; } memcpy(de->event, event, sz); - list_add_tail(&de->list, &evlist->deferred_samples); + list_add_tail(&de->list, evlist__deferred_samples(evlist)); return 0; } return evlist__deliver_sample(evlist, tool, event, sample, evsel, machin= e); @@ -1505,7 +1513,7 @@ static int machines__deliver_event(struct machines *m= achines, return tool->mmap(tool, event, sample, machine); case PERF_RECORD_MMAP2: if (event->header.misc & PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT) - ++evlist->stats.nr_proc_map_timeout; + ++evlist__stats(evlist)->nr_proc_map_timeout; return tool->mmap2(tool, event, sample, machine); case PERF_RECORD_COMM: return tool->comm(tool, event, sample, machine); @@ -1519,13 +1527,13 @@ static int machines__deliver_event(struct machines = *machines, return tool->exit(tool, event, sample, machine); case PERF_RECORD_LOST: if (tool->lost =3D=3D perf_event__process_lost) - evlist->stats.total_lost +=3D event->lost.lost; + evlist__stats(evlist)->total_lost +=3D event->lost.lost; return tool->lost(tool, event, sample, machine); case PERF_RECORD_LOST_SAMPLES: if (event->header.misc & PERF_RECORD_MISC_LOST_SAMPLES_BPF) - evlist->stats.total_dropped_samples +=3D event->lost_samples.lost; + evlist__stats(evlist)->total_dropped_samples +=3D event->lost_samples.l= ost; else if (tool->lost_samples =3D=3D perf_event__process_lost_samples) - evlist->stats.total_lost_samples +=3D event->lost_samples.lost; + evlist__stats(evlist)->total_lost_samples +=3D event->lost_samples.lost; return tool->lost_samples(tool, event, sample, machine); case PERF_RECORD_READ: dump_read(evsel, event); @@ -1537,11 +1545,11 @@ static int machines__deliver_event(struct machines = *machines, case PERF_RECORD_AUX: if (tool->aux =3D=3D perf_event__process_aux) { if (event->aux.flags & PERF_AUX_FLAG_TRUNCATED) - evlist->stats.total_aux_lost +=3D 1; + evlist__stats(evlist)->total_aux_lost +=3D 1; if (event->aux.flags & PERF_AUX_FLAG_PARTIAL) - evlist->stats.total_aux_partial +=3D 1; + evlist__stats(evlist)->total_aux_partial +=3D 1; if (event->aux.flags & PERF_AUX_FLAG_COLLISION) - evlist->stats.total_aux_collision +=3D 1; + evlist__stats(evlist)->total_aux_collision +=3D 1; } return tool->aux(tool, event, sample, machine); case PERF_RECORD_ITRACE_START: @@ -1562,7 +1570,7 @@ static int machines__deliver_event(struct machines *m= achines, return evlist__deliver_deferred_callchain(evlist, tool, event, sample, machine); default: - ++evlist->stats.nr_unknown_events; + ++evlist__stats(evlist)->nr_unknown_events; return -1; } } @@ -1728,7 +1736,7 @@ int perf_session__deliver_synth_event(struct perf_ses= sion *session, struct evlist *evlist =3D session->evlist; const struct perf_tool *tool =3D session->tool; =20 - events_stats__inc(&evlist->stats, event->header.type); + events_stats__inc(evlist__stats(evlist), event->header.type); =20 if (event->header.type >=3D PERF_RECORD_USER_TYPE_START) return perf_session__process_user_event(session, event, 0, NULL); @@ -1876,7 +1884,7 @@ static s64 perf_session__process_event(struct perf_se= ssion *session, return event->header.size; } =20 - events_stats__inc(&evlist->stats, event->header.type); + events_stats__inc(evlist__stats(evlist), event->header.type); =20 if (event->header.type >=3D PERF_RECORD_USER_TYPE_START) return perf_session__process_user_event(session, event, file_offset, fil= e_path); @@ -1937,7 +1945,7 @@ perf_session__warn_order(const struct perf_session *s= ession) =20 static void perf_session__warn_about_errors(const struct perf_session *ses= sion) { - const struct events_stats *stats =3D &session->evlist->stats; + const struct events_stats *stats =3D evlist__stats(session->evlist); =20 if (session->tool->lost =3D=3D perf_event__process_lost && stats->nr_events[PERF_RECORD_LOST] !=3D 0) { @@ -2751,7 +2759,7 @@ size_t perf_session__fprintf_nr_events(struct perf_se= ssion *session, FILE *fp) =20 ret =3D fprintf(fp, "\nAggregated stats:%s\n", msg); =20 - ret +=3D events_stats__fprintf(&session->evlist->stats, fp); + ret +=3D events_stats__fprintf(evlist__stats(session->evlist), fp); return ret; } =20 diff --git a/tools/perf/util/sideband_evlist.c b/tools/perf/util/sideband_e= vlist.c index b84a5463e039..c07dacf3c54c 100644 --- a/tools/perf/util/sideband_evlist.c +++ b/tools/perf/util/sideband_evlist.c @@ -22,7 +22,7 @@ int evlist__add_sb_event(struct evlist *evlist, struct pe= rf_event_attr *attr, attr->sample_id_all =3D 1; } =20 - evsel =3D evsel__new_idx(attr, evlist->core.nr_entries); + evsel =3D evsel__new_idx(attr, evlist__nr_entries(evlist)); if (!evsel) return -1; =20 @@ -49,14 +49,14 @@ static void *perf_evlist__poll_thread(void *arg) while (!done) { bool got_data =3D false; =20 - if (evlist->thread.done) + if (evlist__sb_thread_done(evlist)) draining =3D true; =20 if (!draining) evlist__poll(evlist, 1000); =20 - for (i =3D 0; i < evlist->core.nr_mmaps; i++) { - struct mmap *map =3D &evlist->mmap[i]; + for (i =3D 0; i < evlist__core(evlist)->nr_mmaps; i++) { + struct mmap *map =3D &evlist__mmap(evlist)[i]; union perf_event *event; =20 if (perf_mmap__read_init(&map->core)) @@ -104,7 +104,7 @@ int evlist__start_sb_thread(struct evlist *evlist, stru= ct target *target) if (evlist__create_maps(evlist, target)) goto out_put_evlist; =20 - if (evlist->core.nr_entries > 1) { + if (evlist__nr_entries(evlist) > 1) { bool can_sample_identifier =3D perf_can_sample_identifier(); =20 evlist__for_each_entry(evlist, counter) @@ -114,12 +114,12 @@ int evlist__start_sb_thread(struct evlist *evlist, st= ruct target *target) } =20 evlist__for_each_entry(evlist, counter) { - if (evsel__open(counter, evlist->core.user_requested_cpus, - evlist->core.threads) < 0) + if (evsel__open(counter, evlist__core(evlist)->user_requested_cpus, + evlist__core(evlist)->threads) < 0) goto out_put_evlist; } =20 - if (evlist__mmap(evlist, UINT_MAX)) + if (evlist__do_mmap(evlist, UINT_MAX)) goto out_put_evlist; =20 evlist__for_each_entry(evlist, counter) { @@ -127,8 +127,8 @@ int evlist__start_sb_thread(struct evlist *evlist, stru= ct target *target) goto out_put_evlist; } =20 - evlist->thread.done =3D 0; - if (pthread_create(&evlist->thread.th, NULL, perf_evlist__poll_thread, ev= list)) + evlist__set_sb_thread_done(evlist, 0); + if (pthread_create(evlist__sb_thread_th(evlist), NULL, perf_evlist__poll_= thread, evlist)) goto out_put_evlist; =20 return 0; @@ -143,7 +143,7 @@ void evlist__stop_sb_thread(struct evlist *evlist) { if (!evlist) return; - evlist->thread.done =3D 1; - pthread_join(evlist->thread.th, NULL); + evlist__set_sb_thread_done(evlist, 1); + pthread_join(*evlist__sb_thread_th(evlist), NULL); evlist__put(evlist); } diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 0020089cb13c..f93154f8adfd 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -3481,7 +3481,7 @@ static struct evsel *find_evsel(struct evlist *evlist= , char *event_name) if (event_name[0] =3D=3D '%') { int nr =3D strtol(event_name+1, NULL, 0); =20 - if (nr > evlist->core.nr_entries) + if (nr > evlist__nr_entries(evlist)) return NULL; =20 evsel =3D evlist__first(evlist); diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c index 993f4c4b8f44..c8f49b56815d 100644 --- a/tools/perf/util/stat-display.c +++ b/tools/perf/util/stat-display.c @@ -669,7 +669,7 @@ static void print_metric_header(struct perf_stat_config= *config, =20 /* In case of iostat, print metric header for first root port only */ if (config->iostat_run && - os->evsel->priv !=3D os->evsel->evlist->selected->priv) + os->evsel->priv !=3D evlist__selected(os->evsel->evlist)->priv) return; =20 if (os->evsel->cgrp !=3D os->cgrp) @@ -1128,7 +1128,7 @@ static void print_no_aggr_metric(struct perf_stat_con= fig *config, unsigned int all_idx; struct perf_cpu cpu; =20 - perf_cpu_map__for_each_cpu(cpu, all_idx, evlist->core.user_requested_cpus= ) { + perf_cpu_map__for_each_cpu(cpu, all_idx, evlist__core(evlist)->user_reque= sted_cpus) { struct evsel *counter; bool first =3D true; =20 @@ -1545,7 +1545,7 @@ void evlist__print_counters(struct evlist *evlist, st= ruct perf_stat_config *conf evlist__uniquify_evsel_names(evlist, config); =20 if (config->iostat_run) - evlist->selected =3D evlist__first(evlist); + evlist__set_selected(evlist, evlist__first(evlist)); =20 if (config->interval) prepare_timestamp(config, &os, ts); diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index 48524450326d..482cb70681ab 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c @@ -283,7 +283,7 @@ void *perf_stat__print_shadow_stats_metricgroup(struct = perf_stat_config *config, void *ctxp =3D out->ctx; bool header_printed =3D false; const char *name =3D NULL; - struct rblist *metric_events =3D &evsel->evlist->metric_events; + struct rblist *metric_events =3D evlist__metric_events(evsel->evlist); =20 me =3D metricgroup__lookup(metric_events, evsel, false); if (me =3D=3D NULL) @@ -351,5 +351,5 @@ bool perf_stat__skip_metric_event(struct evsel *evsel) if (!evsel->default_metricgroup) return false; =20 - return !metricgroup__lookup(&evsel->evlist->metric_events, evsel, false); + return !metricgroup__lookup(evlist__metric_events(evsel->evlist), evsel, = false); } diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 66eb9a66a4f7..25f31a174368 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -547,8 +547,8 @@ static void evsel__merge_aliases(struct evsel *evsel) struct evlist *evlist =3D evsel->evlist; struct evsel *alias; =20 - alias =3D list_prepare_entry(evsel, &(evlist->core.entries), core.node); - list_for_each_entry_continue(alias, &evlist->core.entries, core.node) { + alias =3D list_prepare_entry(evsel, &(evlist__core(evlist)->entries), cor= e.node); + list_for_each_entry_continue(alias, &evlist__core(evlist)->entries, core.= node) { if (alias->first_wildcard_match =3D=3D evsel) { /* Merge the same events on different PMUs. */ evsel__merge_aggr_counters(evsel, alias); diff --git a/tools/perf/util/stream.c b/tools/perf/util/stream.c index 3de4a6130853..7bccd2378344 100644 --- a/tools/perf/util/stream.c +++ b/tools/perf/util/stream.c @@ -131,7 +131,7 @@ static int evlist__init_callchain_streams(struct evlist= *evlist, struct evsel *pos; int i =3D 0; =20 - BUG_ON(els->nr_evsel < evlist->core.nr_entries); + BUG_ON(els->nr_evsel < evlist__nr_entries(evlist)); =20 evlist__for_each_entry(evlist, pos) { struct hists *hists =3D evsel__hists(pos); @@ -148,7 +148,7 @@ static int evlist__init_callchain_streams(struct evlist= *evlist, struct evlist_streams *evlist__create_streams(struct evlist *evlist, int nr_streams_max) { - int nr_evsel =3D evlist->core.nr_entries, ret =3D -1; + int nr_evsel =3D evlist__nr_entries(evlist), ret =3D -1; struct evlist_streams *els =3D evlist_streams__new(nr_evsel, nr_streams_max); =20 diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic= -events.c index 2461f25a4d7d..a6a1a83ccbec 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -2230,7 +2230,7 @@ int perf_event__synthesize_tracing_data(const struct = perf_tool *tool, int fd, st * - write the tracing data from the temp file * to the pipe */ - tdata =3D tracing_data_get(&evlist->core.entries, fd, true); + tdata =3D tracing_data_get(&evlist__core(evlist)->entries, fd, true); if (!tdata) return -1; =20 @@ -2378,13 +2378,16 @@ int perf_event__synthesize_stat_events(struct perf_= stat_config *config, const st } =20 err =3D perf_event__synthesize_extra_attr(tool, evlist, process, attrs); - err =3D perf_event__synthesize_thread_map2(tool, evlist->core.threads, pr= ocess, NULL); + err =3D perf_event__synthesize_thread_map2(tool, evlist__core(evlist)->th= reads, + process, /*machine=3D*/NULL); if (err < 0) { pr_err("Couldn't synthesize thread map.\n"); return err; } =20 - err =3D perf_event__synthesize_cpu_map(tool, evlist->core.user_requested_= cpus, process, NULL); + err =3D perf_event__synthesize_cpu_map(tool, + evlist__core(evlist)->user_requested_cpus, + process, /*machine=3D*/NULL); if (err < 0) { pr_err("Couldn't synthesize thread map.\n"); return err; @@ -2492,7 +2495,7 @@ int perf_event__synthesize_for_pipe(const struct perf= _tool *tool, ret +=3D err; =20 #ifdef HAVE_LIBTRACEEVENT - if (have_tracepoints(&evlist->core.entries)) { + if (have_tracepoints(&evlist__core(evlist)->entries)) { int fd =3D perf_data__fd(data); =20 /* diff --git a/tools/perf/util/time-utils.c b/tools/perf/util/time-utils.c index d43c4577d7eb..5558a5a0fea4 100644 --- a/tools/perf/util/time-utils.c +++ b/tools/perf/util/time-utils.c @@ -473,8 +473,8 @@ int perf_time__parse_for_ranges_reltime(const char *tim= e_str, return -ENOMEM; =20 if (has_percent || reltime) { - if (session->evlist->first_sample_time =3D=3D 0 && - session->evlist->last_sample_time =3D=3D 0) { + if (evlist__first_sample_time(session->evlist) =3D=3D 0 && + evlist__last_sample_time(session->evlist) =3D=3D 0) { pr_err("HINT: no first/last sample time found in perf data.\n" "Please use latest perf binary to execute 'perf record'\n" "(if '--buildid-all' is enabled, please set '--timestamp-boundar= y').\n"); @@ -486,8 +486,8 @@ int perf_time__parse_for_ranges_reltime(const char *tim= e_str, num =3D perf_time__percent_parse_str( ptime_range, size, time_str, - session->evlist->first_sample_time, - session->evlist->last_sample_time); + evlist__first_sample_time(session->evlist), + evlist__last_sample_time(session->evlist)); } else { num =3D perf_time__parse_strs(ptime_range, time_str, size); } @@ -499,8 +499,8 @@ int perf_time__parse_for_ranges_reltime(const char *tim= e_str, int i; =20 for (i =3D 0; i < num; i++) { - ptime_range[i].start +=3D session->evlist->first_sample_time; - ptime_range[i].end +=3D session->evlist->first_sample_time; + ptime_range[i].start +=3D evlist__first_sample_time(session->evlist); + ptime_range[i].end +=3D evlist__first_sample_time(session->evlist); } } =20 diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index b06e10a116bb..851a26be6931 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c @@ -71,7 +71,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, ch= ar *bf, size_t size) esamples_percent); } =20 - if (top->evlist->core.nr_entries =3D=3D 1) { + if (evlist__nr_entries(top->evlist) =3D=3D 1) { struct evsel *first =3D evlist__first(top->evlist); ret +=3D SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", (uint64_t)first->core.attr.sample_period, @@ -94,7 +94,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, ch= ar *bf, size_t size) else ret +=3D SNPRINTF(bf + ret, size - ret, " (all"); =20 - nr_cpus =3D perf_cpu_map__nr(top->evlist->core.user_requested_cpus); + nr_cpus =3D perf_cpu_map__nr(evlist__core(top->evlist)->user_requested_cp= us); if (target->cpu_list) ret +=3D SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", nr_cpus > 1 ? "s" : "", --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A480638F654 for ; Sat, 25 Apr 2026 22:50:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157441; cv=none; b=KmBESCZABGj6rRrfmIhinP64xHr6o5gUiMe1Qj0LqSo4tB78TzxYTFS5n0lJ5SeG9sPU6fK7huWUsrX6NN67DVG0dMF+3IoVwCZtN7xdOqbOqz0SzePn8IwuvxtxDtLjodzfBc7X6pgo4EoftFUM2C4Wod8RBw+P98tRBQnIplQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157441; c=relaxed/simple; bh=Xy1jdvXXR0/CcW74V/SbsmucLwmcO9cvRlkHAX93Xiw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=bWddN9fN9ZDib0B/G5mOwHxwhFwcM93dB7pfFv3lmFMC/wLO3YKQMCWClXnLTJUp8SQNMd+evsHkNTQyvm4p/9AdImxJ1yWlaAnq1DwUYoDi5xdspVXGL5rzCFrlTMF9L3k0t9wzM82a5FnynSQgJppoUC+PTLRKLMApvOhzTBE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=DrQ1hPH6; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="DrQ1hPH6" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-12c87ba0890so30472894c88.0 for ; Sat, 25 Apr 2026 15:50:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157439; x=1777762239; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=8vR8VHmbPIJQPTIZrNWt9fCtSF5PQGv7sK1eCsyjJo0=; b=DrQ1hPH6lkcxsuEERQyIL8UsbJgw60gVowXWOtcL8l10l8JvBaNTeR53npdpOlejct 8Yl43TjeNcWU5wbtuHlCO48JhSKCbrqtj+m+qgR0o8P0rIt4LMoZaKcg2MeHNTROHfzU VJR4Ad8p6TMnbpM9bjWkcQyUiMItONHZr3iBZH8Z/A9rnjwPFl+9KJBUdMqbX1cOnSfZ OMTujXx4Bc3zT/abxyiJY3nsESMH9LTd70rkEQEKlsGDbYMfnnfbq/GsFrssc84umB0I erw8/5hev4YYB/4squq4N1Rm0GLl8+LWew224OQYBeBilaQfIR2ldbD9baPqSDfA53Re TYUg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157439; x=1777762239; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=8vR8VHmbPIJQPTIZrNWt9fCtSF5PQGv7sK1eCsyjJo0=; b=qRtlefRthY0VmNrxOSUgQF65CR7WCY37Ybr6XWR5wcQwdm9j0LQmkC4tmjbM2/Ac68 VW/49yIeeKwq1OrySqmgsXyQ453clVJpDQBj/zkkhtuAMdQH9E04pUmgB5bAqzAuCNK4 qaCA8I55+l69Ph2zrTbOnCE1Jvu9dfYXaOC2bv09xF5VXa//35TSt4Ck+TQus7eoAZrT igzOk8K/p6kH7mN8b9W64IyrZLNZgo/f9SJmw4CGx/9m73NiHR2IPRntcfkRuSroMFI7 hdnnFWKvhAwx6Csmr+Wx9UlHBxp604GXkxboU+6Yv9LHYuKiCBfrsuOz+EH5yqAgScX2 +ayA== X-Forwarded-Encrypted: i=1; AFNElJ+UTWNvHEZ5A38CzOINhodtxge9noMPEsLYvTtTg32UrIgqNGsjEpJdZ6tvUPHCcilhinDcf22pLE8+8h8=@vger.kernel.org X-Gm-Message-State: AOJu0YwIt1DV+Hz3A5YYShTA2BxFJaJO127DtLnirfHVuKc4qSZwqMD4 inw9ZvjbFtnsaxqwY38+T7w5yh0Zy8Szt6c955K57MOoEdRabvWWxUA3eLJIREmDHat5zeks1u4 hiUfmRzbDFw== X-Received: from dlbqq1.prod.google.com ([2002:a05:7022:ed01:b0:12c:8bfc:84e3]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:397:b0:128:d438:cad with SMTP id a92af1059eb24-12c73f973a7mr21020269c88.18.1777157438616; Sat, 25 Apr 2026 15:50:38 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:05 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-14-irogers@google.com> Subject: [PATCH v7 13/59] perf python: Use evsel in sample in pyrf_event From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Avoid a duplicated evsel by using the one in sample. Add evsel__get/put to the evsel in perf_sample. Signed-off-by: Ian Rogers --- tools/perf/util/evsel.c | 2 +- tools/perf/util/python.c | 10 +++------- tools/perf/util/sample.c | 17 ++++++++++++----- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 3015b9b4b4da..9b16d832810f 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -3235,7 +3235,7 @@ int evsel__parse_sample(struct evsel *evsel, union pe= rf_event *event, union u64_swap u; =20 perf_sample__init(data, /*all=3D*/true); - data->evsel =3D evsel; + data->evsel =3D evsel__get(evsel); data->cpu =3D data->pid =3D data->tid =3D -1; data->stream_id =3D data->id =3D data->time =3D -1ULL; data->period =3D evsel->core.attr.sample_period; diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 22cc57914ad4..2ba3556c4f16 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -43,7 +43,6 @@ PyMODINIT_FUNC PyInit_perf(void); =20 struct pyrf_event { PyObject_HEAD - struct evsel *evsel; struct perf_sample sample; union perf_event event; }; @@ -274,7 +273,6 @@ static PyMemberDef pyrf_sample_event__members[] =3D { =20 static void pyrf_sample_event__delete(struct pyrf_event *pevent) { - evsel__put(pevent->evsel); perf_sample__exit(&pevent->sample); Py_TYPE(pevent)->tp_free((PyObject*)pevent); } @@ -296,7 +294,7 @@ static PyObject *pyrf_sample_event__repr(const struct p= yrf_event *pevent) #ifdef HAVE_LIBTRACEEVENT static bool is_tracepoint(const struct pyrf_event *pevent) { - return pevent->evsel->core.attr.type =3D=3D PERF_TYPE_TRACEPOINT; + return pevent->sample.evsel->core.attr.type =3D=3D PERF_TYPE_TRACEPOINT; } =20 static PyObject* @@ -343,7 +341,7 @@ tracepoint_field(const struct pyrf_event *pe, struct te= p_format_field *field) static PyObject* get_tracepoint_field(struct pyrf_event *pevent, PyObject *attr_name) { - struct evsel *evsel =3D pevent->evsel; + struct evsel *evsel =3D pevent->sample.evsel; struct tep_event *tp_format =3D evsel__tp_format(evsel); struct tep_format_field *field; =20 @@ -509,7 +507,7 @@ static PyObject *pyrf_event__new(const union perf_event= *event) pevent =3D PyObject_New(struct pyrf_event, ptype); if (pevent !=3D NULL) { memcpy(&pevent->event, event, event->header.size); - pevent->evsel =3D NULL; + perf_sample__init(&pevent->sample, /*all=3D*/false); } return (PyObject *)pevent; } @@ -1798,8 +1796,6 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf= _evlist *pevlist, return Py_None; } =20 - pevent->evsel =3D evsel__get(evsel); - perf_mmap__consume(&md->core); =20 err =3D evsel__parse_sample(evsel, &pevent->event, &pevent->sample); diff --git a/tools/perf/util/sample.c b/tools/perf/util/sample.c index cf73329326d7..7ec534dedc5c 100644 --- a/tools/perf/util/sample.c +++ b/tools/perf/util/sample.c @@ -1,18 +1,23 @@ /* SPDX-License-Identifier: GPL-2.0 */ #include "sample.h" + +#include +#include + +#include +#include + #include "debug.h" +#include "evsel.h" #include "thread.h" -#include +#include "../../arch/x86/include/asm/insn.h" + #ifndef EM_CSKY #define EM_CSKY 252 #endif #ifndef EM_LOONGARCH #define EM_LOONGARCH 258 #endif -#include -#include -#include -#include "../../arch/x86/include/asm/insn.h" =20 void perf_sample__init(struct perf_sample *sample, bool all) { @@ -29,6 +34,8 @@ void perf_sample__init(struct perf_sample *sample, bool a= ll) =20 void perf_sample__exit(struct perf_sample *sample) { + evsel__put(sample->evsel); + sample->evsel =3D NULL; zfree(&sample->user_regs); zfree(&sample->intr_regs); if (sample->merged_callchain) { --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3179B39022E for ; Sat, 25 Apr 2026 22:50:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157445; cv=none; b=aboVs1UJfjBZ3Ixymz3GCRQKqsIncBMImFNFLPzT1IlocFsOG27X7qiAtanXm+RcTZSZ8fK6xB0DYDfQH6hHriObozL7KypLNAIc3aFwbRsTbbXBKAce1twSh5ROknLPIaOdCyXs8WPTWouziRACWK4+UFHAIlfwkYcae24ptPI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157445; c=relaxed/simple; bh=zQJJTQ7WIIRdlqmfoLZApYom+sX71HVjnn5QQIJluZY=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=FC2IQlrwU4AySnahkPI9TGJAxsG9aIGuqiYqcYxUJUcJjRQjojqVyen3ZJ07LFAkHa26vLLpf9wr4pGdgBmv1/yURzuQfwxp7nRughBe82VATsYio6qTfjocOtmyF9RDnVv/jtHptkFtCbJeMeltYR/l12Q0SrxSn1XX6X7VWLg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=muq6mXeB; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="muq6mXeB" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-126e8ee6227so9132351c88.0 for ; Sat, 25 Apr 2026 15:50:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157442; x=1777762242; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=9169CYggYX8a+HEsC3bSJNy6IomTsX+Hp0FHeVzozsY=; b=muq6mXeBGZ/X9NDZJsUoS52+cZ25HB+wEDNv/Y//BkP08/IMmdjJS6eRSub7+amFio /A9lsq2W9qeOQq+ASSi+FbLveC2khknCePoxsxxo5Lm1PESAtoKXsILLHSlJjALuGolV 02J9VIy/PivkiblF+U/DghVfjuECUxvXbZ0KnXZksV4QmtUkb7FHghS+BRpJFJbGHsWu xKYcIE928cX/4daBU/q5AizNlFj+580s4EAPFyrLcCEEoIZxqBX4+Wy5a3Dab0Wfrmz7 jLd+I9Onz4a+Odp0jmmvPIqcsqIknS2dJ1AjrSV2pOOgBAO4DaXBgXakL4QwO7b/TvIB blwA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157442; x=1777762242; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=9169CYggYX8a+HEsC3bSJNy6IomTsX+Hp0FHeVzozsY=; b=oefdKAkpEAR8a3v7btKWI71ioHn7fQCTQ2af/pqtAntduX4QzxE82naW2VvvBzmXu0 MdKv7uMHb6JbVZOVnRV4aypoVe1jMzCnQaTGy3abHAKq1iVsB1vfnUtfxUaVHEmiCzXC 3n+qfDh6HumS7EfeZC85cq0emr9r9dmpXhydeeGx/7DhiunGiuCu9Tdf+IPkDA/jmrmA AkAfq6QV4Qo3UOFLQQKq4bkERznbsqanjz8iDQ+rLdbcdEAdwzUz+m4dU2CFkVEBwcuX 5eM7+6ZZDvqGu4qI/gTXuX9yrRuiqnXVcFSeEKaZrOsRLd/YjslqvGHb4wNQi+CKxyB0 yVgw== X-Forwarded-Encrypted: i=1; AFNElJ/5SHbZR2FUZ2eSHC++YMhsaY5LR73S8CO7bIlAP0adC9TUH2LCxC0MD0nWMimMvV0E2JtDcmAyCC75Oi0=@vger.kernel.org X-Gm-Message-State: AOJu0YwfhgC1wQ3lGTo08suat9ZIHg1y3qXbHSEFJkmqGw8ggS2hOZHL mzE73WctE076Fc8qQ1kOMGiiP2nx47QATuIxFSl8LNfrwAYfY+7PGh37ajJ+BHBU1Rr4a8S3io8 hu4350P0iiw== X-Received: from dlbto5.prod.google.com ([2002:a05:7022:3b05:b0:12c:1f83:7b4c]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:3d87:b0:12a:85ef:1e50 with SMTP id a92af1059eb24-12c73f6c614mr20784618c88.12.1777157442219; Sat, 25 Apr 2026 15:50:42 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:06 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-15-irogers@google.com> Subject: [PATCH v7 14/59] perf python: Add wrapper for perf_data file abstraction From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The perf_data struct is needed for session supported. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Fixed Memory & FD Leaks in pyrf_data__init : Added cleanup of old state (closing file and freeing path) if __init__ is called multiple times on the same object. 2. Fixed Invalid Free in pyrf_data__delete : Ensured pdata->data.path is always dynamically allocated via strdup() , even for the default "perf.data" . This avoids passing a static string literal to free(). 3. Fixed NULL Pointer Dereference in pyrf_data__str : Added a check for NULL path to prevent segfaults if str() or repr() is called on an uninitialized object. 4. Guarded fd Argument Usage: Added a check to ensure that if an fd is provided, it corresponds to a pipe, failing gracefully with a ValueError otherwise. v7: - Added pyrf_data__new to zero-initialize pdata->data to fix crash on re-initialization. --- tools/perf/util/python.c | 103 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 2ba3556c4f16..aa63c7ffe822 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -10,6 +10,7 @@ =20 #include "callchain.h" #include "counts.h" +#include "data.h" #include "event.h" #include "evlist.h" #include "evsel.h" @@ -2316,6 +2317,102 @@ static PyObject *pyrf__metrics(PyObject *self, PyOb= ject *args) return list; } =20 +struct pyrf_data { + PyObject_HEAD + + struct perf_data data; +}; + +static int pyrf_data__init(struct pyrf_data *pdata, PyObject *args, PyObje= ct *kwargs) +{ + static char *kwlist[] =3D { "path", "fd", NULL }; + char *path =3D NULL; + int fd =3D -1; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|si", kwlist, &path, &fd)) + return -1; + + if (pdata->data.open) + perf_data__close(&pdata->data); + free((char *)pdata->data.path); + + if (!path) + path =3D "perf.data"; + + pdata->data.path =3D strdup(path); + if (!pdata->data.path) { + PyErr_NoMemory(); + return -1; + } + + if (fd !=3D -1) { + struct stat st; + + if (fstat(fd, &st) < 0 || !S_ISFIFO(st.st_mode)) { + PyErr_SetString(PyExc_ValueError, + "fd argument is only supported for pipes"); + free((char *)pdata->data.path); + pdata->data.path =3D NULL; + return -1; + } + } + + pdata->data.mode =3D PERF_DATA_MODE_READ; + pdata->data.file.fd =3D fd; + if (perf_data__open(&pdata->data) < 0) { + PyErr_Format(PyExc_IOError, "Failed to open perf data: %s", + pdata->data.path ? pdata->data.path : "perf.data"); + return -1; + } + return 0; +} + +static void pyrf_data__delete(struct pyrf_data *pdata) +{ + perf_data__close(&pdata->data); + free((char *)pdata->data.path); + Py_TYPE(pdata)->tp_free((PyObject *)pdata); +} + +static PyObject *pyrf_data__str(PyObject *self) +{ + const struct pyrf_data *pdata =3D (const struct pyrf_data *)self; + + if (!pdata->data.path) + return PyUnicode_FromString("[uninitialized]"); + return PyUnicode_FromString(pdata->data.path); +} + +static PyObject *pyrf_data__new(PyTypeObject *type, PyObject *args, PyObje= ct *kwargs) +{ + struct pyrf_data *pdata; + + pdata =3D (struct pyrf_data *)PyType_GenericNew(type, args, kwargs); + if (pdata) + memset(&pdata->data, 0, sizeof(pdata->data)); + return (PyObject *)pdata; +} + +static const char pyrf_data__doc[] =3D PyDoc_STR("perf data file object."); + +static PyTypeObject pyrf_data__type =3D { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name =3D "perf.data", + .tp_basicsize =3D sizeof(struct pyrf_data), + .tp_dealloc =3D (destructor)pyrf_data__delete, + .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc =3D pyrf_data__doc, + .tp_init =3D (initproc)pyrf_data__init, + .tp_repr =3D pyrf_data__str, + .tp_str =3D pyrf_data__str, +}; + +static int pyrf_data__setup_types(void) +{ + pyrf_data__type.tp_new =3D pyrf_data__new; + return PyType_Ready(&pyrf_data__type); +} + static PyMethodDef perf__methods[] =3D { { .ml_name =3D "metrics", @@ -2378,7 +2475,8 @@ PyMODINIT_FUNC PyInit_perf(void) pyrf_cpu_map__setup_types() < 0 || pyrf_pmu_iterator__setup_types() < 0 || pyrf_pmu__setup_types() < 0 || - pyrf_counts_values__setup_types() < 0) + pyrf_counts_values__setup_types() < 0 || + pyrf_data__setup_types() < 0) return module; =20 /* The page_size is placed in util object. */ @@ -2426,6 +2524,9 @@ PyMODINIT_FUNC PyInit_perf(void) Py_INCREF(&pyrf_counts_values__type); PyModule_AddObject(module, "counts_values", (PyObject *)&pyrf_counts_valu= es__type); =20 + Py_INCREF(&pyrf_data__type); + PyModule_AddObject(module, "data", (PyObject *)&pyrf_data__type); + dict =3D PyModule_GetDict(module); if (dict =3D=3D NULL) goto error; --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f74.google.com (mail-dl1-f74.google.com [74.125.82.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A76953988F1 for ; Sat, 25 Apr 2026 22:50:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157448; cv=none; b=V7hgk3irHpqFkmS/QQIQ4BqMpUzNQ0myF8EQxk8V6JF2bbeNC7I+llqEB+PnxNWg1czdGP98R1UfCHx9khrCUFqO3fI0XFK4f1xGuRO5fj3QnSNWakW1ePvpC/bkQOsRMzNY+QFgdHKK1F0B961uZoqSZsOeWHNP0lmKkydF1ZE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157448; c=relaxed/simple; bh=ozNykEEtUHxpduvqP4J+aLXrCBbkFXDFkwW7iCbRseM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=J4cmIEF4lTFqMXg5WWC9NGd3lg8X5I4ZO7VqQ3OaB9QvrxWSp+LDBnwdXXLb8c7OdmxXZw/iDXuI2vOB7zyt+aRa8w/Tn+ccpr2WxDZBbnkuhV6KL+rC4fPhVLBk2JEc+IMcI2hZw4UvkNfMemKFV73q80jn2nHSo8TTZ5X6lgM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=irp6lS/D; arc=none smtp.client-ip=74.125.82.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="irp6lS/D" Received: by mail-dl1-f74.google.com with SMTP id a92af1059eb24-126e8ee6227so9132394c88.0 for ; Sat, 25 Apr 2026 15:50:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157445; x=1777762245; darn=vger.kernel.org; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:from:to:cc:subject:date:message-id :reply-to; bh=ksoru2Zcnv41fS9ZZdiwot6rjc0ud5JoTNQnJ6ZzGMs=; b=irp6lS/Drvv6ic6ohD+/Qt6acD8ohWtZr4LepISYJbzXI05RaXFdtevie4s3ksGNiy ww8kZv14mTByHTUS61XICB3wsiSt4W6lSYrwn2+ByklffWnSQnjDKLuKcygCaJl3Wfq1 xuXO/8bfuiDWyeCgony0q0Yv5eQOKE8PuhI9yiQpQmm8h8nGZTUDwT2JX8pwDF89LD9D MBM4ofE2z1s0nOspskJxy8j0Vl8wUktST6RYieCtpOoAF1S3DN6i1K4zWZiDhzkUJKCM qZlMZDkUaMrEfWLejQPksNQiIhS+GOJxM+MSgs94kzippfnu5jpUoRpT/bSJakf1axdg TEiA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157445; x=1777762245; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to; bh=ksoru2Zcnv41fS9ZZdiwot6rjc0ud5JoTNQnJ6ZzGMs=; b=gRTg7NCkLVVcv6cavD0spkpXNYHLfMN59Yw6mvFcjkHBUTsKNrXNh2kIs/MAWW2PBF oxB8VRVRvpr/M3azRx6Lkj7bBEk9LRuPIfUETs9oJoeqMo10gDwhJs/ZA2niEuckm3/Q RSs5qYHpqWuaGTbdZhCP3+myczqEvh+y64qFhO00ZEe+H3dwpn/HQDV9m+/xT0mYLHxw WQ3aWOs48Zr3vlF6BbJKxbM1oGwxcbereR4PKaXaHWRx56adLFwzXA7JEiE43/roYfub BM2hziOue2TfIl7cVKqL8CsyW4Xp9zK8lhBzD/ppyl8mpS8ZYGjG9ksUzHiOduIKfgcy Qp5Q== X-Forwarded-Encrypted: i=1; AFNElJ9IVKjo+xouZpcJRdC44jZxe7jCk1NmQHd5w/CsB8G+HdUnKYYOrGCGAM3FD6OmiiK/HiI8Gs2XtvgGR8A=@vger.kernel.org X-Gm-Message-State: AOJu0Yxr6QhPsNGQs6pXAlH3UbcHvTXJgDg2COSNX3FBVUOv3TDKceiW OQJwAq/eDjMTkzhP1pbMoK5beaeswU0UcDm1+gz0gJwm2CcFau2XaOMhWFLdzsiGdwey8Tbkdy7 HcCjrsHZZsQ== X-Received: from dlad14.prod.google.com ([2002:a05:701b:220e:b0:12c:1ff3:9fa4]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:220f:b0:127:5cd6:fa45 with SMTP id a92af1059eb24-12c73f72632mr18303113c88.14.1777157444626; Sat, 25 Apr 2026 15:50:44 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:07 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-16-irogers@google.com> Subject: [PATCH v7 15/59] perf python: Add python session abstraction wrapping perf's session From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Sessions are necessary to be able to use perf.data files within a tool. Add a wrapper python type that incorporates the tool. Allow a sample callback to be passed when creating the session. When process_events is run this callback will be called, if supplied, for sample events. An example use looks like: ``` $ perf record -e cycles,instructions -a sleep 3 $ PYTHONPATH=3D..../perf/python python3 Python 3.13.7 (main, Aug 20 2025, 22:17:40) [GCC 14.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import perf >>> count=3D0 ... def handle_sample(x): ... global count ... if count < 3: ... print(dir(x)) ... count =3D count + 1 ... perf.session(perf.data("perf.data"),sample=3Dhandle_sample).process_eve= nts() ... ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', = '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init= __', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduc= e__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', = '__subclasshook__', 'sample_addr', 'sample_cpu', 'sample_id', 'sample_ip', = 'sample_period', 'sample_pid', 'sample_stream_id', 'sample_tid', 'sample_ti= me', 'type'] ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', = '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init= __', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduc= e__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', = '__subclasshook__', 'sample_addr', 'sample_cpu', 'sample_id', 'sample_ip', = 'sample_period', 'sample_pid', 'sample_stream_id', 'sample_tid', 'sample_ti= me', 'type'] ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', = '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init= __', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduc= e__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', = '__subclasshook__', 'sample_addr', 'sample_cpu', 'sample_id', 'sample_ip', = 'sample_period', 'sample_pid', 'sample_stream_id', 'sample_tid', 'sample_ti= me', 'type'] ``` Also, add the ability to get the thread associated with a session. For threads, allow the comm string to be retrieved. This can be useful for filtering threads. Connect up some of the standard event handling in psession->tool to better support queries of the machine. Also connect up the symbols. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Fixed Potential Crash in pyrf_thread__comm : Used thread__comm_str() to safely retrieve the command name, avoiding a crash if thread__comm() returns NULL. 2. Fixed Double Free Risk: Zeroed out user_regs , intr_regs , and callchain in the shallow copy of perf_sample to prevent Python from attempting to free pointers it doesn't own. 3. Fixed Memory Leak & Exception Handling in Callback: Handled the return value of PyObject_CallFunction() to avoid leaks, and checked for failure to abort the loop and propagate Python exceptions cleanly. 4. Enforced Type Safety: Used O! with &pyrf_data__type in PyArg_ParseTupleAndKeywords to prevent bad casts from passing arbitrary objects as perf.data. 5. Added Missing Build ID Handler: Registered perf_event__process_build_id to allow correct symbol resolution. 6. Fixed Double Free Crash on Init Failure: Set session and pdata to NULL on failure to prevent tp_dealloc from double-freeing them. 7. Preserved C-level Errors: Made pyrf_session__process_events return the error code integer rather than always returning None . --- v7: - Fixed NULL comm handling. - Avoided swallowing exceptions in module init. - Fixed checkpatch warning for missing blank line. --- tools/perf/util/python.c | 279 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 273 insertions(+), 6 deletions(-) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index aa63c7ffe822..1af53480661f 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -9,8 +9,10 @@ #include =20 #include "callchain.h" +#include "comm.h" #include "counts.h" #include "data.h" +#include "debug.h" #include "event.h" #include "evlist.h" #include "evsel.h" @@ -20,8 +22,12 @@ #include "pmus.h" #include "print_binary.h" #include "record.h" +#include "session.h" #include "strbuf.h" +#include "symbol.h" +#include "thread.h" #include "thread_map.h" +#include "tool.h" #include "tp_pmu.h" #include "trace-event.h" #include "util/sample.h" @@ -2413,6 +2419,256 @@ static int pyrf_data__setup_types(void) return PyType_Ready(&pyrf_data__type); } =20 +struct pyrf_thread { + PyObject_HEAD + + struct thread *thread; +}; + +static void pyrf_thread__delete(struct pyrf_thread *pthread) +{ + thread__put(pthread->thread); + Py_TYPE(pthread)->tp_free((PyObject *)pthread); +} + +static PyObject *pyrf_thread__comm(PyObject *obj) +{ + struct pyrf_thread *pthread =3D (void *)obj; + const char *str =3D thread__comm_str(pthread->thread); + + if (!str) + Py_RETURN_NONE; + + return PyUnicode_FromString(str); +} + +static PyMethodDef pyrf_thread__methods[] =3D { + { + .ml_name =3D "comm", + .ml_meth =3D (PyCFunction)pyrf_thread__comm, + .ml_flags =3D METH_NOARGS, + .ml_doc =3D PyDoc_STR("Comm(and) associated with this thread.") + }, + { .ml_name =3D NULL, } +}; + +static const char pyrf_thread__doc[] =3D PyDoc_STR("perf thread object."); + +static PyTypeObject pyrf_thread__type =3D { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name =3D "perf.thread", + .tp_basicsize =3D sizeof(struct pyrf_thread), + .tp_dealloc =3D (destructor)pyrf_thread__delete, + .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_methods =3D pyrf_thread__methods, + .tp_doc =3D pyrf_thread__doc, +}; + +static int pyrf_thread__setup_types(void) +{ + return PyType_Ready(&pyrf_thread__type); +} + +static PyObject *pyrf_thread__from_thread(struct thread *thread) +{ + struct pyrf_thread *pthread =3D PyObject_New(struct pyrf_thread, &pyrf_th= read__type); + + if (!pthread) + return NULL; + + pthread->thread =3D thread__get(thread); + return (PyObject *)pthread; +} + +struct pyrf_session { + PyObject_HEAD + + struct perf_session *session; + struct perf_tool tool; + struct pyrf_data *pdata; + PyObject *sample; + PyObject *stat; +}; + +static int pyrf_session_tool__sample(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct evsel *evsel, + struct machine *machine __maybe_unused) +{ + struct pyrf_session *psession =3D container_of(tool, struct pyrf_session,= tool); + PyObject *pyevent =3D pyrf_event__new(event); + struct pyrf_event *pevent =3D (struct pyrf_event *)pyevent; + PyObject *ret; + + if (pyevent =3D=3D NULL) + return -ENOMEM; + + memcpy(&pevent->event, event, event->header.size); + if (evsel__parse_sample(evsel, &pevent->event, &pevent->sample) < 0) { + Py_DECREF(pyevent); + return -1; + } + /* Avoid shallow copy pointing to lazily allocated memory that would be d= ouble freed. */ + pevent->sample.user_regs =3D NULL; + pevent->sample.intr_regs =3D NULL; + if (pevent->sample.merged_callchain) + pevent->sample.callchain =3D NULL; + + ret =3D PyObject_CallFunction(psession->sample, "O", pyevent); + if (!ret) { + PyErr_Print(); + Py_DECREF(pyevent); + return -1; + } + Py_DECREF(ret); + Py_DECREF(pyevent); + return 0; +} + +static PyObject *pyrf_session__find_thread(struct pyrf_session *psession, = PyObject *args) +{ + struct machine *machine; + struct thread *thread =3D NULL; + PyObject *result; + int pid; + + if (!PyArg_ParseTuple(args, "i", &pid)) + return NULL; + + machine =3D &psession->session->machines.host; + thread =3D machine__find_thread(machine, pid, pid); + + if (!thread) { + machine =3D perf_session__find_machine(psession->session, pid); + if (machine) + thread =3D machine__find_thread(machine, pid, pid); + } + + if (!thread) { + PyErr_Format(PyExc_TypeError, "Failed to find thread %d", pid); + return NULL; + } + result =3D pyrf_thread__from_thread(thread); + thread__put(thread); + return result; +} + +static int pyrf_session__init(struct pyrf_session *psession, PyObject *arg= s, PyObject *kwargs) +{ + struct pyrf_data *pdata; + PyObject *sample =3D NULL; + static char *kwlist[] =3D { "data", "sample", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &pyrf_data= __type, &pdata, + &sample)) + return -1; + + Py_INCREF(pdata); + psession->pdata =3D pdata; + perf_tool__init(&psession->tool, /*ordered_events=3D*/true); + psession->tool.ordering_requires_timestamps =3D true; + + #define ADD_TOOL(name) \ + do { \ + if (name) { \ + if (!PyCallable_Check(name)) { \ + PyErr_SetString(PyExc_TypeError, #name " must be callable"); \ + return -1; \ + } \ + psession->tool.name =3D pyrf_session_tool__##name; \ + Py_INCREF(name); \ + psession->name =3D name; \ + } \ + } while (0) + + ADD_TOOL(sample); + #undef ADD_TOOL + + psession->tool.comm =3D perf_event__process_comm; + psession->tool.mmap =3D perf_event__process_mmap; + psession->tool.mmap2 =3D perf_event__process_mmap2; + psession->tool.namespaces =3D perf_event__process_namespaces; + psession->tool.cgroup =3D perf_event__process_cgroup; + psession->tool.exit =3D perf_event__process_exit; + psession->tool.fork =3D perf_event__process_fork; + psession->tool.ksymbol =3D perf_event__process_ksymbol; + psession->tool.text_poke =3D perf_event__process_text_poke; + psession->tool.build_id =3D perf_event__process_build_id; + psession->session =3D perf_session__new(&pdata->data, &psession->tool); + if (IS_ERR(psession->session)) { + PyErr_Format(PyExc_IOError, "failed to create session: %ld", + PTR_ERR(psession->session)); + psession->session =3D NULL; + Py_DECREF(pdata); + psession->pdata =3D NULL; + return -1; + } + + if (symbol__init(perf_session__env(psession->session)) < 0) { + perf_session__delete(psession->session); + psession->session =3D NULL; + Py_DECREF(psession->pdata); + psession->pdata =3D NULL; + return -1; + } + + if (perf_session__create_kernel_maps(psession->session) < 0) + pr_warning("Cannot read kernel map\n"); + + return 0; +} + +static void pyrf_session__delete(struct pyrf_session *psession) +{ + Py_XDECREF(psession->pdata); + Py_XDECREF(psession->sample); + perf_session__delete(psession->session); + Py_TYPE(psession)->tp_free((PyObject *)psession); +} + +static PyObject *pyrf_session__find_thread_events(struct pyrf_session *pse= ssion) +{ + int err =3D perf_session__process_events(psession->session); + + return PyLong_FromLong(err); +} + +static PyMethodDef pyrf_session__methods[] =3D { + { + .ml_name =3D "process_events", + .ml_meth =3D (PyCFunction)pyrf_session__find_thread_events, + .ml_flags =3D METH_NOARGS, + .ml_doc =3D PyDoc_STR("Iterate and process events.") + }, + { + .ml_name =3D "find_thread", + .ml_meth =3D (PyCFunction)pyrf_session__find_thread, + .ml_flags =3D METH_VARARGS, + .ml_doc =3D PyDoc_STR("Returns the thread associated with a pid.") + }, + { .ml_name =3D NULL, } +}; + +static const char pyrf_session__doc[] =3D PyDoc_STR("perf session object."= ); + +static PyTypeObject pyrf_session__type =3D { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name =3D "perf.session", + .tp_basicsize =3D sizeof(struct pyrf_session), + .tp_dealloc =3D (destructor)pyrf_session__delete, + .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_methods =3D pyrf_session__methods, + .tp_doc =3D pyrf_session__doc, + .tp_init =3D (initproc)pyrf_session__init, +}; + +static int pyrf_session__setup_types(void) +{ + pyrf_session__type.tp_new =3D PyType_GenericNew; + return PyType_Ready(&pyrf_session__type); +} + static PyMethodDef perf__methods[] =3D { { .ml_name =3D "metrics", @@ -2467,8 +2723,10 @@ PyMODINIT_FUNC PyInit_perf(void) }; PyObject *module =3D PyModule_Create(&moduledef); =20 - if (module =3D=3D NULL || - pyrf_event__setup_types() < 0 || + if (module =3D=3D NULL) + return NULL; + + if (pyrf_event__setup_types() < 0 || pyrf_evlist__setup_types() < 0 || pyrf_evsel__setup_types() < 0 || pyrf_thread_map__setup_types() < 0 || @@ -2476,8 +2734,12 @@ PyMODINIT_FUNC PyInit_perf(void) pyrf_pmu_iterator__setup_types() < 0 || pyrf_pmu__setup_types() < 0 || pyrf_counts_values__setup_types() < 0 || - pyrf_data__setup_types() < 0) - return module; + pyrf_data__setup_types() < 0 || + pyrf_session__setup_types() < 0 || + pyrf_thread__setup_types() < 0) { + Py_DECREF(module); + return NULL; + } =20 /* The page_size is placed in util object. */ page_size =3D sysconf(_SC_PAGE_SIZE); @@ -2527,6 +2789,9 @@ PyMODINIT_FUNC PyInit_perf(void) Py_INCREF(&pyrf_data__type); PyModule_AddObject(module, "data", (PyObject *)&pyrf_data__type); =20 + Py_INCREF(&pyrf_session__type); + PyModule_AddObject(module, "session", (PyObject *)&pyrf_session__type); + dict =3D PyModule_GetDict(module); if (dict =3D=3D NULL) goto error; @@ -2540,7 +2805,9 @@ PyMODINIT_FUNC PyInit_perf(void) } =20 error: - if (PyErr_Occurred()) - PyErr_SetString(PyExc_ImportError, "perf: Init failed!"); + if (PyErr_Occurred()) { + Py_XDECREF(module); + return NULL; + } return module; } --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A401838F62A for ; Sat, 25 Apr 2026 22:50:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157450; cv=none; b=AaxwngvdTTG7DO5CSLozbDh3MBx60g4pXvPME/wwSWNg3OtjpLaBOYEOjpHt55LL6T9PCCI0FPv/yfBpH4oQrTpHbuamwH4oOOeAyPXfFUlnyHF6c8qC2Hwh1zijlzVL7eFNLGyzkOdRMTUwhfS86gQeXThhV/9b/orz7QZ1cFY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157450; c=relaxed/simple; bh=20COE+F8AnmEkDkK/TnYKsZ+aMgWybNxHdBlRVGcSpA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=eAzmeK3fqlAygSOJY6GcZTvZd96GkafPL7EwCPskmqi2akL97VkB+CXbxGNSobFksdsaSNx21bS9jEt5iSn71ekkyx7IbjspYl9puMA7RwQnrODrsxbBSHZDDAfqTONCyvZnV2r3fYXHJhyoZRFL5DUrGc6uUWFFBoVKhMm8FN4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=aVtnLVGo; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="aVtnLVGo" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2bdd327d970so5511767eec.1 for ; Sat, 25 Apr 2026 15:50:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157447; x=1777762247; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=nmZaRmxvqC34085lIoL5dgTRYciZnOOQN4pPPp0OcBc=; b=aVtnLVGow1HLOn+GeGdYkZue3DIBhMkOJM0Fae2XZDmStp1EfIwyC0GxqVr5Vv3T0f E+EY9rsBJvheSxEg5PpmbY8TyX2f6oawSyFtTuQHIT8j5EUPXTxUNVbXaWKwFjcrfiWh 5bI3bpIF0vUg7XhnChs2X8OLvH1x0MHvWHzUM5f3uoThsPpSPi5gaJBkGktYGdxEHCTe qjuRv+IXIaQlL4jDCOym+pLeStx/w0WF+MuCm82LTB1v4ZIr2NI25TIqe0p459v8+zKx p9L20ejAeyBXA6u2RH9uby9pyIw4cEu+SNEwXCspqWz4rGetkRPspqKZjeL5XGImTtVN Bnxg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157447; x=1777762247; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=nmZaRmxvqC34085lIoL5dgTRYciZnOOQN4pPPp0OcBc=; b=lOeDWIlZYrgtVZqWjMgMry/hQ2esTrf/LR0JMYTBW1kfflMgEYFMlBD7IqKOy/er09 yFwYayzC3skAmsW9SqF5zdywV974+J3qzR3xpNQj+qPm9pu8X5i91CP+zgx0iMrgwEnc F4/0LnWX2nXnAgECIK3VRuA0rVM4MuA9vnCmqRn5Qs8DcpKnN95+sqVDRCgy/eLV66ZF dHW16VUKJMQjpMUXlhnonS7IhtfU+9o55ig//Wtzi7bGIbH7hlhKvrfyNh0vZtjWwsaa MUK6VXcPPvAW2jF1ALaY+776X8jvBZqwLoTbAE5fiHC4X68gTsA5vDnR7qXz/NLxe0gZ B6Ng== X-Forwarded-Encrypted: i=1; AFNElJ+WZ56ulk6rikkAITQCppGTKYE0t7WIHv6fOu/g5YHf6VSif729ygQFr2EReIOYBuiC0dOEKVcTHvJrSxk=@vger.kernel.org X-Gm-Message-State: AOJu0YzURV9VZ+LAhXkadxix0VIE9SKbe3rJKmCsN4HChXXqxkPLthdP qZyKtRQN0LmjOWEYoinf+vs0Ojip25IDotzB+ElT4rE0RWDn6/u4Fi3xwgyJINyVCC2yKg/gYq1 pL4PONX5acg== X-Received: from dycpq4.prod.google.com ([2002:a05:7301:fd04:b0:2df:6abf:7cee]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:e12a:b0:2d3:9c91:6c45 with SMTP id 5a478bee46e88-2e42c84c585mr16800947eec.6.1777157446435; Sat, 25 Apr 2026 15:50:46 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:08 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-17-irogers@google.com> Subject: [PATCH v7 16/59] perf python: Add syscall name/id to convert syscall number and name From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Use perf's syscalltbl support to convert syscall number to name assuming the number is for the host machine. This avoids python libaudit support as tools/perf/scripts/python/syscall-counts.py requires. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Guarded with HAVE_LIBTRACEEVENT : Wrapped the syscall functions and their entries in perf__methods with #ifdef HAVE_LIBTRACEEVENT to avoid potential linker errors if CONFIG_TRACE is disabled. 2. Changed Exception Type: Updated pyrf__syscall_id to raise a ValueError instead of a TypeError when a syscall is not found. 3. Fixed Typo: Corrected "name number" to "name" in the docstring for syscall_id. v6: - Added optional keyword-only `elf_machine` argument to `syscall_name` and `syscall_id`. v7: - Made syscalltbl.o unconditional in Build to fix undefined symbol when building without libtraceevent. --- tools/perf/util/Build | 2 +- tools/perf/util/python.c | 48 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 70cc91d00804..fd55d02dd433 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -75,7 +75,7 @@ perf-util-y +=3D sample.o perf-util-y +=3D sample-raw.o perf-util-y +=3D s390-sample-raw.o perf-util-y +=3D amd-sample-raw.o -perf-util-$(CONFIG_TRACE) +=3D syscalltbl.o +perf-util-y +=3D syscalltbl.o perf-util-y +=3D ordered-events.o perf-util-y +=3D namespaces.o perf-util-y +=3D comm.o diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 1af53480661f..861973144106 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -13,6 +13,7 @@ #include "counts.h" #include "data.h" #include "debug.h" +#include "dwarf-regs.h" #include "event.h" #include "evlist.h" #include "evsel.h" @@ -25,6 +26,7 @@ #include "session.h" #include "strbuf.h" #include "symbol.h" +#include "syscalltbl.h" #include "thread.h" #include "thread_map.h" #include "tool.h" @@ -2669,6 +2671,40 @@ static int pyrf_session__setup_types(void) return PyType_Ready(&pyrf_session__type); } =20 +static PyObject *pyrf__syscall_name(PyObject *self, PyObject *args, PyObje= ct *kwargs) +{ + const char *name; + int id; + int elf_machine =3D EM_HOST; + static char * const kwlist[] =3D { "id", "elf_machine", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|$i", kwlist, &id, &elf_= machine)) + return NULL; + + name =3D syscalltbl__name(elf_machine, id); + if (!name) + Py_RETURN_NONE; + return PyUnicode_FromString(name); +} + +static PyObject *pyrf__syscall_id(PyObject *self, PyObject *args, PyObject= *kwargs) +{ + const char *name; + int id; + int elf_machine =3D EM_HOST; + static char * const kwlist[] =3D { "name", "elf_machine", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|$i", kwlist, &name, &el= f_machine)) + return NULL; + + id =3D syscalltbl__id(elf_machine, name); + if (id < 0) { + PyErr_Format(PyExc_ValueError, "Failed to find syscall %s", name); + return NULL; + } + return PyLong_FromLong(id); +} + static PyMethodDef perf__methods[] =3D { { .ml_name =3D "metrics", @@ -2702,6 +2738,18 @@ static PyMethodDef perf__methods[] =3D { .ml_flags =3D METH_NOARGS, .ml_doc =3D PyDoc_STR("Returns a sequence of pmus.") }, + { + .ml_name =3D "syscall_name", + .ml_meth =3D (PyCFunction) pyrf__syscall_name, + .ml_flags =3D METH_VARARGS | METH_KEYWORDS, + .ml_doc =3D PyDoc_STR("Turns a syscall number to a string.") + }, + { + .ml_name =3D "syscall_id", + .ml_meth =3D (PyCFunction) pyrf__syscall_id, + .ml_flags =3D METH_VARARGS | METH_KEYWORDS, + .ml_doc =3D PyDoc_STR("Turns a syscall name to a number.") + }, { .ml_name =3D NULL, } }; =20 --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 52F1A39937C for ; Sat, 25 Apr 2026 22:50:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157452; cv=none; b=F8w8oGgiHzkaaLsvLomr+7MJDZQxRI4pv/C1NIZKQalX39YEUc2Zfuoh8FPvARYuh1hcXfj2hvfKLgUExq/Z0RcXmaVED5r53Rk9W0f/kVRvqOq20o0EzVYyEl2Df2yA2+xPL+Fbun8D7FMEav2vDU/bydoFGgs7oWtO2cu7++4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157452; c=relaxed/simple; bh=XuPkux70kfH5RwH7/h9cCEdBL0jgZPDqWnP38SUuz64=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=MPMfmHer/aOC50BCe/+dgh8c93wV0O+1mPsgeJ9XoNhZb8XKUNYnQ8EQgQ99jqxXKyiSvNJBX6pcXktFGsjbY1v5zm45nd8LryjCq7Cz6yKglTbrpNuZGZ76RntUGnORARIqzrB1q6Rp0gnQVubQL+V+JXbN1x+DKBJkvjzhPXY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=OWG8CG7R; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="OWG8CG7R" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2bdf6fe90a9so13691596eec.1 for ; Sat, 25 Apr 2026 15:50:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157448; x=1777762248; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=WGkE3qmLEV+WvW1f6i+WSiVTMFxII7e5rmRTRboaSrI=; b=OWG8CG7RVpUPsncXJtVFGA3sIqBlACXAskSyjNSLaYMRLdrHuJCphy+vN0UUjqKUCa kgc+l7Ud3UgJ/0Zdw8eWXbRfzGZNk85KRpyob2XL6bCfIDgs+mXCAZz1DksWfcQ5ANMh b1tqLKu83pg60Vn4W6pH5Xo31znZqAW+8Hn40N6vMRLbrpv2BIpZ8+NuWA+HVzjxLQqS JaL8ON9rwBd+qS1eTawwZqv4hT6oTVEL6wPSS+irEA+jtuJ6fjfVobroW8TPalSdrFaz EZUte3+rvI1z4+mb2bSkZ2fJjTrZ2tBvr7SXTgqofJeNjyA1IIMMgwb+nJXwVwQrOjo6 GXKA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157448; x=1777762248; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=WGkE3qmLEV+WvW1f6i+WSiVTMFxII7e5rmRTRboaSrI=; b=i6LyjLPKOkyuWg2+5YkMmNWYaCJTqnCRZOB70nc1XTnyAILF7wuzWShIiJrFlMcuXN nVDLo1Jb1WvaVUCmjh2tSFtTqRsWHzA97Vzifb4b6ZQDFzNoECqhTGnKBht8FnztaPBl o6q6djV1suJt41eWleel8jIql7lqT7ipvzdsEQe5pUmjOqij2W3pGHfVqUz/fTVb8Mp4 fHeTCoKfY0FNx9veRYpJyjc6Fndv4bdzVyKLvCwIxgIVoosFpJuwyyAUKaZaP9fVVh5n Jk70+hJOWNnKnJoBgbrM8GcJibX+Wr4qR0eLqUcOH8Ghtj9wPyOc3yJc1eV6CAY05Kl6 yBFA== X-Forwarded-Encrypted: i=1; AFNElJ8JlppmwmhJoAh8ErZ95NXWU1MW5AyAdBmlsW9koq1kcD7o92KUxn9lcU5/hsnnEyFWzxrulLtL4Jbhhto=@vger.kernel.org X-Gm-Message-State: AOJu0YyYvuwEL/BRESqqJPHL5H+vR5y/amFL5Pxb2zray9l9noCeM5Xm om4WbpBeKwdUW1ctZ6B+AyUVxOQFNctsOYBbGSLJ07BWOFw/JP+U1wN6UT7LeftwM5eoHKXnza+ FZTkPVTUmIg== X-Received: from dlbro6.prod.google.com ([2002:a05:7022:1586:b0:12d:cb64:e75d]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:4583:b0:128:df80:1852 with SMTP id a92af1059eb24-12c73f75b29mr19057213c88.9.1777157448192; Sat, 25 Apr 2026 15:50:48 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:09 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-18-irogers@google.com> Subject: [PATCH v7 17/59] perf python: Refactor and add accessors to sample event From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add common evsel field for events and move sample specific fields to only be present in sample events. Add accessors for sample events. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v5: 1. Fix Uninitialized Memory: Restore zero-initialization of `pevent->sample` in `pyrf_event__new()` to prevent wild free crashes on error paths. v6: - Refactored `pyrf_event__new` to take `evsel` and `session`, and use dynamic allocation based on event size. Updated callers. --- tools/perf/util/python.c | 516 ++++++++++++++++++++++++++++++++------- 1 file changed, 434 insertions(+), 82 deletions(-) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 861973144106..824cf58645e0 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -8,22 +8,29 @@ #include #include =20 +#include "addr_location.h" +#include "build-id.h" #include "callchain.h" #include "comm.h" #include "counts.h" #include "data.h" #include "debug.h" +#include "dso.h" #include "dwarf-regs.h" #include "event.h" #include "evlist.h" #include "evsel.h" #include "expr.h" +#include "map.h" #include "metricgroup.h" #include "mmap.h" #include "pmus.h" #include "print_binary.h" #include "record.h" +#include "sample.h" #include "session.h" +#include "srccode.h" +#include "srcline.h" #include "strbuf.h" #include "symbol.h" #include "syscalltbl.h" @@ -32,7 +39,6 @@ #include "tool.h" #include "tp_pmu.h" #include "trace-event.h" -#include "util/sample.h" =20 #ifdef HAVE_LIBTRACEEVENT #include @@ -40,6 +46,8 @@ =20 PyMODINIT_FUNC PyInit_perf(void); =20 +static PyObject *pyrf_evsel__from_evsel(struct evsel *evsel); + #define member_def(type, member, ptype, help) \ { #member, ptype, \ offsetof(struct pyrf_event, event) + offsetof(struct type, member), \ @@ -52,21 +60,53 @@ PyMODINIT_FUNC PyInit_perf(void); =20 struct pyrf_event { PyObject_HEAD + /** @sample: The parsed sample from the event. */ struct perf_sample sample; - union perf_event event; + /** @al: The address location from machine__resolve, lazily computed. */ + struct addr_location al; + /** @al_resolved: True when machine__resolve been called. */ + bool al_resolved; + /** @event: The underlying perf_event that may be in a file or ring buffe= r. */ + union perf_event event; }; =20 #define sample_members \ - sample_member_def(sample_ip, ip, T_ULONGLONG, "event ip"), \ sample_member_def(sample_pid, pid, T_INT, "event pid"), \ sample_member_def(sample_tid, tid, T_INT, "event tid"), \ sample_member_def(sample_time, time, T_ULONGLONG, "event timestamp"), \ - sample_member_def(sample_addr, addr, T_ULONGLONG, "event addr"), \ sample_member_def(sample_id, id, T_ULONGLONG, "event id"), \ sample_member_def(sample_stream_id, stream_id, T_ULONGLONG, "event stream= id"), \ sample_member_def(sample_period, period, T_ULONGLONG, "event period"), \ sample_member_def(sample_cpu, cpu, T_UINT, "event cpu"), =20 +static PyObject *pyrf_event__get_evsel(PyObject *self, void *closure __may= be_unused) +{ + struct pyrf_event *pevent =3D (void *)self; + + if (!pevent->sample.evsel) + Py_RETURN_NONE; + + return pyrf_evsel__from_evsel(pevent->sample.evsel); +} + +static PyGetSetDef pyrf_event__getset[] =3D { + { + .name =3D "evsel", + .get =3D pyrf_event__get_evsel, + .set =3D NULL, + .doc =3D "tracking event.", + }, + { .name =3D NULL, }, +}; + +static void pyrf_event__delete(struct pyrf_event *pevent) +{ + if (pevent->al_resolved) + addr_location__exit(&pevent->al); + perf_sample__exit(&pevent->sample); + Py_TYPE(pevent)->tp_free((PyObject *)pevent); +} + static const char pyrf_mmap_event__doc[] =3D PyDoc_STR("perf mmap event ob= ject."); =20 static PyMemberDef pyrf_mmap_event__members[] =3D { @@ -105,9 +145,12 @@ static PyTypeObject pyrf_mmap_event__type =3D { PyVarObject_HEAD_INIT(NULL, 0) .tp_name =3D "perf.mmap_event", .tp_basicsize =3D sizeof(struct pyrf_event), + .tp_new =3D PyType_GenericNew, + .tp_dealloc =3D (destructor)pyrf_event__delete, .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, .tp_doc =3D pyrf_mmap_event__doc, .tp_members =3D pyrf_mmap_event__members, + .tp_getset =3D pyrf_event__getset, .tp_repr =3D (reprfunc)pyrf_mmap_event__repr, }; =20 @@ -140,9 +183,12 @@ static PyTypeObject pyrf_task_event__type =3D { PyVarObject_HEAD_INIT(NULL, 0) .tp_name =3D "perf.task_event", .tp_basicsize =3D sizeof(struct pyrf_event), + .tp_new =3D PyType_GenericNew, + .tp_dealloc =3D (destructor)pyrf_event__delete, .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, .tp_doc =3D pyrf_task_event__doc, .tp_members =3D pyrf_task_event__members, + .tp_getset =3D pyrf_event__getset, .tp_repr =3D (reprfunc)pyrf_task_event__repr, }; =20 @@ -172,6 +218,7 @@ static PyTypeObject pyrf_comm_event__type =3D { .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, .tp_doc =3D pyrf_comm_event__doc, .tp_members =3D pyrf_comm_event__members, + .tp_getset =3D pyrf_event__getset, .tp_repr =3D (reprfunc)pyrf_comm_event__repr, }; =20 @@ -201,9 +248,12 @@ static PyTypeObject pyrf_throttle_event__type =3D { PyVarObject_HEAD_INIT(NULL, 0) .tp_name =3D "perf.throttle_event", .tp_basicsize =3D sizeof(struct pyrf_event), + .tp_new =3D PyType_GenericNew, + .tp_dealloc =3D (destructor)pyrf_event__delete, .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, .tp_doc =3D pyrf_throttle_event__doc, .tp_members =3D pyrf_throttle_event__members, + .tp_getset =3D pyrf_event__getset, .tp_repr =3D (reprfunc)pyrf_throttle_event__repr, }; =20 @@ -236,9 +286,12 @@ static PyTypeObject pyrf_lost_event__type =3D { PyVarObject_HEAD_INIT(NULL, 0) .tp_name =3D "perf.lost_event", .tp_basicsize =3D sizeof(struct pyrf_event), + .tp_new =3D PyType_GenericNew, + .tp_dealloc =3D (destructor)pyrf_event__delete, .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, .tp_doc =3D pyrf_lost_event__doc, .tp_members =3D pyrf_lost_event__members, + .tp_getset =3D pyrf_event__getset, .tp_repr =3D (reprfunc)pyrf_lost_event__repr, }; =20 @@ -269,6 +322,7 @@ static PyTypeObject pyrf_read_event__type =3D { .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, .tp_doc =3D pyrf_read_event__doc, .tp_members =3D pyrf_read_event__members, + .tp_getset =3D pyrf_event__getset, .tp_repr =3D (reprfunc)pyrf_read_event__repr, }; =20 @@ -276,16 +330,17 @@ static const char pyrf_sample_event__doc[] =3D PyDoc_= STR("perf sample event object =20 static PyMemberDef pyrf_sample_event__members[] =3D { sample_members + sample_member_def(sample_ip, ip, T_ULONGLONG, "event ip"), + sample_member_def(sample_addr, addr, T_ULONGLONG, "event addr"), + sample_member_def(sample_phys_addr, phys_addr, T_ULONGLONG, "event physic= al addr"), + sample_member_def(sample_weight, weight, T_ULONGLONG, "event weight"), + sample_member_def(sample_data_src, data_src, T_ULONGLONG, "event data sou= rce"), + sample_member_def(sample_insn_count, insn_cnt, T_ULONGLONG, "event instru= ction count"), + sample_member_def(sample_cyc_count, cyc_cnt, T_ULONGLONG, "event cycle co= unt"), member_def(perf_event_header, type, T_UINT, "event type"), { .name =3D NULL, }, }; =20 -static void pyrf_sample_event__delete(struct pyrf_event *pevent) -{ - perf_sample__exit(&pevent->sample); - Py_TYPE(pevent)->tp_free((PyObject*)pevent); -} - static PyObject *pyrf_sample_event__repr(const struct pyrf_event *pevent) { PyObject *ret; @@ -373,6 +428,199 @@ get_tracepoint_field(struct pyrf_event *pevent, PyObj= ect *attr_name) } #endif /* HAVE_LIBTRACEEVENT */ =20 +static int pyrf_sample_event__resolve_al(struct pyrf_event *pevent) +{ + struct evsel *evsel =3D pevent->sample.evsel; + struct evlist *evlist =3D evsel ? evsel->evlist : NULL; + struct perf_session *session =3D evlist ? evlist__session(evlist) : NULL; + + if (pevent->al_resolved) + return 0; + + if (!session) + return -1; + + addr_location__init(&pevent->al); + if (machine__resolve(&session->machines.host, &pevent->al, &pevent->sampl= e) < 0) { + addr_location__exit(&pevent->al); + return -1; + } + + pevent->al_resolved =3D true; + return 0; +} + +static PyObject *pyrf_sample_event__get_dso(struct pyrf_event *pevent, + void *closure __maybe_unused) +{ + if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.map) + Py_RETURN_NONE; + + return PyUnicode_FromString(dso__name(map__dso(pevent->al.map))); +} + +static PyObject *pyrf_sample_event__get_dso_long_name(struct pyrf_event *p= event, + void *closure __maybe_unused) +{ + if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.map) + Py_RETURN_NONE; + + return PyUnicode_FromString(dso__long_name(map__dso(pevent->al.map))); +} + +static PyObject *pyrf_sample_event__get_dso_bid(struct pyrf_event *pevent, + void *closure __maybe_unused) +{ + char sbuild_id[SBUILD_ID_SIZE]; + + if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.map) + Py_RETURN_NONE; + + build_id__snprintf(dso__bid(map__dso(pevent->al.map)), sbuild_id, sizeof(= sbuild_id)); + return PyUnicode_FromString(sbuild_id); +} + +static PyObject *pyrf_sample_event__get_map_start(struct pyrf_event *peven= t, + void *closure __maybe_unused) +{ + if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.map) + Py_RETURN_NONE; + + return PyLong_FromUnsignedLong(map__start(pevent->al.map)); +} + +static PyObject *pyrf_sample_event__get_map_end(struct pyrf_event *pevent, + void *closure __maybe_unused) +{ + if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.map) + Py_RETURN_NONE; + + return PyLong_FromUnsignedLong(map__end(pevent->al.map)); +} + +static PyObject *pyrf_sample_event__get_map_pgoff(struct pyrf_event *peven= t, + void *closure __maybe_unused) +{ + if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.map) + Py_RETURN_NONE; + + return PyLong_FromUnsignedLongLong(map__pgoff(pevent->al.map)); +} + +static PyObject *pyrf_sample_event__get_symbol(struct pyrf_event *pevent, + void *closure __maybe_unused) +{ + if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.sym) + Py_RETURN_NONE; + + return PyUnicode_FromString(pevent->al.sym->name); +} + +static PyObject *pyrf_sample_event__get_sym_start(struct pyrf_event *peven= t, + void *closure __maybe_unused) +{ + if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.sym) + Py_RETURN_NONE; + + return PyLong_FromUnsignedLongLong(pevent->al.sym->start); +} + +static PyObject *pyrf_sample_event__get_sym_end(struct pyrf_event *pevent, + void *closure __maybe_unused) +{ + if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.sym) + Py_RETURN_NONE; + + return PyLong_FromUnsignedLongLong(pevent->al.sym->end); +} + +static PyObject *pyrf_sample_event__get_raw_buf(struct pyrf_event *pevent, + void *closure __maybe_unused) +{ + if (pevent->event.header.type !=3D PERF_RECORD_SAMPLE) + Py_RETURN_NONE; + + return PyBytes_FromStringAndSize((const char *)pevent->sample.raw_data, + pevent->sample.raw_size); +} + +static PyObject *pyrf_sample_event__srccode(PyObject *self, PyObject *args) +{ + struct pyrf_event *pevent =3D (void *)self; + u64 addr =3D pevent->sample.ip; + char *srcfile =3D NULL; + char *srccode =3D NULL; + unsigned int line =3D 0; + int len =3D 0; + PyObject *result; + struct addr_location al; + + if (!PyArg_ParseTuple(args, "|K", &addr)) + return NULL; + + if (pyrf_sample_event__resolve_al(pevent) < 0) + Py_RETURN_NONE; + + if (addr !=3D pevent->sample.ip) { + addr_location__init(&al); + thread__find_symbol_fb(pevent->al.thread, pevent->sample.cpumode, addr, = &al); + } else { + addr_location__init(&al); + al.thread =3D thread__get(pevent->al.thread); + al.map =3D map__get(pevent->al.map); + al.sym =3D pevent->al.sym; + al.addr =3D pevent->al.addr; + } + + if (al.map) { + struct dso *dso =3D map__dso(al.map); + + if (dso) { + srcfile =3D get_srcline_split(dso, map__rip_2objdump(al.map, addr), + &line); + } + } + addr_location__exit(&al); + + if (srcfile) { + srccode =3D find_sourceline(srcfile, line, &len); + result =3D Py_BuildValue("(sIs#)", srcfile, line, srccode, (Py_ssize_t)l= en); + free(srcfile); + } else { + result =3D Py_BuildValue("(sIs#)", NULL, 0, NULL, (Py_ssize_t)0); + } + + return result; +} + +static PyObject *pyrf_sample_event__insn(PyObject *self, PyObject *args __= maybe_unused) +{ + struct pyrf_event *pevent =3D (void *)self; + struct thread *thread; + struct machine *machine; + + if (pyrf_sample_event__resolve_al(pevent) < 0) + Py_RETURN_NONE; + + thread =3D pevent->al.thread; + + if (!thread || !thread__maps(thread)) + Py_RETURN_NONE; + + machine =3D maps__machine(thread__maps(thread)); + if (!machine) + Py_RETURN_NONE; + + if (pevent->sample.ip && !pevent->sample.insn_len) + perf_sample__fetch_insn(&pevent->sample, thread, machine); + + if (!pevent->sample.insn_len) + Py_RETURN_NONE; + + return PyBytes_FromStringAndSize((const char *)pevent->sample.insn, + pevent->sample.insn_len); +} + static PyObject* pyrf_sample_event__getattro(struct pyrf_event *pevent, PyObject *attr_name) { @@ -386,13 +634,103 @@ pyrf_sample_event__getattro(struct pyrf_event *peven= t, PyObject *attr_name) return obj ?: PyObject_GenericGetAttr((PyObject *) pevent, attr_name); } =20 +static PyGetSetDef pyrf_sample_event__getset[] =3D { + { + .name =3D "raw_buf", + .get =3D (getter)pyrf_sample_event__get_raw_buf, + .set =3D NULL, + .doc =3D "event raw buffer.", + }, + { + .name =3D "evsel", + .get =3D pyrf_event__get_evsel, + .set =3D NULL, + .doc =3D "tracking event.", + }, + { + .name =3D "dso", + .get =3D (getter)pyrf_sample_event__get_dso, + .set =3D NULL, + .doc =3D "event dso short name.", + }, + { + .name =3D "dso_long_name", + .get =3D (getter)pyrf_sample_event__get_dso_long_name, + .set =3D NULL, + .doc =3D "event dso long name.", + }, + { + .name =3D "dso_bid", + .get =3D (getter)pyrf_sample_event__get_dso_bid, + .set =3D NULL, + .doc =3D "event dso build id.", + }, + { + .name =3D "map_start", + .get =3D (getter)pyrf_sample_event__get_map_start, + .set =3D NULL, + .doc =3D "event map start address.", + }, + { + .name =3D "map_end", + .get =3D (getter)pyrf_sample_event__get_map_end, + .set =3D NULL, + .doc =3D "event map end address.", + }, + { + .name =3D "map_pgoff", + .get =3D (getter)pyrf_sample_event__get_map_pgoff, + .set =3D NULL, + .doc =3D "event map page offset.", + }, + { + .name =3D "symbol", + .get =3D (getter)pyrf_sample_event__get_symbol, + .set =3D NULL, + .doc =3D "event symbol name.", + }, + { + .name =3D "sym_start", + .get =3D (getter)pyrf_sample_event__get_sym_start, + .set =3D NULL, + .doc =3D "event symbol start address.", + }, + { + .name =3D "sym_end", + .get =3D (getter)pyrf_sample_event__get_sym_end, + .set =3D NULL, + .doc =3D "event symbol end address.", + }, + { .name =3D NULL, }, +}; + +static PyMethodDef pyrf_sample_event__methods[] =3D { + { + .ml_name =3D "srccode", + .ml_meth =3D (PyCFunction)pyrf_sample_event__srccode, + .ml_flags =3D METH_VARARGS, + .ml_doc =3D PyDoc_STR("Get source code for an address.") + }, + { + .ml_name =3D "insn", + .ml_meth =3D (PyCFunction)pyrf_sample_event__insn, + .ml_flags =3D METH_NOARGS, + .ml_doc =3D PyDoc_STR("Get instruction bytes for a sample.") + }, + { .ml_name =3D NULL, } +}; + static PyTypeObject pyrf_sample_event__type =3D { PyVarObject_HEAD_INIT(NULL, 0) .tp_name =3D "perf.sample_event", .tp_basicsize =3D sizeof(struct pyrf_event), + .tp_new =3D PyType_GenericNew, + .tp_dealloc =3D (destructor)pyrf_event__delete, .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, .tp_doc =3D pyrf_sample_event__doc, .tp_members =3D pyrf_sample_event__members, + .tp_getset =3D pyrf_sample_event__getset, + .tp_methods =3D pyrf_sample_event__methods, .tp_repr =3D (reprfunc)pyrf_sample_event__repr, .tp_getattro =3D (getattrofunc) pyrf_sample_event__getattro, }; @@ -428,25 +766,18 @@ static PyTypeObject pyrf_context_switch_event__type = =3D { PyVarObject_HEAD_INIT(NULL, 0) .tp_name =3D "perf.context_switch_event", .tp_basicsize =3D sizeof(struct pyrf_event), + .tp_new =3D PyType_GenericNew, + .tp_dealloc =3D (destructor)pyrf_event__delete, .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, .tp_doc =3D pyrf_context_switch_event__doc, .tp_members =3D pyrf_context_switch_event__members, + .tp_getset =3D pyrf_event__getset, .tp_repr =3D (reprfunc)pyrf_context_switch_event__repr, }; =20 static int pyrf_event__setup_types(void) { int err; - pyrf_mmap_event__type.tp_new =3D - pyrf_task_event__type.tp_new =3D - pyrf_comm_event__type.tp_new =3D - pyrf_lost_event__type.tp_new =3D - pyrf_read_event__type.tp_new =3D - pyrf_sample_event__type.tp_new =3D - pyrf_context_switch_event__type.tp_new =3D - pyrf_throttle_event__type.tp_new =3D PyType_GenericNew; - - pyrf_sample_event__type.tp_dealloc =3D (destructor)pyrf_sample_event__del= ete, =20 err =3D PyType_Ready(&pyrf_mmap_event__type); if (err < 0) @@ -490,10 +821,14 @@ static PyTypeObject *pyrf_event__type[] =3D { [PERF_RECORD_SWITCH_CPU_WIDE] =3D &pyrf_context_switch_event__type, }; =20 -static PyObject *pyrf_event__new(const union perf_event *event) +static PyObject *pyrf_event__new(const union perf_event *event, struct evs= el *evsel, + struct perf_session *session __maybe_unused) { struct pyrf_event *pevent; PyTypeObject *ptype; + size_t size; + int err; + size_t min_size =3D sizeof(struct perf_event_header); =20 if ((event->header.type < PERF_RECORD_MMAP || event->header.type > PERF_RECORD_SAMPLE) && @@ -504,19 +839,62 @@ static PyObject *pyrf_event__new(const union perf_eve= nt *event) return NULL; } =20 - // FIXME this better be dynamic or we need to parse everything - // before calling perf_mmap__consume(), including tracepoint fields. - if (sizeof(pevent->event) < event->header.size) { - PyErr_Format(PyExc_TypeError, "Unexpected event size: %zd < %u", - sizeof(pevent->event), event->header.size); - return NULL; + ptype =3D pyrf_event__type[event->header.type]; + + switch (event->header.type) { + case PERF_RECORD_MMAP: + min_size =3D sizeof(struct perf_record_mmap); + break; + case PERF_RECORD_COMM: + min_size =3D sizeof(struct perf_record_comm); + break; + case PERF_RECORD_FORK: + case PERF_RECORD_EXIT: + min_size =3D sizeof(struct perf_record_fork); + break; + case PERF_RECORD_THROTTLE: + case PERF_RECORD_UNTHROTTLE: + min_size =3D sizeof(struct perf_record_throttle); + break; + case PERF_RECORD_LOST: + min_size =3D sizeof(struct perf_record_lost); + break; + case PERF_RECORD_READ: + min_size =3D sizeof(struct perf_record_read); + break; + case PERF_RECORD_SWITCH: + case PERF_RECORD_SWITCH_CPU_WIDE: + min_size =3D sizeof(struct perf_record_switch); + break; + default: + break; } + if (event->header.size < min_size) + return PyErr_Format(PyExc_ValueError, "Event size %u too small for type = %u", + event->header.size, event->header.type); + + /* Allocate just enough memory for the size of event. */ + size =3D offsetof(struct pyrf_event, event) + event->header.size; + pevent =3D (struct pyrf_event *)PyObject_Malloc(size); + if (pevent =3D=3D NULL) + return PyErr_NoMemory(); =20 - ptype =3D pyrf_event__type[event->header.type]; - pevent =3D PyObject_New(struct pyrf_event, ptype); - if (pevent !=3D NULL) { - memcpy(&pevent->event, event, event->header.size); - perf_sample__init(&pevent->sample, /*all=3D*/false); + /* Copy the event for memory safety and initilaize variables. */ + PyObject_Init((PyObject *)pevent, ptype); + memcpy(&pevent->event, event, event->header.size); + perf_sample__init(&pevent->sample, /*all=3D*/true); + pevent->al_resolved =3D false; + addr_location__init(&pevent->al); + + if (!evsel) + return (PyObject *)pevent; + + /* Parse the sample again so that pointers are within the copied event. */ + err =3D evsel__parse_sample(evsel, &pevent->event, &pevent->sample); + if (err < 0) { + Py_DECREF(pevent); + return PyErr_Format(PyExc_OSError, + "perf: can't parse sample, err=3D%d", err); } return (PyObject *)pevent; } @@ -1209,7 +1587,7 @@ static PyObject *pyrf_evsel__str(PyObject *self) struct pyrf_evsel *pevsel =3D (void *)self; struct evsel *evsel =3D pevsel->evsel; =20 - return PyUnicode_FromFormat("evsel(%s/%s/)", evsel__pmu_name(evsel), evse= l__name(evsel)); + return PyUnicode_FromFormat("evsel(%s)", evsel__name(evsel)); } =20 static PyMethodDef pyrf_evsel__methods[] =3D { @@ -1771,9 +2149,11 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyr= f_evlist *pevlist, { struct evlist *evlist =3D pevlist->evlist; union perf_event *event; + struct evsel *evsel; int sample_id_all =3D 1, cpu; static char *kwlist[] =3D { "cpu", "sample_id_all", NULL }; struct mmap *md; + PyObject *pyevent; int err; =20 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, @@ -1781,44 +2161,31 @@ static PyObject *pyrf_evlist__read_on_cpu(struct py= rf_evlist *pevlist, return NULL; =20 md =3D get_md(evlist, cpu); - if (!md) { - PyErr_Format(PyExc_TypeError, "Unknown CPU '%d'", cpu); - return NULL; - } + if (!md) + return PyErr_Format(PyExc_TypeError, "Unknown CPU '%d'", cpu); =20 - if (perf_mmap__read_init(&md->core) < 0) - goto end; + err =3D perf_mmap__read_init(&md->core); + if (err < 0) { + return PyErr_Format(PyExc_OSError, + "perf: error mmap read init, err=3D%d", err); + } =20 event =3D perf_mmap__read_event(&md->core); - if (event !=3D NULL) { - PyObject *pyevent =3D pyrf_event__new(event); - struct pyrf_event *pevent =3D (struct pyrf_event *)pyevent; - struct evsel *evsel; - - if (pyevent =3D=3D NULL) - return PyErr_NoMemory(); - - evsel =3D evlist__event2evsel(evlist, event); - if (!evsel) { - Py_DECREF(pyevent); - Py_INCREF(Py_None); - return Py_None; - } + if (event =3D=3D NULL) + Py_RETURN_NONE; =20 + evsel =3D evlist__event2evsel(evlist, event); + if (!evsel) { + /* Unknown evsel. */ perf_mmap__consume(&md->core); - - err =3D evsel__parse_sample(evsel, &pevent->event, &pevent->sample); - if (err) { - Py_DECREF(pyevent); - return PyErr_Format(PyExc_OSError, - "perf: can't parse sample, err=3D%d", err); - } - - return pyevent; + Py_RETURN_NONE; } -end: - Py_INCREF(Py_None); - return Py_None; + pyevent =3D pyrf_event__new(event, evsel, evlist__session(evlist)); + perf_mmap__consume(&md->core); + if (pyevent =3D=3D NULL) + return PyErr_Occurred() ? NULL : PyErr_NoMemory(); + + return pyevent; } =20 static PyObject *pyrf_evlist__open(struct pyrf_evlist *pevlist, @@ -2013,10 +2380,7 @@ static PyObject *pyrf_evlist__str(PyObject *self) evlist__for_each_entry(pevlist->evlist, pos) { if (!first) strbuf_addch(&sb, ','); - if (!pos->pmu) - strbuf_addstr(&sb, evsel__name(pos)); - else - strbuf_addf(&sb, "%s/%s/", pos->pmu->name, evsel__name(pos)); + strbuf_addstr(&sb, evsel__name(pos)); first =3D false; } strbuf_addstr(&sb, "])"); @@ -2499,24 +2863,12 @@ static int pyrf_session_tool__sample(const struct p= erf_tool *tool, struct machine *machine __maybe_unused) { struct pyrf_session *psession =3D container_of(tool, struct pyrf_session,= tool); - PyObject *pyevent =3D pyrf_event__new(event); - struct pyrf_event *pevent =3D (struct pyrf_event *)pyevent; + PyObject *pyevent =3D pyrf_event__new(event, sample->evsel, psession->ses= sion); PyObject *ret; =20 if (pyevent =3D=3D NULL) return -ENOMEM; =20 - memcpy(&pevent->event, event, event->header.size); - if (evsel__parse_sample(evsel, &pevent->event, &pevent->sample) < 0) { - Py_DECREF(pyevent); - return -1; - } - /* Avoid shallow copy pointing to lazily allocated memory that would be d= ouble freed. */ - pevent->sample.user_regs =3D NULL; - pevent->sample.intr_regs =3D NULL; - if (pevent->sample.merged_callchain) - pevent->sample.callchain =3D NULL; - ret =3D PyObject_CallFunction(psession->sample, "O", pyevent); if (!ret) { PyErr_Print(); --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 47F1639A7E0 for ; Sat, 25 Apr 2026 22:50:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157453; cv=none; b=lIscvujWxoPV8/AuA7LsVe/7ASjInuK5h2Eji4E5agzT7SmdUmX0JB2RK0J4FU1LKq4Y58W3k9+3Lzua9jeLoOxRFIvGiMdb8wRv861oEzsae8E2UlgAqJEvqfNQkFZbHcexlTHDhjIMzCuK4ggsVwzmbeiQ09nX4Fvg8UUDqA8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157453; c=relaxed/simple; bh=KN5i1jN6Kb78peV6jKL5TpWLveWGVlk5wDsutMsOQkA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=LnEE6RyEdY3HTWSMiWUhCkjHR+HnFFBvLFfZXaN18ul9oWkdME0i70A97DbHQgz1xSy/6gOZZ7nEdaEXC+xN0mnapzoTetfMhgEbXgFHCDwlsVfhgqFZKBvssjVoMapRIxcPwPPqEOdR+CB2o/C54YvC0EjQrkOEXFpNQFx/MQE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=jifbgMBC; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="jifbgMBC" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-1270dcd11c1so5627433c88.0 for ; Sat, 25 Apr 2026 15:50:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157450; x=1777762250; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=teyYQp0E3EyOIZgpUXSUO0oKReWVKi9UUmdHMeA+/YA=; b=jifbgMBCMbCJj05hy95ajIcIjO/zDKBmBSIQpbG/DC4kfixtOAVUHShlMb1rIm6Rk4 YVaY+Qyb3erf3anRiWpJwWURXZZNP8pteWjQqAMfGW989vi7VetpgpM5ffU4cF0CEB9n QJ94zOir/wGDzMOZRg5Zs4Sb9l4dB3qZxuAw95HoaIZmCfzkpMF6eK1VvB1mYp4yTvTf 5Cg2+JSuwt3HSGiOJzBJrY3BsCBkFgDX8NjcqVkWg235WMHQWnuRxL3CosxhCQeFvSll oumpwDsIIikRMmrYE/mg+9TSjZO9byel9WwBHEmLwjew3joJpXO2jxmWghhCjSKMqgWV W/2Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157450; x=1777762250; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=teyYQp0E3EyOIZgpUXSUO0oKReWVKi9UUmdHMeA+/YA=; b=GDFgguRZ2tx7nMwQbuO4w2YWLU+5MPsGYCB91+zkugirhganjVWNz7gnsLRkTREMCs mGiBDBY22Nx+KfRgWDLhXfgvvrcNfvqN8x2eKLHoGDfAr/cmaiMOCW3hB43As1Q6J+Sp 7RP8w4JVTTZCo/anCKP6p2IE/wJ6XvtS98xyMvoWQaSsB44dqTFe5LrA99ROwOYHe+EH 6n7WBc5BAdU+AawnAi4H2O45okwmp0BMs+aJvVt8R0xC9/XoiL9n72HAhPOf5g8jSh0e fsWIZsds1PR2eJZCaAHmr1IFe6JtmNWI8XecrUq42ihFDCmxw+rRmP2Z4gH39f7vuqNC w7Iw== X-Forwarded-Encrypted: i=1; AFNElJ8TgM+dCnyVlvxtQLw2LM4qvyLH0mdwl/BDbmra9XuHQ9BFuo7FWDag2sCDaEGMfE2IYcHpmfkvHrDR0zI=@vger.kernel.org X-Gm-Message-State: AOJu0Ywj8f54bdyKsxmxzb344WL9PEwquMI8vGaDUdVUxhYwqgO8thON eLxZgXenBkXpb1OW/bK41oFjiKqI3t4qh+k9xdCj0tBRgjlfgpSHn6mmrHyUHSZvDWjWynrV7Ed ISrG/PI2BTQ== X-Received: from dlad28.prod.google.com ([2002:a05:701b:221c:b0:12a:7f44:d2e3]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:6981:b0:12c:856:ddcc with SMTP id a92af1059eb24-12c73fa3c9amr19854524c88.27.1777157450294; Sat, 25 Apr 2026 15:50:50 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:10 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-19-irogers@google.com> Subject: [PATCH v7 18/59] perf python: Add callchain support From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Implement pyrf_callchain_node and pyrf_callchain types for lazy iteration over callchain frames. Add callchain property to sample_event. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Eager Callchain Resolution: Moved the callchain resolution from deferred iteration to eager processing in pyrf_session_tool__sample() . This avoids risks of reading from unmapped memory or following dangling pointers to closed sessions. 2. Cached Callchain: Added a callchain field to struct pyrf_event to store the resolved object. 3. Simplified Access: pyrf_sample_event__get_callchain() now just returns the cached object if available. 4. Avoided Double Free: Handled lazy cleanups properly. v6: - Moved callchain resolution from `session_tool__sample` to `pyrf_event__new`. --- tools/perf/util/python.c | 241 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 240 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 824cf58645e0..2953c4c8e142 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -66,6 +66,8 @@ struct pyrf_event { struct addr_location al; /** @al_resolved: True when machine__resolve been called. */ bool al_resolved; + /** @callchain: Resolved callchain, eagerly computed if requested. */ + PyObject *callchain; /** @event: The underlying perf_event that may be in a file or ring buffe= r. */ union perf_event event; }; @@ -103,6 +105,7 @@ static void pyrf_event__delete(struct pyrf_event *peven= t) { if (pevent->al_resolved) addr_location__exit(&pevent->al); + Py_XDECREF(pevent->callchain); perf_sample__exit(&pevent->sample); Py_TYPE(pevent)->tp_free((PyObject *)pevent); } @@ -621,6 +624,181 @@ static PyObject *pyrf_sample_event__insn(PyObject *se= lf, PyObject *args __maybe_ pevent->sample.insn_len); } =20 +struct pyrf_callchain_node { + PyObject_HEAD + u64 ip; + struct map *map; + struct symbol *sym; +}; + +static void pyrf_callchain_node__delete(struct pyrf_callchain_node *pnode) +{ + map__put(pnode->map); + Py_TYPE(pnode)->tp_free((PyObject*)pnode); +} + +static PyObject *pyrf_callchain_node__get_ip(struct pyrf_callchain_node *p= node, + void *closure __maybe_unused) +{ + return PyLong_FromUnsignedLongLong(pnode->ip); +} + +static PyObject *pyrf_callchain_node__get_symbol(struct pyrf_callchain_nod= e *pnode, + void *closure __maybe_unused) +{ + if (pnode->sym) + return PyUnicode_FromString(pnode->sym->name); + return PyUnicode_FromString("[unknown]"); +} + +static PyObject *pyrf_callchain_node__get_dso(struct pyrf_callchain_node *= pnode, + void *closure __maybe_unused) +{ + const char *dsoname =3D "[unknown]"; + + if (pnode->map) { + struct dso *dso =3D map__dso(pnode->map); + if (dso) { + if (symbol_conf.show_kernel_path && dso__long_name(dso)) + dsoname =3D dso__long_name(dso); + else + dsoname =3D dso__name(dso); + } + } + return PyUnicode_FromString(dsoname); +} + +static PyGetSetDef pyrf_callchain_node__getset[] =3D { + { .name =3D "ip", .get =3D (getter)pyrf_callchain_node__get_ip, }, + { .name =3D "symbol", .get =3D (getter)pyrf_callchain_node__get_symbol, }, + { .name =3D "dso", .get =3D (getter)pyrf_callchain_node__get_dso, }, + { .name =3D NULL, }, +}; + +static PyTypeObject pyrf_callchain_node__type =3D { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name =3D "perf.callchain_node", + .tp_basicsize =3D sizeof(struct pyrf_callchain_node), + .tp_dealloc =3D (destructor)pyrf_callchain_node__delete, + .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc =3D "perf callchain node object.", + .tp_getset =3D pyrf_callchain_node__getset, +}; + +struct pyrf_callchain_frame { + u64 ip; + struct map *map; + struct symbol *sym; +}; + +struct pyrf_callchain { + PyObject_HEAD + struct pyrf_event *pevent; + struct pyrf_callchain_frame *frames; + u64 nr_frames; + u64 pos; + bool resolved; +}; + +static void pyrf_callchain__delete(struct pyrf_callchain *pchain) +{ + Py_XDECREF(pchain->pevent); + if (pchain->frames) { + for (u64 i =3D 0; i < pchain->nr_frames; i++) + map__put(pchain->frames[i].map); + free(pchain->frames); + } + Py_TYPE(pchain)->tp_free((PyObject*)pchain); +} + +static PyObject *pyrf_callchain__next(struct pyrf_callchain *pchain) +{ + struct pyrf_callchain_node *pnode; + + if (!pchain->resolved) { + struct evsel *evsel =3D pchain->pevent->sample.evsel; + struct evlist *evlist =3D evsel->evlist; + struct perf_session *session =3D evlist ? evlist__session(evlist) : NULL; + struct addr_location al; + struct callchain_cursor *cursor; + struct callchain_cursor_node *node; + u64 i; + + if (!session || !pchain->pevent->sample.callchain) + return NULL; + + addr_location__init(&al); + if (machine__resolve(&session->machines.host, &al, &pchain->pevent->samp= le) < 0) { + addr_location__exit(&al); + return NULL; + } + + cursor =3D get_tls_callchain_cursor(); + if (thread__resolve_callchain(al.thread, cursor, evsel, + &pchain->pevent->sample, NULL, NULL, + PERF_MAX_STACK_DEPTH) !=3D 0) { + addr_location__exit(&al); + return NULL; + } + callchain_cursor_commit(cursor); + + pchain->nr_frames =3D cursor->nr; + if (pchain->nr_frames > 0) { + pchain->frames =3D calloc(pchain->nr_frames, sizeof(*pchain->frames)); + if (!pchain->frames) { + addr_location__exit(&al); + return PyErr_NoMemory(); + } + + for (i =3D 0; i < pchain->nr_frames; i++) { + node =3D callchain_cursor_current(cursor); + pchain->frames[i].ip =3D node->ip; + pchain->frames[i].map =3D map__get(node->ms.map); + pchain->frames[i].sym =3D node->ms.sym; + callchain_cursor_advance(cursor); + } + } + pchain->resolved =3D true; + addr_location__exit(&al); + } + + if (pchain->pos >=3D pchain->nr_frames) + return NULL; + + pnode =3D PyObject_New(struct pyrf_callchain_node, &pyrf_callchain_node__= type); + if (!pnode) + return NULL; + + pnode->ip =3D pchain->frames[pchain->pos].ip; + pnode->map =3D map__get(pchain->frames[pchain->pos].map); + pnode->sym =3D pchain->frames[pchain->pos].sym; + + pchain->pos++; + return (PyObject *)pnode; +} + +static PyTypeObject pyrf_callchain__type =3D { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name =3D "perf.callchain", + .tp_basicsize =3D sizeof(struct pyrf_callchain), + .tp_dealloc =3D (destructor)pyrf_callchain__delete, + .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc =3D "perf callchain object.", + .tp_iter =3D PyObject_SelfIter, + .tp_iternext =3D (iternextfunc)pyrf_callchain__next, +}; + +static PyObject *pyrf_sample_event__get_callchain(PyObject *self, void *cl= osure __maybe_unused) +{ + struct pyrf_event *pevent =3D (void *)self; + + if (!pevent->callchain) + Py_RETURN_NONE; + + Py_INCREF(pevent->callchain); + return pevent->callchain; +} + static PyObject* pyrf_sample_event__getattro(struct pyrf_event *pevent, PyObject *attr_name) { @@ -635,6 +813,12 @@ pyrf_sample_event__getattro(struct pyrf_event *pevent,= PyObject *attr_name) } =20 static PyGetSetDef pyrf_sample_event__getset[] =3D { + { + .name =3D "callchain", + .get =3D pyrf_sample_event__get_callchain, + .set =3D NULL, + .doc =3D "event callchain.", + }, { .name =3D "raw_buf", .get =3D (getter)pyrf_sample_event__get_raw_buf, @@ -803,6 +987,12 @@ static int pyrf_event__setup_types(void) err =3D PyType_Ready(&pyrf_context_switch_event__type); if (err < 0) goto out; + err =3D PyType_Ready(&pyrf_callchain_node__type); + if (err < 0) + goto out; + err =3D PyType_Ready(&pyrf_callchain__type); + if (err < 0) + goto out; out: return err; } @@ -822,10 +1012,12 @@ static PyTypeObject *pyrf_event__type[] =3D { }; =20 static PyObject *pyrf_event__new(const union perf_event *event, struct evs= el *evsel, - struct perf_session *session __maybe_unused) + struct perf_session *session) { struct pyrf_event *pevent; PyTypeObject *ptype; + struct perf_sample *sample; + struct machine *machine =3D session ? &session->machines.host : NULL; size_t size; int err; size_t min_size =3D sizeof(struct perf_event_header); @@ -883,6 +1075,7 @@ static PyObject *pyrf_event__new(const union perf_even= t *event, struct evsel *ev PyObject_Init((PyObject *)pevent, ptype); memcpy(&pevent->event, event, event->header.size); perf_sample__init(&pevent->sample, /*all=3D*/true); + pevent->callchain =3D NULL; pevent->al_resolved =3D false; addr_location__init(&pevent->al); =20 @@ -896,6 +1089,49 @@ static PyObject *pyrf_event__new(const union perf_eve= nt *event, struct evsel *ev return PyErr_Format(PyExc_OSError, "perf: can't parse sample, err=3D%d", err); } + sample =3D &pevent->sample; + if (machine && sample->callchain) { + struct addr_location al; + struct callchain_cursor *cursor; + u64 i; + struct pyrf_callchain *pchain; + + addr_location__init(&al); + if (machine__resolve(machine, &al, sample) >=3D 0) { + cursor =3D get_tls_callchain_cursor(); + if (thread__resolve_callchain(al.thread, cursor, evsel, sample, + NULL, NULL, PERF_MAX_STACK_DEPTH) =3D=3D 0) { + callchain_cursor_commit(cursor); + + pchain =3D PyObject_New(struct pyrf_callchain, &pyrf_callchain__type); + if (pchain) { + pchain->pevent =3D pevent; + Py_INCREF(pevent); + pchain->nr_frames =3D cursor->nr; + pchain->pos =3D 0; + pchain->resolved =3D true; + pchain->frames =3D calloc(pchain->nr_frames, + sizeof(*pchain->frames)); + if (pchain->frames) { + struct callchain_cursor_node *node; + + for (i =3D 0; i < pchain->nr_frames; i++) { + node =3D callchain_cursor_current(cursor); + pchain->frames[i].ip =3D node->ip; + pchain->frames[i].map =3D + map__get(node->ms.map); + pchain->frames[i].sym =3D node->ms.sym; + callchain_cursor_advance(cursor); + } + pevent->callchain =3D (PyObject *)pchain; + } else { + Py_DECREF(pchain); + } + } + } + addr_location__exit(&al); + } + } return (PyObject *)pevent; } =20 @@ -2959,6 +3195,9 @@ static int pyrf_session__init(struct pyrf_session *ps= ession, PyObject *args, PyO return -1; } =20 + symbol_conf.use_callchain =3D true; + symbol_conf.show_kernel_path =3D true; + symbol_conf.inline_name =3D false; if (symbol__init(perf_session__env(psession->session)) < 0) { perf_session__delete(psession->session); psession->session =3D NULL; --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F0AB239B96D for ; Sat, 25 Apr 2026 22:50:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157455; cv=none; b=f7L1NjzqbeiIE8v7dBw9lc2gchBvBUNSsiiQrigvI4lCYFmmEN1DpJMH9nXLIDDvs5f+YWT7Kl7MBCW4HPPRJMNCeCU+CPxvTGk40yXq/k3b9RZk49DlVtkHo1yXxiIVc2wL4c0/3kNC0TnCjFTZQnmvmhy7yvnK6loenFGFO4I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157455; c=relaxed/simple; bh=PiF/VN47oVtkaq0UVg1dETvbcKU6+L/0hmPAremZV2Q=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=DFkBem3Z3v90eC5UcOOaTFsqzyxn0qmMK9Bgd54U0kOfjoqeMYxJ4ApyKStQrrfrofpRHtWCuCbMvqwfRF+E6mcfU7YLFYsR9xdflwqnntIoeBRyup9h3gViyOHupgBhLz6Nd+eGYKmW6QwI+D6JDEZAEO028z68AzYZawPPBYc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=WOrDsRxn; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="WOrDsRxn" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2c0f6593ef5so11439685eec.1 for ; Sat, 25 Apr 2026 15:50:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157453; x=1777762253; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=5qNJyUA9ryt9seTGqwkY6UAT2Vxtn3jg366bqqeB58s=; b=WOrDsRxnc4jna2Hn+XCzrmGDemPTwi3kxJACJrfswIn8mZspvcfEtS7cRSgl9IxtMG TiiFPmVT8e1Lk56sczVV5d2sEZQultodkm1UYcpp8STiaX4ef+wVGUP+ZXN9Qyt4Cvbp LXBglp+rZH+IErTJzlgmxoZRMTmoJyAeQBlq1BCsMKlNLQx/bHdnyKFZhI2tAad5luCG 5UCjYuq8Y51/UEYNEA4gSjEhFOsVJPTgjQbcrMbTd3JtLmwjtQmlDJnO4mWEKYV+Lt04 Ig26lvtMb9baoujSZZxFFqCaj6iWoH90SHRSpHH+l8OLPJqvQacbcjvT6HHpa7e3gRdV 0Dcw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157453; x=1777762253; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=5qNJyUA9ryt9seTGqwkY6UAT2Vxtn3jg366bqqeB58s=; b=NSpftt89EgPHcrmsmPVOI5Hj/lGgJOECIfpH5hqfE0+6qDKzIJCn0z7HJmAGj/tTO3 HyzylmgdLMJHi62xB2gz7wA6rovyBvfmsih0k6uOGpx/b1Nc9m0ROnWaA6+UhIQySurl 2XHrsFfzix+wAqBg69zEBMwR6/QVe8B/q9aEg9nz8YSh/CXuKA7LaRQr5+xG9dLQILpP ICPzaZWT9xWJixGv2Z9pcW1XvYKoJ3WnLpRlOdQB/9kOa60PmF36J/GGqeHiitOFJHXx ExnB0SiHtyyFGUAbRMAOZvFUDzZegRC0FfrMrNiiy4D12vr7LVrZz2byXsGt1Cl7AwcP iiuw== X-Forwarded-Encrypted: i=1; AFNElJ+0MPsmR7L+R8K7YivUnfD6Wpe/2M342ywHye0H//qMhTf25LjRX69B+f/YXtS6PD/bNZr2m+cXIggurik=@vger.kernel.org X-Gm-Message-State: AOJu0YzVSQS+jKiHp7CEiYNb9xjCYUvOPLhCeVKMQXFwuMN5WbAsA60d N8sOWTcMG3a+oH3l9W8Pi2WFVh+5dPXg7dyOGyMFZCfyQegxd6GCGBzd/VTmfBc1ofSidfC3k3q OFKIDZncsNw== X-Received: from dybaz36.prod.google.com ([2002:a05:7300:6c24:b0:2da:2525:9f3]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7301:f90:b0:2ea:7901:8d72 with SMTP id 5a478bee46e88-2ea79019c06mr8624190eec.17.1777157452753; Sat, 25 Apr 2026 15:50:52 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:11 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-20-irogers@google.com> Subject: [PATCH v7 19/59] perf python: Add config file access From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add perf.config_get(name) to expose the perf configuration system. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- tools/perf/util/python.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 2953c4c8e142..5478561ca62c 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -12,6 +12,7 @@ #include "build-id.h" #include "callchain.h" #include "comm.h" +#include "config.h" #include "counts.h" #include "data.h" #include "debug.h" @@ -3296,7 +3297,26 @@ static PyObject *pyrf__syscall_id(PyObject *self, Py= Object *args, PyObject *kwar return PyLong_FromLong(id); } =20 +static PyObject *pyrf__config_get(PyObject *self, PyObject *args) +{ + const char *config_name, *val; + + if (!PyArg_ParseTuple(args, "s", &config_name)) + return NULL; + + val =3D perf_config_get(config_name); + if (!val) + Py_RETURN_NONE; + return PyUnicode_FromString(val); +} + static PyMethodDef perf__methods[] =3D { + { + .ml_name =3D "config_get", + .ml_meth =3D (PyCFunction) pyrf__config_get, + .ml_flags =3D METH_VARARGS, + .ml_doc =3D PyDoc_STR("Get a perf config value.") + }, { .ml_name =3D "metrics", .ml_meth =3D (PyCFunction) pyrf__metrics, --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1A52C39C631 for ; Sat, 25 Apr 2026 22:50:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157459; cv=none; b=jKheDmd7WOqaWr+I9vI7pMv8x7ymWk1gfORtnuJmqYXYPbH53XbnedkCpe47cmj2dseP4aqo4Rx+8DOuwAqJqp/TIuG++lMduBlzwNPPUjjz8YTPTXx0y9/o0GY8j7599r3DGXg841a7tomusfwB5f1SAOgGjMXv7bwzIkxWg6k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157459; c=relaxed/simple; bh=/9+s+2uN+0FSb3j4s+R0LP1vky14f3XNoupPJzN7HwQ=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=FvuPMowS3tv/YwOjAX3jnTNJwHlc7w9H/iEVM4SOyYajzDB6544vzSZ1hkzJqU5OO3XobRhyAXph5/ce8HsQ18UGzTI2UmFJm0NeLXan0WUGCeZS7K7cxU3QzQvuWDSRLbXVfGtRUqtALbnknLZDt2mBBavpVXko3qpo8ZAbpuc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=DjgYhSgp; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="DjgYhSgp" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2c0f6593ef5so11439704eec.1 for ; Sat, 25 Apr 2026 15:50:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157455; x=1777762255; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=LehBsqXOO+3vyC/RHNJFTkhwNHC6p/PwtZuYnp7DrgM=; b=DjgYhSgpF3WuxEnqilbxjd9nVMdukrf+0J/p3Svy4shJzYzFzbfLz8Ac4xJ9GbclgV 0hQdmRqCc7O0IS0MC4trV7yYmlblIzHXnSPWfuZ4nvoPud6D5PZEpu1pEHJsCS1cNa22 Ux+hVPSH4L+zCe2bCF124eIvYe5GxYyBEJMyNr3G09IenPq+5+erHIAxPL2ujhSh4EH6 /AycOWQuuBm+k2X+Q49r4JpwwFcxK9wleUNI4B3mIin/P6D0+rKoMH4uu9vlQruudXTL fZHht6NVQQi7yV3//OoJSfhjZlb/qiwUP/2m6MDvV5L8zVyc4sUnXdvdgEKpTOM11NK1 tuow== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157455; x=1777762255; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=LehBsqXOO+3vyC/RHNJFTkhwNHC6p/PwtZuYnp7DrgM=; b=NEAlF41nWyjl2X+aPpc6bcDKsS3fAnj0MZaGkdqiRRW2A/ko1A705i1GnFr3nZXbbO FKKQoBGOlMnBk1Vrc+FXYQ4zf4Uv5FKdytbD109Wx3aCduJDQjF8on9SsjdNZ5+rniKY 5ofh3mrR39r1ax312zexdJJu4f0q8KHunmFjrM9f6ciLagv7UQadiZVXqCR9UpBY5ZSk jwoeva0QCQiJeuYuwd0jv9y2296vAEvEk8P7hXaW0/oGut6tgNk8SGUDvjeZpYco4XfZ iydfLhjLjjN85f7DTMmoRe+G4zwZVn8tYlZwJGJGozNzA74BZJY/Y8gYR+PHUJTFZrk9 SYGQ== X-Forwarded-Encrypted: i=1; AFNElJ+C/b+REol4va5YA0l7SAeF5P7ZR0BVNMBxpYmDyzAfbZPmBS/KcWrGg8NANyxFkBrbJZYsCJnjlmUa6P4=@vger.kernel.org X-Gm-Message-State: AOJu0YwyBW7o2SChVmbJMsUPI8UN19aN9umiFn9LNhaWxsjWHxYapPNH 6cGcHzfRfY3O0HC9NWKcmwfGTzroYrq3j1OvZ8bUp9c49+6ETIcaGSO7x997QWNAlBDiFHmDNg9 ck8RgBzYy4A== X-Received: from dycw20.prod.google.com ([2002:a05:7300:2154:b0:2d6:2fd7:91f8]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7301:4288:b0:2da:2ec2:64e5 with SMTP id 5a478bee46e88-2e479c042b2mr13816235eec.18.1777157454743; Sat, 25 Apr 2026 15:50:54 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:12 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-21-irogers@google.com> Subject: [PATCH v7 20/59] perf python: Extend API for stat events in python.c From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add stat information to the session. Add call backs for stat events. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v5: 1. Fix Memory Corruption: Corrected the memory offset for `stat_round_type` in `pyrf_stat_round_event__members` by adding the base offset of `struct pyrf_event`. 2. Fix Memory Leak: Added `Py_XDECREF()` to free the unused return value of the Python callback in `pyrf_session_tool__stat_round()`. --- v7: - Added comprehensive size checks in pyrf_event__new for all event types. - Fixed line length warning. --- tools/perf/util/python.c | 271 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 264 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 5478561ca62c..17d0ee15336f 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -299,6 +299,77 @@ static PyTypeObject pyrf_lost_event__type =3D { .tp_repr =3D (reprfunc)pyrf_lost_event__repr, }; =20 +static const char pyrf_stat_event__doc[] =3D PyDoc_STR("perf stat event ob= ject."); + +static PyMemberDef pyrf_stat_event__members[] =3D { + sample_members + member_def(perf_event_header, type, T_UINT, "event type"), + member_def(perf_record_stat, id, T_ULONGLONG, "event id"), + member_def(perf_record_stat, cpu, T_UINT, "event cpu"), + member_def(perf_record_stat, thread, T_UINT, "event thread"), + member_def(perf_record_stat, val, T_ULONGLONG, "counter value"), + member_def(perf_record_stat, ena, T_ULONGLONG, "enabled time"), + member_def(perf_record_stat, run, T_ULONGLONG, "running time"), + { .name =3D NULL, }, +}; + +static PyObject *pyrf_stat_event__repr(const struct pyrf_event *pevent) +{ + return PyUnicode_FromFormat( + "{ type: stat, id: %llu, cpu: %u, thread: %u, val: %llu, ena: %llu, run:= %llu }", + pevent->event.stat.id, + pevent->event.stat.cpu, + pevent->event.stat.thread, + pevent->event.stat.val, + pevent->event.stat.ena, + pevent->event.stat.run); +} + +static PyTypeObject pyrf_stat_event__type =3D { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name =3D "perf.stat_event", + .tp_basicsize =3D sizeof(struct pyrf_event), + .tp_new =3D PyType_GenericNew, + .tp_dealloc =3D (destructor)pyrf_event__delete, + .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc =3D pyrf_stat_event__doc, + .tp_members =3D pyrf_stat_event__members, + .tp_getset =3D pyrf_event__getset, + .tp_repr =3D (reprfunc)pyrf_stat_event__repr, +}; + +static const char pyrf_stat_round_event__doc[] =3D PyDoc_STR("perf stat ro= und event object."); + +static PyMemberDef pyrf_stat_round_event__members[] =3D { + sample_members + member_def(perf_event_header, type, T_UINT, "event type"), + { .name =3D "stat_round_type", .type =3D T_ULONGLONG, + .offset =3D offsetof(struct pyrf_event, event) + offsetof(struct perf_r= ecord_stat_round, type), + .doc =3D "round type" }, + member_def(perf_record_stat_round, time, T_ULONGLONG, "round time"), + { .name =3D NULL, }, +}; + +static PyObject *pyrf_stat_round_event__repr(const struct pyrf_event *peve= nt) +{ + return PyUnicode_FromFormat("{ type: stat_round, type: %llu, time: %llu }= ", + pevent->event.stat_round.type, + pevent->event.stat_round.time); +} + +static PyTypeObject pyrf_stat_round_event__type =3D { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name =3D "perf.stat_round_event", + .tp_basicsize =3D sizeof(struct pyrf_event), + .tp_new =3D PyType_GenericNew, + .tp_dealloc =3D (destructor)pyrf_event__delete, + .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc =3D pyrf_stat_round_event__doc, + .tp_members =3D pyrf_stat_round_event__members, + .tp_getset =3D pyrf_event__getset, + .tp_repr =3D (reprfunc)pyrf_stat_round_event__repr, +}; + static const char pyrf_read_event__doc[] =3D PyDoc_STR("perf read event ob= ject."); =20 static PyMemberDef pyrf_read_event__members[] =3D { @@ -986,6 +1057,12 @@ static int pyrf_event__setup_types(void) if (err < 0) goto out; err =3D PyType_Ready(&pyrf_context_switch_event__type); + if (err < 0) + goto out; + err =3D PyType_Ready(&pyrf_stat_event__type); + if (err < 0) + goto out; + err =3D PyType_Ready(&pyrf_stat_round_event__type); if (err < 0) goto out; err =3D PyType_Ready(&pyrf_callchain_node__type); @@ -1010,6 +1087,8 @@ static PyTypeObject *pyrf_event__type[] =3D { [PERF_RECORD_SAMPLE] =3D &pyrf_sample_event__type, [PERF_RECORD_SWITCH] =3D &pyrf_context_switch_event__type, [PERF_RECORD_SWITCH_CPU_WIDE] =3D &pyrf_context_switch_event__type, + [PERF_RECORD_STAT] =3D &pyrf_stat_event__type, + [PERF_RECORD_STAT_ROUND] =3D &pyrf_stat_round_event__type, }; =20 static PyObject *pyrf_event__new(const union perf_event *event, struct evs= el *evsel, @@ -1026,7 +1105,9 @@ static PyObject *pyrf_event__new(const union perf_eve= nt *event, struct evsel *ev if ((event->header.type < PERF_RECORD_MMAP || event->header.type > PERF_RECORD_SAMPLE) && !(event->header.type =3D=3D PERF_RECORD_SWITCH || - event->header.type =3D=3D PERF_RECORD_SWITCH_CPU_WIDE)) { + event->header.type =3D=3D PERF_RECORD_SWITCH_CPU_WIDE || + event->header.type =3D=3D PERF_RECORD_STAT || + event->header.type =3D=3D PERF_RECORD_STAT_ROUND)) { PyErr_Format(PyExc_TypeError, "Unexpected header type %u", event->header.type); return NULL; @@ -1038,6 +1119,9 @@ static PyObject *pyrf_event__new(const union perf_eve= nt *event, struct evsel *ev case PERF_RECORD_MMAP: min_size =3D sizeof(struct perf_record_mmap); break; + case PERF_RECORD_MMAP2: + min_size =3D sizeof(struct perf_record_mmap2); + break; case PERF_RECORD_COMM: min_size =3D sizeof(struct perf_record_comm); break; @@ -1045,13 +1129,13 @@ static PyObject *pyrf_event__new(const union perf_e= vent *event, struct evsel *ev case PERF_RECORD_EXIT: min_size =3D sizeof(struct perf_record_fork); break; + case PERF_RECORD_LOST: + min_size =3D sizeof(struct perf_record_lost); + break; case PERF_RECORD_THROTTLE: case PERF_RECORD_UNTHROTTLE: min_size =3D sizeof(struct perf_record_throttle); break; - case PERF_RECORD_LOST: - min_size =3D sizeof(struct perf_record_lost); - break; case PERF_RECORD_READ: min_size =3D sizeof(struct perf_record_read); break; @@ -1059,6 +1143,96 @@ static PyObject *pyrf_event__new(const union perf_ev= ent *event, struct evsel *ev case PERF_RECORD_SWITCH_CPU_WIDE: min_size =3D sizeof(struct perf_record_switch); break; + case PERF_RECORD_AUX: + min_size =3D sizeof(struct perf_record_aux); + break; + case PERF_RECORD_ITRACE_START: + min_size =3D sizeof(struct perf_record_itrace_start); + break; + case PERF_RECORD_LOST_SAMPLES: + min_size =3D sizeof(struct perf_record_lost_samples); + break; + case PERF_RECORD_NAMESPACES: + min_size =3D sizeof(struct perf_record_namespaces); + break; + case PERF_RECORD_KSYMBOL: + min_size =3D sizeof(struct perf_record_ksymbol); + break; + case PERF_RECORD_BPF_EVENT: + min_size =3D sizeof(struct perf_record_bpf_event); + break; + case PERF_RECORD_CGROUP: + min_size =3D sizeof(struct perf_record_cgroup); + break; + case PERF_RECORD_TEXT_POKE: + min_size =3D sizeof(struct perf_record_text_poke_event); + break; + case PERF_RECORD_AUX_OUTPUT_HW_ID: + min_size =3D sizeof(struct perf_record_aux_output_hw_id); + break; + case PERF_RECORD_CALLCHAIN_DEFERRED: + min_size =3D sizeof(struct perf_record_callchain_deferred); + break; + case PERF_RECORD_HEADER_ATTR: + min_size =3D sizeof(struct perf_record_header_attr); + break; + case PERF_RECORD_HEADER_TRACING_DATA: + min_size =3D sizeof(struct perf_record_header_tracing_data); + break; + case PERF_RECORD_HEADER_BUILD_ID: + min_size =3D sizeof(struct perf_record_header_build_id); + break; + case PERF_RECORD_ID_INDEX: + min_size =3D sizeof(struct perf_record_id_index); + break; + case PERF_RECORD_AUXTRACE_INFO: + min_size =3D sizeof(struct perf_record_auxtrace_info); + break; + case PERF_RECORD_AUXTRACE: + min_size =3D sizeof(struct perf_record_auxtrace); + break; + case PERF_RECORD_AUXTRACE_ERROR: + min_size =3D sizeof(struct perf_record_auxtrace_error); + break; + case PERF_RECORD_THREAD_MAP: + min_size =3D sizeof(struct perf_record_thread_map); + break; + case PERF_RECORD_CPU_MAP: + min_size =3D sizeof(struct perf_record_cpu_map); + break; + case PERF_RECORD_STAT_CONFIG: + min_size =3D sizeof(struct perf_record_stat_config); + break; + case PERF_RECORD_STAT: + min_size =3D sizeof(struct perf_record_stat); + break; + case PERF_RECORD_STAT_ROUND: + min_size =3D sizeof(struct perf_record_stat_round); + break; + case PERF_RECORD_EVENT_UPDATE: + min_size =3D sizeof(struct perf_record_event_update); + break; + case PERF_RECORD_TIME_CONV: + min_size =3D sizeof(struct perf_record_time_conv); + break; + case PERF_RECORD_HEADER_FEATURE: + min_size =3D sizeof(struct perf_record_header_feature); + break; + case PERF_RECORD_COMPRESSED: + min_size =3D sizeof(struct perf_record_compressed); + break; + case PERF_RECORD_COMPRESSED2: + min_size =3D sizeof(struct perf_record_compressed2); + break; + case PERF_RECORD_BPF_METADATA: + min_size =3D sizeof(struct perf_record_bpf_metadata); + break; + case PERF_RECORD_SCHEDSTAT_CPU: + min_size =3D sizeof(struct perf_record_schedstat_cpu); + break; + case PERF_RECORD_SCHEDSTAT_DOMAIN: + min_size =3D sizeof(struct perf_record_schedstat_domain); + break; default: break; } @@ -1970,7 +2144,40 @@ static PyObject *pyrf_evsel__get_attr_wakeup_events(= PyObject *self, void */*clos return PyLong_FromUnsignedLong(pevsel->evsel->core.attr.wakeup_events); } =20 +static PyObject *pyrf_evsel__get_ids(struct pyrf_evsel *pevsel, void *clos= ure __maybe_unused) +{ + struct evsel *evsel =3D pevsel->evsel; + PyObject *list =3D PyList_New(0); + + if (!list) + return NULL; + + for (u32 i =3D 0; i < evsel->core.ids; i++) { + PyObject *id =3D PyLong_FromUnsignedLongLong(evsel->core.id[i]); + int ret; + + if (!id) { + Py_DECREF(list); + return NULL; + } + ret =3D PyList_Append(list, id); + Py_DECREF(id); + if (ret < 0) { + Py_DECREF(list); + return NULL; + } + } + + return list; +} + static PyGetSetDef pyrf_evsel__getset[] =3D { + { + .name =3D "ids", + .get =3D (getter)pyrf_evsel__get_ids, + .set =3D NULL, + .doc =3D "event IDs.", + }, { .name =3D "tracking", .get =3D pyrf_evsel__get_tracking, @@ -2743,6 +2950,8 @@ static const struct perf_constant perf__constants[] = =3D { PERF_CONST(RECORD_LOST_SAMPLES), PERF_CONST(RECORD_SWITCH), PERF_CONST(RECORD_SWITCH_CPU_WIDE), + PERF_CONST(RECORD_STAT), + PERF_CONST(RECORD_STAT_ROUND), =20 PERF_CONST(RECORD_MISC_SWITCH_OUT), { .name =3D NULL, }, @@ -3117,6 +3326,47 @@ static int pyrf_session_tool__sample(const struct pe= rf_tool *tool, return 0; } =20 +static int pyrf_session_tool__stat(const struct perf_tool *tool, + struct perf_session *session, + union perf_event *event) +{ + struct pyrf_session *psession =3D container_of(tool, struct pyrf_session,= tool); + struct evsel *evsel =3D evlist__id2evsel(session->evlist, event->stat.id); + PyObject *pyevent =3D pyrf_event__new(event, evsel, psession->session); + const char *name =3D evsel ? evsel__name(evsel) : "unknown"; + PyObject *ret; + + if (pyevent =3D=3D NULL) + return -ENOMEM; + + ret =3D PyObject_CallFunction(psession->stat, "Os", pyevent, name); + if (!ret) { + PyErr_Print(); + Py_DECREF(pyevent); + return -1; + } + Py_DECREF(ret); + Py_DECREF(pyevent); + return 0; +} + +static int pyrf_session_tool__stat_round(const struct perf_tool *tool, + struct perf_session *session __maybe_unused, + union perf_event *event) +{ + struct pyrf_session *psession =3D container_of(tool, struct pyrf_session,= tool); + PyObject *pyevent =3D pyrf_event__new(event, /*evsel=3D*/NULL, psession->= session); + PyObject *ret; + + if (pyevent =3D=3D NULL) + return -ENOMEM; + + ret =3D PyObject_CallFunction(psession->stat, "O", pyevent); + Py_XDECREF(ret); + Py_DECREF(pyevent); + return 0; +} + static PyObject *pyrf_session__find_thread(struct pyrf_session *psession, = PyObject *args) { struct machine *machine; @@ -3149,10 +3399,11 @@ static int pyrf_session__init(struct pyrf_session *= psession, PyObject *args, PyO { struct pyrf_data *pdata; PyObject *sample =3D NULL; - static char *kwlist[] =3D { "data", "sample", NULL }; + PyObject *stat =3D NULL; + static char *kwlist[] =3D { "data", "sample", "stat", NULL }; =20 - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &pyrf_data= __type, &pdata, - &sample)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO", kwlist, &pyrf_dat= a__type, &pdata, + &sample, &stat)) return -1; =20 Py_INCREF(pdata); @@ -3174,8 +3425,13 @@ static int pyrf_session__init(struct pyrf_session *p= session, PyObject *args, PyO } while (0) =20 ADD_TOOL(sample); + ADD_TOOL(stat); #undef ADD_TOOL =20 + if (stat) + psession->tool.stat_round =3D pyrf_session_tool__stat_round; + + psession->tool.comm =3D perf_event__process_comm; psession->tool.mmap =3D perf_event__process_mmap; psession->tool.mmap2 =3D perf_event__process_mmap2; @@ -3217,6 +3473,7 @@ static void pyrf_session__delete(struct pyrf_session = *psession) { Py_XDECREF(psession->pdata); Py_XDECREF(psession->sample); + Py_XDECREF(psession->stat); perf_session__delete(psession->session); Py_TYPE(psession)->tp_free((PyObject *)psession); } --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E8C8839D6D4 for ; Sat, 25 Apr 2026 22:50:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157460; cv=none; b=aojSuyln00LFK6E7aX4ZK7BAXc6Ihw2bPj+HD3yaWczRF2rZBUsokRpzds5nOr0l/v3imSAs7uwIJU9tiBdt+MN+qvOqj9w08h3eSEp03E4mNOvmzD3qxX4BIjy5fRXoEId1h/I1c++sobFZlNUK+RmLPhkxI1MFVkDu2XRGXzU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157460; c=relaxed/simple; bh=jDmMhZA3CJOJjsZss/AinSXlk6Jzm8+ex9BbvTUV1fw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=F2zcG/XnNAChqQyxx1fnujfvgvzyJc67oQcGgh6LkKAaMRb8xaCMZImjfnpmfSOoOVgXthpZIGNzC26+tayjBxMH6WEXBJhCgFBOAbNISEwBmMt0HjVA4CJI4YC0jRRl3hZFBq2WmzKe+7VQDbKkspUotutOT4VjdZRd9bJLqgc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=e7Xzah3U; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="e7Xzah3U" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-12db218e265so14788846c88.0 for ; Sat, 25 Apr 2026 15:50:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157457; x=1777762257; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=FZwlD8Dg9RxeiBkuuR5VQDv1GiXEHsJfT+1oBNmSxHU=; b=e7Xzah3Uc1HkRcvhj8SRrx/UeefMH0qQhgO6g0TK7TkzbtvBFPt5bnXzP6dafFGTiB N83IYtZxv/DHtJcsIYbqvYELFUZai+ht1ab11RzhyEcpYyLMZCKWoMNsiZUhLCyl2mkQ ZYD5IGqL+bcO+o/UVcspcs4wZhB92wrlEAc5vr+d+GUzil5n9vPJsc5VC8Ks1QKs/eCF Sn2hCWmNz+Im5kTLUorqpG/q8X7PpHbdoUW/8/+PtQCG9Q4SfHvHhx8mhZBzJztSFcjO PhClcqm/qD9KYoY5jK+Y0JYN3jCjPnNFaADBnOYjqYIGKKZVTAh06Py7YSyq0c8ZAJ9o GLwA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157457; x=1777762257; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=FZwlD8Dg9RxeiBkuuR5VQDv1GiXEHsJfT+1oBNmSxHU=; b=ocCl/uF3YVY6SzMGvnt4t+WVuRu35A5dcouBRswylcSU5QMtC4tO8oiqUq08Dn1Av2 JwqSbffDcoJ+v7LknvRBOrAIrPND5rLxkbNMqbkhaQlG7Rl6+IX4a2cDWbEMKQ3HuTmS 2c+X8nvoJqHR7vQ4yjsCtv95SyEl7R7dewBGcndYMg5ysFkf338m0+GokpjfIa8KCe9b S+ZYoKBpAl8+OXCtXNr3CFutEuPiiJVqWBLjmkPndwE5KgMQh79YAHO3uBjOD4mis4Bi K4+LGwRCb1mP6ibuW5AOQE4uJfSMKpSLNG37Zx5ZMcaGPppacghtmFBdAVXRedi/hbCB fDcg== X-Forwarded-Encrypted: i=1; AFNElJ/oN6Bwmb3QiG+gTCvyIEhdQH0eofwFWvFG7cfkWkmrlA+OPdZtzCxCBpBgCh2GWwWP55g665Lh8k0LyxE=@vger.kernel.org X-Gm-Message-State: AOJu0Yz4nTuXzxswsgH/yFBxKmkmaOHtv2Oupen9jTgx3BLlrR0WMxPL nk9GoYQou/bbHiFrsGXMb7zWf/Z/dDBfcupdTcE2Arq4le0uyeZOSKtEk30sw2/M0ZGblIwNLUs UTIPEfu1FNw== X-Received: from dlbts2-n2.prod.google.com ([2002:a05:7022:b042:20b0:128:cfd6:a3c6]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:30b:b0:12b:ed30:5b85 with SMTP id a92af1059eb24-12c73f67190mr19833604c88.2.1777157456770; Sat, 25 Apr 2026 15:50:56 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:13 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-22-irogers@google.com> Subject: [PATCH v7 21/59] perf python: Expose brstack in sample event From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Implement pyrf_branch_entry and pyrf_branch_stack for lazy iteration over branch stack entries. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Avoided Keyword Collision: Renamed the properties "from" and "to" to "from_ip" and "to_ip" in pyrf_branch_entry__getset[] to prevent syntax errors in Python. 2. Eager Branch Stack Resolution: Updated pyrf_session_tool__sample() to allocate and copy the branch stack entries eagerly when the event is processed, instead of deferring it to iteration time. This avoids reading from potentially overwritten or unmapped mmap buffers. 3. Updated Iterators: Updated pyrf_branch_stack and pyrf_branch_stack__next() to use the copied entries rather than pointing directly to the sample's buffer. 4. Avoided Reference Leak on Init Failure: Added proper error checking for PyModule_AddObject() in the module initialization function, decrementing references on failure. v6: - Moved branch stack resolution from `session_tool__sample` to `pyrf_event__new`. --- tools/perf/util/python.c | 188 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 17d0ee15336f..256129fef4f8 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -19,6 +19,7 @@ #include "dso.h" #include "dwarf-regs.h" #include "event.h" +#include "branch.h" #include "evlist.h" #include "evsel.h" #include "expr.h" @@ -69,6 +70,8 @@ struct pyrf_event { bool al_resolved; /** @callchain: Resolved callchain, eagerly computed if requested. */ PyObject *callchain; + /** @brstack: Resolved branch stack, eagerly computed if requested. */ + PyObject *brstack; /** @event: The underlying perf_event that may be in a file or ring buffe= r. */ union perf_event event; }; @@ -107,6 +110,7 @@ static void pyrf_event__delete(struct pyrf_event *peven= t) if (pevent->al_resolved) addr_location__exit(&pevent->al); Py_XDECREF(pevent->callchain); + Py_XDECREF(pevent->brstack); perf_sample__exit(&pevent->sample); Py_TYPE(pevent)->tp_free((PyObject *)pevent); } @@ -871,6 +875,144 @@ static PyObject *pyrf_sample_event__get_callchain(PyO= bject *self, void *closure return pevent->callchain; } =20 +struct pyrf_branch_entry { + PyObject_HEAD + u64 from; + u64 to; + struct branch_flags flags; +}; + +static void pyrf_branch_entry__delete(struct pyrf_branch_entry *pentry) +{ + Py_TYPE(pentry)->tp_free((PyObject *)pentry); +} + +static PyObject *pyrf_branch_entry__get_from(struct pyrf_branch_entry *pen= try, + void *closure __maybe_unused) +{ + return PyLong_FromUnsignedLongLong(pentry->from); +} + +static PyObject *pyrf_branch_entry__get_to(struct pyrf_branch_entry *pentr= y, + void *closure __maybe_unused) +{ + return PyLong_FromUnsignedLongLong(pentry->to); +} + +static PyObject *pyrf_branch_entry__get_mispred(struct pyrf_branch_entry *= pentry, + void *closure __maybe_unused) +{ + return PyBool_FromLong(pentry->flags.mispred); +} + +static PyObject *pyrf_branch_entry__get_predicted(struct pyrf_branch_entry= *pentry, + void *closure __maybe_unused) +{ + return PyBool_FromLong(pentry->flags.predicted); +} + +static PyObject *pyrf_branch_entry__get_in_tx(struct pyrf_branch_entry *pe= ntry, + void *closure __maybe_unused) +{ + return PyBool_FromLong(pentry->flags.in_tx); +} + +static PyObject *pyrf_branch_entry__get_abort(struct pyrf_branch_entry *pe= ntry, + void *closure __maybe_unused) +{ + return PyBool_FromLong(pentry->flags.abort); +} + +static PyObject *pyrf_branch_entry__get_cycles(struct pyrf_branch_entry *p= entry, + void *closure __maybe_unused) +{ + return PyLong_FromUnsignedLongLong(pentry->flags.cycles); +} + +static PyObject *pyrf_branch_entry__get_type(struct pyrf_branch_entry *pen= try, + void *closure __maybe_unused) +{ + return PyLong_FromLong(pentry->flags.type); +} + +static PyGetSetDef pyrf_branch_entry__getset[] =3D { + { .name =3D "from_ip", .get =3D (getter)pyrf_branch_entry__get_from,= }, + { .name =3D "to_ip", .get =3D (getter)pyrf_branch_entry__get_to, }, + { .name =3D "mispred", .get =3D (getter)pyrf_branch_entry__get_mispred,= }, + { .name =3D "predicted", .get =3D (getter)pyrf_branch_entry__get_predicte= d, }, + { .name =3D "in_tx", .get =3D (getter)pyrf_branch_entry__get_in_tx, }, + { .name =3D "abort", .get =3D (getter)pyrf_branch_entry__get_abort, }, + { .name =3D "cycles", .get =3D (getter)pyrf_branch_entry__get_cycles, = }, + { .name =3D "type", .get =3D (getter)pyrf_branch_entry__get_type, }, + { .name =3D NULL, }, +}; + +static PyTypeObject pyrf_branch_entry__type =3D { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name =3D "perf.branch_entry", + .tp_basicsize =3D sizeof(struct pyrf_branch_entry), + .tp_dealloc =3D (destructor)pyrf_branch_entry__delete, + .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc =3D "perf branch entry object.", + .tp_getset =3D pyrf_branch_entry__getset, +}; + +struct pyrf_branch_stack { + PyObject_HEAD + struct pyrf_event *pevent; + struct branch_entry *entries; + u64 nr; + u64 pos; +}; + +static void pyrf_branch_stack__delete(struct pyrf_branch_stack *pstack) +{ + Py_XDECREF(pstack->pevent); + free(pstack->entries); + Py_TYPE(pstack)->tp_free((PyObject *)pstack); +} + +static PyObject *pyrf_branch_stack__next(struct pyrf_branch_stack *pstack) +{ + struct pyrf_branch_entry *pentry; + + if (pstack->pos >=3D pstack->nr) + return NULL; + + pentry =3D PyObject_New(struct pyrf_branch_entry, &pyrf_branch_entry__typ= e); + if (!pentry) + return NULL; + + pentry->from =3D pstack->entries[pstack->pos].from; + pentry->to =3D pstack->entries[pstack->pos].to; + pentry->flags =3D pstack->entries[pstack->pos].flags; + + pstack->pos++; + return (PyObject *)pentry; +} + +static PyTypeObject pyrf_branch_stack__type =3D { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name =3D "perf.branch_stack", + .tp_basicsize =3D sizeof(struct pyrf_branch_stack), + .tp_dealloc =3D (destructor)pyrf_branch_stack__delete, + .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc =3D "perf branch stack object.", + .tp_iter =3D PyObject_SelfIter, + .tp_iternext =3D (iternextfunc)pyrf_branch_stack__next, +}; + +static PyObject *pyrf_sample_event__get_brstack(PyObject *self, void *clos= ure __maybe_unused) +{ + struct pyrf_event *pevent =3D (void *)self; + + if (!pevent->brstack) + Py_RETURN_NONE; + + Py_INCREF(pevent->brstack); + return pevent->brstack; +} + static PyObject* pyrf_sample_event__getattro(struct pyrf_event *pevent, PyObject *attr_name) { @@ -891,6 +1033,12 @@ static PyGetSetDef pyrf_sample_event__getset[] =3D { .set =3D NULL, .doc =3D "event callchain.", }, + { + .name =3D "brstack", + .get =3D pyrf_sample_event__get_brstack, + .set =3D NULL, + .doc =3D "event branch stack.", + }, { .name =3D "raw_buf", .get =3D (getter)pyrf_sample_event__get_raw_buf, @@ -1071,6 +1219,12 @@ static int pyrf_event__setup_types(void) err =3D PyType_Ready(&pyrf_callchain__type); if (err < 0) goto out; + err =3D PyType_Ready(&pyrf_branch_entry__type); + if (err < 0) + goto out; + err =3D PyType_Ready(&pyrf_branch_stack__type); + if (err < 0) + goto out; out: return err; } @@ -1251,6 +1405,7 @@ static PyObject *pyrf_event__new(const union perf_eve= nt *event, struct evsel *ev memcpy(&pevent->event, event, event->header.size); perf_sample__init(&pevent->sample, /*all=3D*/true); pevent->callchain =3D NULL; + pevent->brstack =3D NULL; pevent->al_resolved =3D false; addr_location__init(&pevent->al); =20 @@ -1307,6 +1462,27 @@ static PyObject *pyrf_event__new(const union perf_ev= ent *event, struct evsel *ev addr_location__exit(&al); } } + if (sample->branch_stack) { + struct branch_stack *bs =3D sample->branch_stack; + struct branch_entry *entries =3D perf_sample__branch_entries(sample); + struct pyrf_branch_stack *pstack; + + pstack =3D PyObject_New(struct pyrf_branch_stack, &pyrf_branch_stack__ty= pe); + if (pstack) { + Py_INCREF(pevent); + pstack->pevent =3D pevent; + pstack->pos =3D 0; + pstack->nr =3D bs->nr; + pstack->entries =3D calloc(bs->nr, sizeof(struct branch_entry)); + if (pstack->entries) { + memcpy(pstack->entries, entries, + bs->nr * sizeof(struct branch_entry)); + pevent->brstack =3D (PyObject *)pstack; + } else { + Py_DECREF(pstack); + } + } + } return (PyObject *)pevent; } =20 @@ -3708,6 +3884,18 @@ PyMODINIT_FUNC PyInit_perf(void) Py_INCREF(&pyrf_session__type); PyModule_AddObject(module, "session", (PyObject *)&pyrf_session__type); =20 + Py_INCREF(&pyrf_branch_entry__type); + if (PyModule_AddObject(module, "branch_entry", (PyObject *)&pyrf_branch_e= ntry__type) < 0) { + Py_DECREF(&pyrf_branch_entry__type); + goto error; + } + + Py_INCREF(&pyrf_branch_stack__type); + if (PyModule_AddObject(module, "branch_stack", (PyObject *)&pyrf_branch_s= tack__type) < 0) { + Py_DECREF(&pyrf_branch_stack__type); + goto error; + } + dict =3D PyModule_GetDict(module); if (dict =3D=3D NULL) goto error; --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f74.google.com (mail-dl1-f74.google.com [74.125.82.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B814239DBDD for ; Sat, 25 Apr 2026 22:50:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157463; cv=none; b=dFm+TS6GI0BhXuwzsvZIhfbfIe27/6DMDywBfZk5Ts/vkZwGlH/pcNN6jouzCRt3ljV3rhUTYk34RJv6V5RqUV2ZHAxR8iZOVhCd6glqsCgCwSEIXSPhMvSq3aPpnsnH3Ghiam9MqqHK4bf+lmfc63E6l+DtN/DjQFQjWYslgZU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157463; c=relaxed/simple; bh=2xwRFEaSEjKi0Cy8DGoaKUV5F4Zl9/TEdQNVkxTeaCU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=TvNP0LgPspVadxI8h4QfXo5iZW9Ho5nHfbMyLskdod17R2Q10/cUURFS2WyIhXiYJOMHYAlZjPvnJq8jfVa79iSyXsj1sB7smV8utsVMSYYdeRtJp/MhRV8EOLmoKdUv+XpT5Kkry1j5M1VoO3oTKzalqTAj1j27GE9On266ZiA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=OHZVGG+u; arc=none smtp.client-ip=74.125.82.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="OHZVGG+u" Received: by mail-dl1-f74.google.com with SMTP id a92af1059eb24-12c20d5d7f4so43628920c88.1 for ; Sat, 25 Apr 2026 15:50:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157459; x=1777762259; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=p5jktYnXLVV3ufAgYuB6nR2y55Mog/bG7LgNxL64ue4=; b=OHZVGG+uWToqpcsDn5ZiaMgex3iEdM5WXxLuxVpBFzCI6lWAQ+HN7mONXlifbn+TBD znA15lnNgqyEE4NuAEDu7Pe19QMNCTjUiE5KNjn4vURER2x2QnkgEwDKAhaORCf7ZGed a7uJ8TZh2mOXF+fVFVXNGkwOheIzBa6asWrgl5jqjCTpw5hneV5moVU3oOX965acY90Q hfkM0VKuuTlOTD/Uv1OocxxgRk/tspgU6M02pchTMqa7ieg8SSSWku8zcTdWQhXOwfpE M+Rkk0JcF/IHl+KKoeUzGG2mAU3W9JAbBsjLDTXg8r0HnOUmfxHILCINXI+wnz2ZcmTE qMJw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157459; x=1777762259; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=p5jktYnXLVV3ufAgYuB6nR2y55Mog/bG7LgNxL64ue4=; b=RhlfGRWTdgEcTj1yxnZkRYjAzB4gNEziDNqaBl6T8K7dKsnKLPZ5qcvEiQhLp9u7Fl 7qw+VMDrdFLct8OkDvUVEJQWKWwWlHx/qjOXSkqEHPPBMLlYK8oxcvL8taKri1TtU01D Q5d0pymB271RFbvPO6A2fipWToXekQumJBPh5DXyVCZs294cqzMdMTZCQRhHpQiU5Mjz Qo1fCFe4SCf9irzHrEGfOF6uyCiBIl473rgXmOzAvebDEIVNuP+p+w3mpQkFpck7ojWX bi+yTFx46FADmKNWACA8DfLxPj2+m52F3B/D2X7uuZgCkD+szEhoq4xaNhxL6VGLXtR/ 8udA== X-Forwarded-Encrypted: i=1; AFNElJ+IJmFz/lItJS5Vk89liVf5WXLG06D2k062S51wwhVoehTHCCYTyzthaIFIZvTstm5+SSaDWI0mX7BWay4=@vger.kernel.org X-Gm-Message-State: AOJu0Ywuz7zswQkOCWFjagXnuZuQf6Ix3mka7UsMrEhu4p+uxI9pIJxz C/ueMq6Tj+LJ+cLqHQsJSmfjg/qkBKdwqziO7zNPPpMtmF5OG0fTwPh3+FSDnv3Of2oC+dFyR/U k9TPCSNQNlQ== X-Received: from dlbsn12.prod.google.com ([2002:a05:7022:b90c:b0:12d:b26a:1571]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:701b:220e:b0:12d:b3f8:60e7 with SMTP id a92af1059eb24-12db3f8635amr8453061c88.34.1777157458747; Sat, 25 Apr 2026 15:50:58 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:14 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-23-irogers@google.com> Subject: [PATCH v7 22/59] perf python: Add perf.pyi stubs file From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add Python type stubs for the perf module to improve IDE support and static analysis. Includes docstrings for classes, methods, and constants derived from C source and JSON definitions. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Added Missing Module Functions: Added parse_metrics and pmus. Renamed metrics to parse_metrics to match python.c . 2. Added Constructors: Added __init__ methods for data , evsel , and evlist with their appropriate arguments. 3. Removed sample_comm : Removed it from sample_event since it is not exported in python.c . 4. Keyword Handling in branch_entry : I used from_ip and to_ip in the stubs to match the rename I did in python.c (in turn 145) to avoid the Python from keyword conflict. 5. Added Missing Event Classes: Added mmap_event , lost_event , comm_event , task_event , throttle_event , read_event , and switch_event . 6. Added Missing evlist Methods: Added get_pollfd and add . 7. Updated Return Types: Changed process_events to return int . v6: - Updated `perf.pyi` to use `find_thread` and `elf_machine`. --- tools/perf/python/perf.pyi | 581 +++++++++++++++++++++++++++++++++++++ 1 file changed, 581 insertions(+) create mode 100644 tools/perf/python/perf.pyi diff --git a/tools/perf/python/perf.pyi b/tools/perf/python/perf.pyi new file mode 100644 index 000000000000..91e19704e595 --- /dev/null +++ b/tools/perf/python/perf.pyi @@ -0,0 +1,581 @@ +"""Type stubs for the perf Python module.""" +from typing import Callable, Dict, List, Optional, Any, Iterator + +def config_get(name: str) -> Optional[str]: + """Get a configuration value from perf config. + + Args: + name: The configuration variable name (e.g., 'colors.top'). + + Returns: + The configuration value as a string, or None if not set. + """ + ... + +def metrics() -> List[Dict[str, str]]: + """Get a list of available metrics. + + Returns: + A list of dictionaries, each describing a metric. + """ + ... + +def syscall_name(sc_id: int, *, elf_machine: Optional[int] =3D None) -> st= r: + """Convert a syscall number to its name. + + Args: + sc_id: The syscall number. + elf_machine: Optional ELF machine type. + + Returns: + The name of the syscall. + """ + ... + +def syscall_id(name: str, *, elf_machine: Optional[int] =3D None) -> int: + """Convert a syscall name to its number. + + Args: + name: The syscall name. + elf_machine: Optional ELF machine type. + + Returns: + The number of the syscall. + """ + ... + +def parse_events( + event_string: str, + cpus: Optional[cpu_map] =3D None, + threads: Optional[Any] =3D None +) -> 'evlist': + """Parse an event string and return an evlist. + + Args: + event_string: The event string (e.g., 'cycles,instructions'). + cpus: Optional CPU map to bind events to. + threads: Optional thread map to bind events to. + + Returns: + An evlist containing the parsed events. + """ + ... + +def parse_metrics(metrics_string: str) -> 'evlist': + """Parse a string of metrics or metric groups and return an evlist.""" + ... + +def pmus() -> Iterator[Any]: + """Returns a sequence of pmus.""" + ... + +class data: + """Represents a perf data file.""" + def __init__(self, path: str =3D ..., fd: int =3D ...) -> None: ... + +class thread: + """Represents a thread in the system.""" + def comm(self) -> str: + """Get the command name of the thread.""" + ... + +class counts_values: + """Raw counter values.""" + val: int + ena: int + run: int + +class thread_map: + """Map of threads being monitored.""" + def __init__(self, pid: int =3D -1, tid: int =3D -1) -> None: + """Initialize a thread map. + + Args: + pid: Process ID to monitor (-1 for all). + tid: Thread ID to monitor (-1 for all). + """ + ... + def __len__(self) -> int: ... + def __getitem__(self, index: int) -> int: ... + def __iter__(self) -> Iterator[int]: ... + +class evsel: + """Event selector, represents a single event being monitored.""" + def __init__( + self, + type: int =3D ..., + config: int =3D ..., + sample_freq: int =3D ..., + sample_period: int =3D ..., + sample_type: int =3D ..., + read_format: int =3D ..., + disabled: bool =3D ..., + inherit: bool =3D ..., + pinned: bool =3D ..., + exclusive: bool =3D ..., + exclude_user: bool =3D ..., + exclude_kernel: bool =3D ..., + exclude_hv: bool =3D ..., + exclude_idle: bool =3D ..., + mmap: bool =3D ..., + context_switch: bool =3D ..., + comm: bool =3D ..., + freq: bool =3D ..., + idx: int =3D ..., + ) -> None: ... + def __str__(self) -> str: + """Return string representation of the event.""" + ... + def open(self) -> None: + """Open the event selector file descriptor table.""" + ... + def read(self, cpu: int, thread: int) -> counts_values: + """Read counter values for a specific CPU and thread.""" + ... + ids: List[int] + def cpus(self) -> cpu_map: + """Get CPU map for this event.""" + ... + def threads(self) -> thread_map: + """Get thread map for this event.""" + ... + + +class sample_event: + """Represents a sample event from perf.""" + evsel: evsel + sample_cpu: int + sample_time: int + sample_pid: int + type: int + brstack: Optional['branch_stack'] + callchain: Optional['callchain'] + def __getattr__(self, name: str) -> Any: ... + +class mmap_event: + """Represents a mmap event from perf.""" + type: int + pid: int + tid: int + addr: int + len: int + pgoff: int + filename: str + +class lost_event: + """Represents a lost events record.""" + type: int + id: int + lost: int + +class comm_event: + """Represents a COMM record.""" + type: int + pid: int + tid: int + comm: str + +class task_event: + """Represents an EXIT or FORK record.""" + type: int + pid: int + ppid: int + tid: int + ptid: int + time: int + +class throttle_event: + """Represents a THROTTLE or UNTHROTTLE record.""" + type: int + time: int + id: int + stream_id: int + +class read_event: + """Represents a READ record.""" + type: int + pid: int + tid: int + value: int + +class switch_event: + """Represents a SWITCH or SWITCH_CPU_WIDE record.""" + type: int + +class branch_entry: + """Represents a branch entry in the branch stack. + + Attributes: + from_ip: Source address of the branch (corresponds to 'from' keywo= rd in C). + to_ip: Destination address of the branch. + mispred: True if the branch was mispredicted. + predicted: True if the branch was predicted. + in_tx: True if the branch was in a transaction. + abort: True if the branch was an abort. + cycles: Number of cycles since the last branch. + type: Type of branch. + """ + from_ip: int + to_ip: int + mispred: bool + predicted: bool + in_tx: bool + abort: bool + cycles: int + type: int + +class branch_stack: + """Iterator over branch entries in the branch stack.""" + def __iter__(self) -> Iterator[branch_entry]: ... + def __next__(self) -> branch_entry: ... + +class callchain_node: + """Represents a frame in the callchain.""" + ip: int + sym: Optional[Any] + map: Optional[Any] + +class callchain: + """Iterator over callchain frames.""" + def __iter__(self) -> Iterator[callchain_node]: ... + def __next__(self) -> callchain_node: ... + +class stat_event: + """Represents a stat event from perf.""" + type: int + id: int + cpu: int + thread: int + val: int + ena: int + run: int + +class stat_round_event: + """Represents a stat round event from perf.""" + type: int + time: int + +class cpu_map: + """Map of CPUs being monitored.""" + def __init__(self, cpustr: Optional[str] =3D None) -> None: ... + def __len__(self) -> int: ... + def __getitem__(self, index: int) -> int: ... + def __iter__(self) -> Iterator[int]: ... + + +class evlist: + def __init__(self, cpus: cpu_map, threads: thread_map) -> None: ... + def open(self) -> None: + """Open the events in the list.""" + ... + def close(self) -> None: + """Close the events in the list.""" + ... + def mmap(self) -> None: + """Memory map the event buffers.""" + ... + def poll(self, timeout: int) -> int: + """Poll for events. + + Args: + timeout: Timeout in milliseconds. + + Returns: + Number of events ready. + """ + ... + def read_on_cpu(self, cpu: int) -> Optional[sample_event]: + """Read a sample event from a specific CPU. + + Args: + cpu: The CPU number. + + Returns: + A sample_event if available, or None. + """ + ... + def all_cpus(self) -> cpu_map: + """Get a cpu_map of all CPUs in the system.""" + ... + def metrics(self) -> List[str]: + """Get a list of metric names within the evlist.""" + ... + def compute_metric(self, metric: str, cpu: int, thread: int) -> float: + """Compute metric for given name, cpu and thread. + + Args: + metric: The metric name. + cpu: The CPU number. + thread: The thread ID. + + Returns: + The computed metric value. + """ + ... + def config(self) -> None: + """Configure the events in the list.""" + ... + def disable(self) -> None: + """Disable all events in the list.""" + ... + def enable(self) -> None: + """Enable all events in the list.""" + ... + def get_pollfd(self) -> List[int]: + """Get a list of file descriptors for polling.""" + ... + def add(self, evsel: evsel) -> int: + """Add an event to the list.""" + ... + def __iter__(self) -> Iterator[evsel]: + """Iterate over the events (evsel) in the list.""" + ... + + +class session: + def __init__( + self, + data: data, + sample: Optional[Callable[[sample_event], None]] =3D None, + stat: Optional[Callable[[Any, Optional[str]], None]] =3D None + ) -> None: + """Initialize a perf session. + + Args: + data: The perf data file to read. + sample: Callback for sample events. + stat: Callback for stat events. + """ + ... + def process_events(self) -> int: + """Process all events in the session.""" + ... + def find_thread(self, pid: int) -> thread: + """Returns the thread associated with a pid.""" + ... + +# Event Types +TYPE_HARDWARE: int +"""Hardware event.""" + +TYPE_SOFTWARE: int +"""Software event.""" + +TYPE_TRACEPOINT: int +"""Tracepoint event.""" + +TYPE_HW_CACHE: int +"""Hardware cache event.""" + +TYPE_RAW: int +"""Raw hardware event.""" + +TYPE_BREAKPOINT: int +"""Breakpoint event.""" + + +# Hardware Counters +COUNT_HW_CPU_CYCLES: int +"""Total cycles. Be wary of what happens during CPU frequency scaling.""" + +COUNT_HW_INSTRUCTIONS: int +"""Retired instructions. Be careful, these can be affected by various issu= es, +most notably hardware interrupt counts.""" + +COUNT_HW_CACHE_REFERENCES: int +"""Cache accesses. Usually this indicates Last Level Cache accesses but th= is +may vary depending on your CPU.""" + +COUNT_HW_CACHE_MISSES: int +"""Cache misses. Usually this indicates Last Level Cache misses.""" + +COUNT_HW_BRANCH_INSTRUCTIONS: int +"""Retired branch instructions.""" + +COUNT_HW_BRANCH_MISSES: int +"""Mispredicted branch instructions.""" + +COUNT_HW_BUS_CYCLES: int +"""Bus cycles, which can be different from total cycles.""" + +COUNT_HW_STALLED_CYCLES_FRONTEND: int +"""Stalled cycles during issue [This event is an alias of idle-cycles-fron= tend].""" + +COUNT_HW_STALLED_CYCLES_BACKEND: int +"""Stalled cycles during retirement [This event is an alias of idle-cycles= -backend].""" + +COUNT_HW_REF_CPU_CYCLES: int +"""Total cycles; not affected by CPU frequency scaling.""" + + +# Cache Counters +COUNT_HW_CACHE_L1D: int +"""Level 1 data cache.""" + +COUNT_HW_CACHE_L1I: int +"""Level 1 instruction cache.""" + +COUNT_HW_CACHE_LL: int +"""Last Level Cache.""" + +COUNT_HW_CACHE_DTLB: int +"""Data TLB.""" + +COUNT_HW_CACHE_ITLB: int +"""Instruction TLB.""" + +COUNT_HW_CACHE_BPU: int +"""Branch Processing Unit.""" + +COUNT_HW_CACHE_OP_READ: int +"""Read accesses.""" + +COUNT_HW_CACHE_OP_WRITE: int +"""Write accesses.""" + +COUNT_HW_CACHE_OP_PREFETCH: int +"""Prefetch accesses.""" + +COUNT_HW_CACHE_RESULT_ACCESS: int +"""Accesses.""" + +COUNT_HW_CACHE_RESULT_MISS: int +"""Misses.""" + + +# Software Counters +COUNT_SW_CPU_CLOCK: int +"""CPU clock event.""" + +COUNT_SW_TASK_CLOCK: int +"""Task clock event.""" + +COUNT_SW_PAGE_FAULTS: int +"""Page faults.""" + +COUNT_SW_CONTEXT_SWITCHES: int +"""Context switches.""" + +COUNT_SW_CPU_MIGRATIONS: int +"""CPU migrations.""" + +COUNT_SW_PAGE_FAULTS_MIN: int +"""Minor page faults.""" + +COUNT_SW_PAGE_FAULTS_MAJ: int +"""Major page faults.""" + +COUNT_SW_ALIGNMENT_FAULTS: int +"""Alignment faults.""" + +COUNT_SW_EMULATION_FAULTS: int +"""Emulation faults.""" + +COUNT_SW_DUMMY: int +"""Dummy event.""" + + +# Sample Fields +SAMPLE_IP: int +"""Instruction pointer.""" + +SAMPLE_TID: int +"""Process and thread ID.""" + +SAMPLE_TIME: int +"""Timestamp.""" + +SAMPLE_ADDR: int +"""Sampled address.""" + +SAMPLE_READ: int +"""Read barcode.""" + +SAMPLE_CALLCHAIN: int +"""Call chain.""" + +SAMPLE_ID: int +"""Unique ID.""" + +SAMPLE_CPU: int +"""CPU number.""" + +SAMPLE_PERIOD: int +"""Sample period.""" + +SAMPLE_STREAM_ID: int +"""Stream ID.""" + +SAMPLE_RAW: int +"""Raw sample.""" + + +# Format Fields +FORMAT_TOTAL_TIME_ENABLED: int +"""Total time enabled.""" + +FORMAT_TOTAL_TIME_RUNNING: int +"""Total time running.""" + +FORMAT_ID: int +"""Event ID.""" + +FORMAT_GROUP: int +"""Event group.""" + + +# Record Types +RECORD_MMAP: int +"""MMAP record. Contains header, pid, tid, addr, len, pgoff, filename, and= sample_id.""" + +RECORD_LOST: int +"""Lost events record. Contains header, id, lost count, and sample_id.""" + +RECORD_COMM: int +"""COMM record. Contains header, pid, tid, comm, and sample_id.""" + +RECORD_EXIT: int +"""EXIT record. Contains header, pid, ppid, tid, ptid, time, and sample_id= .""" + +RECORD_THROTTLE: int +"""THROTTLE record. Contains header, time, id, stream_id, and sample_id.""" + +RECORD_UNTHROTTLE: int +"""UNTHROTTLE record. Contains header, time, id, stream_id, and sample_id.= """ + +RECORD_FORK: int +"""FORK record. Contains header, pid, ppid, tid, ptid, time, and sample_id= .""" + +RECORD_READ: int +"""READ record. Contains header, and read values.""" + +RECORD_SAMPLE: int +"""SAMPLE record. Contains header, and sample data requested by sample_typ= e.""" + +RECORD_MMAP2: int +"""MMAP2 record. Contains header, pid, tid, addr, len, pgoff, maj, min, in= o, +ino_generation, prot, flags, filename, and sample_id.""" + +RECORD_AUX: int +"""AUX record. Contains header, aux_offset, aux_size, flags, and sample_id= .""" + +RECORD_ITRACE_START: int +"""ITRACE_START record. Contains header, pid, tid, and sample_id.""" + +RECORD_LOST_SAMPLES: int +"""LOST_SAMPLES record. Contains header, lost count, and sample_id.""" + +RECORD_SWITCH: int +"""SWITCH record. Contains header, and sample_id.""" + +RECORD_SWITCH_CPU_WIDE: int +"""SWITCH_CPU_WIDE record. Contains header, and sample_id.""" + +RECORD_STAT: int +"""STAT record.""" + +RECORD_STAT_ROUND: int +"""STAT_ROUND record.""" + +RECORD_MISC_SWITCH_OUT: int +"""MISC_SWITCH_OUT record.""" --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D381A39EF1A for ; Sat, 25 Apr 2026 22:51:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157464; cv=none; b=EDdhMNHfHkrgLRIN6t7sm+O0aM1jDRKKJ5WcAKIPbudIMJnecy/H5ntKFxx9Rn5vvW96KSWSmouOrDYqBnvYnBcpzabCggOsB6yZfp2/wm+l24XLQBN+XqeggV/cW9OvDRrVOL1ILAlScm6n55+hYtLqCHR03dYYs+hk0h7OdT8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157464; c=relaxed/simple; bh=zDAi6+BP11nSSIhghkCbe/DAtdl7FJlUT7QQCt3+JnI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ixF0vgdxpEGiDB5Mqk0tBsNy3nHnC57LR06N2NAEnP8FZRypgMVWguNnW54IS8h3r3dzjCVPMNjiHMApX6e/3G/hwPxi7nkfpl0BcNjgRQdoCoP5j5ELMs+LcCi/cGlIC+w+ThHdQh6V/lgauDgWg4OjILlpzLEST4N+wj8EFws= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=km+srlJ2; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="km+srlJ2" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2ba9a744f7dso11750345eec.0 for ; Sat, 25 Apr 2026 15:51:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157461; x=1777762261; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=2j+/G/gLHQ7NZrTXlwU5UyQ2EY4+32u+JcYzqEO/fuc=; b=km+srlJ2cu6I2Yj1/8/10M2ceZH/B2h24yvA64rSIniyumuaXITFiyTBy2O2Br34PY PoUNJOo7UJr3yMya4axU++YZ5wfDgKGIro8RJpjf3UKa+z9ZNQXv+Jat0I6ZbS49737R kRbnDv68MElkOS0xJcil6HpJ+esxMHP8+Q52jPejjCi2DEeJSLt6IFgl3Gvs1O/aeOS5 7B0BFMKSUxT0diw5OvJXfFRICyZWJYETggSzofFVY50gwXKcLSF7s0W1bW5qZjJZmjjw emfuLdtKCgqKm6xz6FN6eGDbHjn0eTVqdRAhdT+8z4iqcKtEGmYosnp51DTGOrQuRKr4 P7pQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157461; x=1777762261; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=2j+/G/gLHQ7NZrTXlwU5UyQ2EY4+32u+JcYzqEO/fuc=; b=R7URM/03MWyzvZFGHF7DsiTCFGC97KDlUZNyVOHLM36A84IIk+vDuOq1mkvQPa1P8B 0KSNAPioDsavuCj1FSvaG/tRsn+tzPqCdzIbA3A275slUNyHgpo+bVY8D8NreRZAPPgq Kz06XI/RG18pv8w7Oh1UW3MAv0ByGRrp9wM0BkHuc5CUnvyyz09xEKj0ieUrPzG55UJH Ml6HGukeHByPKoPnmteWyzcrvbDjgdV+kDscFl6Dro6k5NL9I2qX5/90TZWKjJduNSrq YJ/MvitXqVZVlcPNb1dNhB+6n3KSt5Y7vShiRYrttC66MDcg1plugEUTuhhgAF95qojs 7otw== X-Forwarded-Encrypted: i=1; AFNElJ/f49j5mWzy/XL3s+ghos6DMQvsNwL0GaW2mXiWuFBnLYrSqFiA4jSiUx6u4UrVNz5AkeOVHKLwiTiCnAw=@vger.kernel.org X-Gm-Message-State: AOJu0Ywb+NFtpoo/VnYi2EBYwCqpdhlFQVNdpHYsAQe4gQo5wx1o5Sr/ NaBwfLfWknKf5gl7d6nnKNCkdjlldX8iXYTUE46mkllZsYZmG/+da4um1wMiWIfZaaU7Cg9n2Jn 3SVXQnPrxIA== X-Received: from dybnj5.prod.google.com ([2002:a05:7300:d085:b0:2dd:4573:2897]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7301:1f01:b0:2c7:5a7b:e8c0 with SMTP id 5a478bee46e88-2e4657714d1mr19141205eec.12.1777157460756; Sat, 25 Apr 2026 15:51:00 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:15 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-24-irogers@google.com> Subject: [PATCH v7 23/59] perf python: Add LiveSession helper From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add LiveSession class in tools/perf/python/perf_live.py to support live event collection using perf.evlist and perf.parse_events, avoiding the need to fork a separate perf record process. Signed-off-by: Ian Rogers Assisted-by: Gemini:gemini-3.1-pro-preview --- v2: 1. Fixed File Descriptor Leak: I moved self.evlist.mmap() inside the try block so that if it raises an exception, the finally block will still be executed and call self.evlist.close() , preventing file descriptor leaks. 2. Handled InterruptedError in poll() : I wrapped the poll() call in a try-except block to catch InterruptedError and continue the loop. This prevents the live session from crashing on non-fatal signals like SIGWINCH . 3. Added evlist.config() : I added a call to self.evlist.config() in the constructor after parse_events() . This applies the default record options to the events, enabling sampling and setting up PERF_SAMPLE_* fields so that the kernel will actually generate PERF_RECORD_SAMPLE events. 4. Enable the evlist and be robust to exceptions from reading unsupported events like mmap2. --- tools/perf/python/perf_live.py | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100755 tools/perf/python/perf_live.py diff --git a/tools/perf/python/perf_live.py b/tools/perf/python/perf_live.py new file mode 100755 index 000000000000..d1dcbab1150b --- /dev/null +++ b/tools/perf/python/perf_live.py @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0 +""" +Live event session helper using perf.evlist. + +This module provides a LiveSession class that allows running a callback +for each event collected live from the system, similar to perf.session +but without requiring a perf.data file. +""" + +import perf + + +class LiveSession: + """Represents a live event collection session.""" + + def __init__(self, event_string: str, sample_callback): + self.event_string =3D event_string + self.sample_callback =3D sample_callback + # Create a cpu map for all online CPUs + self.cpus =3D perf.cpu_map() + # Parse events and set maps + self.evlist =3D perf.parse_events(self.event_string, self.cpus) + self.evlist.config() + + def run(self): + """Run the live session.""" + self.evlist.open() + try: + self.evlist.mmap() + self.evlist.enable() + + while True: + # Poll for events with 100ms timeout + try: + self.evlist.poll(100) + except InterruptedError: + continue + for cpu in self.cpus: + try: + event =3D self.evlist.read_on_cpu(cpu) + if event and event.type =3D=3D perf.RECORD_SAMPLE: + self.sample_callback(event) + except Exception: + pass + except KeyboardInterrupt: + pass + finally: + self.evlist.close() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f74.google.com (mail-dl1-f74.google.com [74.125.82.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 39B9C39FCB4 for ; Sat, 25 Apr 2026 22:51:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157466; cv=none; b=nUbq1qy2zj8Xq96QTnC/vqstJqFk/khgy356wYTmtZx0jtY14zpU/nmtufSHupp+RxVDRjAmOLa6Qr5Cf5AO/eDwbLLsJIXcyoUdks2EDBV67UsPqujBLlzIZXABVKFIvck3wDVxnX9D8AAh+cKcsi3IsCsxun5O19NTwv2wPXo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157466; c=relaxed/simple; bh=QiFIbgAs9hH4vFZpTy1iQiMx4BtLAVtLXQUTOjfmcRY=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=mUmp6zisKdwgg7H33lqRdUICL0KyL2N4KWEfisZpiyVc0maGxaIKfUV0pWsvGv3Cxg4yTVCPr7bIp+6vcP6ngAUKSN2MCUcRLg/gRZPtw6RsEyhpDJycYhg79+RchKan+lE8DZgnjqaHuD4jkeckV81a+8ZI64tkSkmVpqmVhqc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Ikri6p3h; arc=none smtp.client-ip=74.125.82.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Ikri6p3h" Received: by mail-dl1-f74.google.com with SMTP id a92af1059eb24-12c87ba0890so30473632c88.0 for ; Sat, 25 Apr 2026 15:51:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157463; x=1777762263; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=FvZ6XnZhDqeAbUzg1oigROcVSwy0qm3+WBD99Jn9WsU=; b=Ikri6p3hNkV+WKobE7wXjy3xFHdWttlLKCucVQqTYJhphseiueL3NGH/wAkcviOJ3C 0mE/KfJJfyEmUVFuReR1qQJ9wZSz9iDPpXrhP7bYdj8hiy3bo9JQJECnCCsefgpOTHOB 76wFTScmOoJ3UJaUp8wHyvCx8dggDMKWfKclVD9WPLC+ap7EPIcXmp9C/rdgtGPvcpmB vBPpVZrsfAB3rAg08c1Hbx8lH+iW1Bnu1zG+k9uqh75obzEJrQQcl44IoLOXWY89kx4y ijB2+kCoawnhQzt4yStAh3gJWA6d/AXRHNtuxB+/kMfuzQCJdPhhSWm1vULn/veNe6PT Y40w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157463; x=1777762263; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=FvZ6XnZhDqeAbUzg1oigROcVSwy0qm3+WBD99Jn9WsU=; b=hGVHSB8BG7e3dsVgQHzSB2asx4La2bAr64p1Tyea6HLkB7snJ01cB+Ez3Uwi36E9Q3 o/HziP2bBEB88QMtiF26w8D6hc54Lf6fQErvRzuZKyT4/SoO+FKQNTN21Gt3vIgQuMZB /K75MWNcAs+lRroThZNqiRnB3VhCPkAX/s3vBbSIvlzxmbyoSRy8aX55ftv15VzBoOU5 JF1elzlAGfGgWeuuxFt4OPiNVQ9c+5//4qEPymUp1G0sbHQyDrr12z5mPw2kdBGCf8ub YXmWhn0DmFy+192Xj4WpfTkeHbFy/dX6xt8mRroN2H4x9f+fesvJs7mr5KqdpmlZsxA6 BAww== X-Forwarded-Encrypted: i=1; AFNElJ/l2EHp1KSTv1yFI5jP9VISN2VNLaKmQMCO/h3W6GiabwU57MSjWn7iVk/kb7hiehns32N60ukKMo34r5w=@vger.kernel.org X-Gm-Message-State: AOJu0YzhivqDcDV5/3cIFyiajmEWc+mgHSUVGQl+pTGtVfZ/p39yVIbw uu2z7iEKOYnHU62Ib0JNmfpLB4sYwHmWE8Hi2ylzHH2a43O5niPVXDQZwCrNlffyIaVBkwr0z4l evbufuYjBKg== X-Received: from dyb19.prod.google.com ([2002:a05:693c:6313:b0:2d9:db60:7492]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:f40b:b0:12d:ce36:8930 with SMTP id a92af1059eb24-12dce369210mr1680182c88.3.1777157462870; Sat, 25 Apr 2026 15:51:02 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:16 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-25-irogers@google.com> Subject: [PATCH v7 24/59] perf python: Move exported-sql-viewer.py and parallel-perf.py to tools/perf/python/ From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" These scripts are standalone and not using the `perf script` libpython support. Move to tools/perf/python in an effort to deprecate the tools/perf/scripts/python support. Signed-off-by: Ian Rogers --- v2: 1. Updated exported-sql-viewer.py : I updated the comments at the top of the script to use the new path tools/perf/python/exported-sql-viewer.py in the usage examples. 2. Fixed Test Path in script.sh : I updated the path in tools/perf/tests/shell/script.sh to point to the new location of parallel-perf.py at ../../python/parallel-perf.py . v5: 1. Fix Test 105 Failure: Added a shebang line and marked the generated `db_test.py` script as executable in `script.sh`, preventing permission denied errors during standalone execution. --- tools/perf/{scripts =3D> }/python/exported-sql-viewer.py | 4 ++-- tools/perf/{scripts =3D> }/python/parallel-perf.py | 0 tools/perf/tests/shell/script.sh | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) rename tools/perf/{scripts =3D> }/python/exported-sql-viewer.py (99%) rename tools/perf/{scripts =3D> }/python/parallel-perf.py (100%) diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/= python/exported-sql-viewer.py similarity index 99% rename from tools/perf/scripts/python/exported-sql-viewer.py rename to tools/perf/python/exported-sql-viewer.py index e0b2e7268ef6..f3ac96ada1f5 100755 --- a/tools/perf/scripts/python/exported-sql-viewer.py +++ b/tools/perf/python/exported-sql-viewer.py @@ -10,12 +10,12 @@ # Following on from the example in the export scripts, a # call-graph can be displayed for the pt_example database like this: # -# python tools/perf/scripts/python/exported-sql-viewer.py pt_example +# python tools/perf/python/exported-sql-viewer.py pt_example # # Note that for PostgreSQL, this script supports connecting to remote data= bases # by setting hostname, port, username, password, and dbname e.g. # -# python tools/perf/scripts/python/exported-sql-viewer.py "hostname=3Dmyho= st username=3Dmyuser password=3Dmypassword dbname=3Dpt_example" +# python tools/perf/python/exported-sql-viewer.py "hostname=3Dmyhost usern= ame=3Dmyuser password=3Dmypassword dbname=3Dpt_example" # # The result is a GUI window with a tree representing a context-sensitive # call-graph. Expanding a couple of levels of the tree and adjusting colu= mn diff --git a/tools/perf/scripts/python/parallel-perf.py b/tools/perf/python= /parallel-perf.py similarity index 100% rename from tools/perf/scripts/python/parallel-perf.py rename to tools/perf/python/parallel-perf.py diff --git a/tools/perf/tests/shell/script.sh b/tools/perf/tests/shell/scri= pt.sh index 7007f1cdf761..f983b80e77b7 100755 --- a/tools/perf/tests/shell/script.sh +++ b/tools/perf/tests/shell/script.sh @@ -43,6 +43,7 @@ test_db() fi =20 cat << "_end_of_file_" > "${db_test}" +#!/usr/bin/env python3 perf_db_export_mode =3D True perf_db_export_calls =3D False perf_db_export_callchains =3D True @@ -53,6 +54,7 @@ def sample_table(*args): def call_path_table(*args): print(f'call_path_table({args}') _end_of_file_ + chmod +x "${db_test}" case $(uname -m) in s390x) cmd_flags=3D"--call-graph dwarf -e cpu-clock";; @@ -76,7 +78,7 @@ test_parallel_perf() err=3D2 return fi - pp=3D$(dirname "$0")/../../scripts/python/parallel-perf.py + pp=3D$(dirname "$0")/../../python/parallel-perf.py if [ ! -f "${pp}" ] ; then echo "SKIP: parallel-perf.py script not found " err=3D2 --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C3050390229 for ; Sat, 25 Apr 2026 22:51:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157468; cv=none; b=Iv58WcKk6X1Cp9k5OhtaJKYYKBOyG61lwLFa7Yl48hDdIKQAD95bhPqRsML35S6Rb/mA8zV4sU9sJuB4ZtITuJyduflAr9wcNEweRSjpK1z6X55rOpqhyUuF0HX1Ed/qTwrbvBz3abjcyM8sCOZJq1z5BXU6Uq+VJN/zOD2sUlA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157468; c=relaxed/simple; bh=eJ9kVepVnSBQCHOyn4G/66b2UzKOBaVMlZphDP73+zk=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=hh+TLSljUIvhYVmk77Lo70TU5Rew7rX9Tsx0IVo3CQfu9M3Lu4ozkukyc84Lvw4vruIlU+rPgIUsr47kCmvm7jvpSnrjPl+bmhzsc1BeAahPS2dsFXF0OVSoRZ7Rlzm9BujqbFMijqhSb/TSVOurVXMAYm4MAvEtxNQnHcOoWq0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=mUkOMUAC; arc=none smtp.client-ip=209.85.214.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="mUkOMUAC" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-2b79f4b35b7so29811425ad.0 for ; Sat, 25 Apr 2026 15:51:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157465; x=1777762265; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=VNEC1d+XQU6xcwJqo0QpL4AVBd93i989y6KjyJp07QI=; b=mUkOMUACwco5bvOeeJyQGnU7ilpBVtqjjlO8JB1EO1myXfwkV3tIyDxS/wYOvywsvJ kl5bPPtEeHOBHXWyjvDzFszJ/ETstDkl0zV3YDuH+WSN0rQi0JvsrcdGZLz6KXoEBr8d L2SjSvmXtxNMGcNwphM+7sQbWueS7uZAGLDsO4KyvYxxwqT6Mqr3q+YLS+cgrBAH1Qzp RHOdqDaShmDQXabQLZPa2z7/ZdaCd/ZqfB83RKZuKJklGPB1yagwGNx8ZTKx4I/MEApt EpW+ulxDWd1+wkw/v2CAzoLCbD205Vsr0ntXJY8GNJerYSkbt6QixJ336Gw/oUVuForp 6jKQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157465; x=1777762265; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=VNEC1d+XQU6xcwJqo0QpL4AVBd93i989y6KjyJp07QI=; b=M9L29Giq6wMII4YPSOsplnnXTq6+F60NDAoyPbjdWD0DNf+7CAOjmeYHXCMmT9qGyn VWjcqlCkjJVaI6V2quxuwQHqHsXvIPYaZKtNSz8k6LDGqDIc3AwR3HtjpZRtWqpTdnB2 TF03UJiFgD93Pjq31/G80qsdiXdJZvvWvWhz837ZNEoGgYzfPxcYag5gJXGjueZcMibD DsuK4MVBDfsmVtmCBXOeOTlgTGEPy8KzTiV7KpiUhebbn8cLCmX1THG+Cdxsc0p7XJr4 h39028JhCwrapLgP6+xEDrVT7XmcFHjwqCdPBpRT9h9KDez92r4eXfXH4d2dUKTcukdW 5ikQ== X-Forwarded-Encrypted: i=1; AFNElJ/zLSgs9URgoxOa3e781YzA7ukHtrUfTXWf6CyoCORaYLpCVGyICtOKACRqL/R4aRGtclO9iDMNe6y+rh0=@vger.kernel.org X-Gm-Message-State: AOJu0YxlA/eifJJR18W2A9Y8hwzLajtmSt+I291Hu81I24i0eoUjGeVd TSk6c0iT7DUNU+nLZNQLzHY+EU4r2DQUX1W3ESAwGdeZGB+nZu+9jk0F/5iYrBgcx1BYfxQsGpQ bQqsAD4pDOA== X-Received: from plfh1.prod.google.com ([2002:a17:902:f541:b0:2b7:f9d8:3d1b]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:1ac6:b0:2b2:5840:808e with SMTP id d9443c01a7336-2b5f9ee9487mr368769035ad.10.1777157465041; Sat, 25 Apr 2026 15:51:05 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:17 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-26-irogers@google.com> Subject: [PATCH v7 25/59] perf stat-cpi: Port stat-cpi to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Port stat-cpi.py from the legacy framework to a standalone script. Support both file processing mode (using perf.session) and live mode (reading counters directly via perf.parse_events and evsel.read). Use argparse for command line options handling. Calculate and display CPI (Cycles Per Instruction) per interval per CPU/thread. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Accurate CPI Calculation (Multiplexing Support): - Before: The get() method returned the raw counter value directly, ignoring whether the counter ran for the full interval. - After: The get() method now scales the raw value by the ratio of enabled time to running time ( val * (ena / float(run)) ) when run > 0 . This handles cases where PMU counters are overcommitted and multiplexed. 2. Per-Interval CPI in File Mode: - Before: store() saved absolute counter values as read from PERF_RECORD_STAT . Since these are cumulative from the start of the trace, and data.clear() was called every round, the script computed cumulative CPI rather than per-interval CPI. - After: store() now computes the delta between the current absolute value and the value from the previous interval. It saves this delta in self.data and retains the absolute value in self. prev_data for the next delta computation. 3. Prevention of Dummy Output (Cartesian Product Fix): - Before: self.cpus and self.threads lists accumulated all unique CPUs and threads seen independently. The nested loops in print_interval() then created a Cartesian product of all seen CPUs and threads, querying data for combinations that might never have occurred. - After: Replaced lists with a self.recorded_pairs set that stores (cpu, thread) tuples only when a sample actually records them. The output loop now iterates strictly over these verified pairs. --- tools/perf/python/stat-cpi.py | 151 ++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100755 tools/perf/python/stat-cpi.py diff --git a/tools/perf/python/stat-cpi.py b/tools/perf/python/stat-cpi.py new file mode 100755 index 000000000000..4b1f1f69c94a --- /dev/null +++ b/tools/perf/python/stat-cpi.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +"""Calculate CPI from perf stat data or live.""" + +import argparse +import sys +import time +from typing import Any, Optional +import perf + +class StatCpiAnalyzer: + """Accumulates cycles and instructions and calculates CPI.""" + + def __init__(self, args: argparse.Namespace) -> None: + self.args =3D args + self.data: dict[str, tuple[int, int, int]] =3D {} + self.prev_data: dict[str, tuple[int, int, int]] =3D {} + self.recorded_pairs: set[tuple[int, int]] =3D set() + + def get_key(self, event: str, cpu: int, thread: int) -> str: + """Get key for data dictionary.""" + return f"{event}-{cpu}-{thread}" + + def store_key(self, cpu: int, thread: int) -> None: + """Store CPU and thread IDs.""" + self.recorded_pairs.add((cpu, thread)) + + def store(self, event: str, cpu: int, thread: int, counts: tuple[int, = int, int]) -> None: + """Store counter values, computing difference from previous absolu= te values.""" + self.store_key(cpu, thread) + key =3D self.get_key(event, cpu, thread) + + val, ena, run =3D counts + if key in self.prev_data: + prev_val, prev_ena, prev_run =3D self.prev_data[key] + cur_val =3D val - prev_val + cur_ena =3D ena - prev_ena + cur_run =3D run - prev_run + else: + cur_val =3D val + cur_ena =3D ena + cur_run =3D run + + self.data[key] =3D (cur_val, cur_ena, cur_run) + self.prev_data[key] =3D counts # Store absolute value for next time + + def get(self, event: str, cpu: int, thread: int) -> float: + """Get scaled counter value.""" + key =3D self.get_key(event, cpu, thread) + if key not in self.data: + return 0.0 + val, ena, run =3D self.data[key] + if run > 0: + return val * (ena / float(run)) + return float(val) + + def process_stat_event(self, event: Any, name: Optional[str] =3D None)= -> None: + """Process PERF_RECORD_STAT and PERF_RECORD_STAT_ROUND events.""" + if event.type =3D=3D perf.RECORD_STAT: + if name: + if "cycles" in name: + event_name =3D "cycles" + elif "instructions" in name: + event_name =3D "instructions" + else: + return + self.store(event_name, event.cpu, event.thread, (event.val= , event.ena, event.run)) + elif event.type =3D=3D perf.RECORD_STAT_ROUND: + timestamp =3D getattr(event, "time", 0) + self.print_interval(timestamp) + self.data.clear() + self.recorded_pairs.clear() + + def print_interval(self, timestamp: int) -> None: + """Print CPI for the current interval.""" + for cpu, thread in sorted(self.recorded_pairs): + cyc =3D self.get("cycles", cpu, thread) + ins =3D self.get("instructions", cpu, thread) + cpi =3D 0.0 + if ins !=3D 0: + cpi =3D cyc / float(ins) + t_sec =3D timestamp / 1000000000.0 + print(f"{t_sec:15f}: cpu {cpu}, thread {thread} -> cpi {cpi:f}= ({cyc:.0f}/{ins:.0f})") + + def read_counters(self, evlist: Any) -> None: + """Read counters live.""" + for evsel in evlist: + name =3D str(evsel) + if "cycles" in name: + event_name =3D "cycles" + elif "instructions" in name: + event_name =3D "instructions" + else: + continue + + for cpu in evsel.cpus(): + for thread in evsel.threads(): + try: + counts =3D evsel.read(cpu, thread) + self.store(event_name, cpu, thread, + (counts.val, counts.ena, counts.run)) + except OSError: + pass + + def run_file(self) -> None: + """Process events from file.""" + session =3D perf.session(perf.data(self.args.input), stat=3Dself.p= rocess_stat_event) + session.process_events() + + def run_live(self) -> None: + """Read counters live.""" + evlist =3D perf.parse_events("cycles,instructions") + if not evlist: + print("Failed to parse events", file=3Dsys.stderr) + return + try: + evlist.open() + except OSError as e: + print(f"Failed to open events: {e}", file=3Dsys.stderr) + return + + print("Live mode started. Press Ctrl+C to stop.") + try: + while True: + time.sleep(self.args.interval) + timestamp =3D time.time_ns() + self.read_counters(evlist) + self.print_interval(timestamp) + self.data.clear() + self.recorded_pairs.clear() + except KeyboardInterrupt: + print("\nStopped.") + finally: + evlist.close() + +def main() -> None: + """Main function.""" + ap =3D argparse.ArgumentParser(description=3D"Calculate CPI from perf = stat data or live") + ap.add_argument("-i", "--input", help=3D"Input file name (enables file= mode)") + ap.add_argument("-I", "--interval", type=3Dfloat, default=3D1.0, + help=3D"Interval in seconds for live mode") + args =3D ap.parse_args() + + analyzer =3D StatCpiAnalyzer(args) + if args.input: + analyzer.run_file() + else: + analyzer.run_live() + +if __name__ =3D=3D "__main__": + main() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1122D39C631 for ; Sat, 25 Apr 2026 22:51:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157470; cv=none; b=RU2v8gM+CUTStUNMvGWHnSXJpL+QYYZt9Eogl4AR9g0bfgfgfh8ufrH1fnU+OJeI29CFWOkVowXw7g6YtjOYQAf3k21DDuGgrGhQ+xRj9rr+kjHcwWUj11d4QA/m2tOETtX29jC9cdbFz+8GQQPlYWfHVMSf98vJn9ZWdWJe+dY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157470; c=relaxed/simple; bh=vx6IVFKsB1ON0PlvRVEBkeEhDRXrkHr25y7aVh90UHg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=TIzsZFqhbsiJY+MxNsgcdwkCztzj2xZFOxJAQIocepROsntShm79ZgJ11cd6Yi1ftd4j8eUjAPIN5+Qe5Ud6oe5N2aYTkwauZWcMg898kn/i3j9iX+k/AgXkvQ2Mf1xd00hSOree/B3mA3C0CQx7HKGNbb6eBOS5SSlhtna2gsk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=btB07TBB; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="btB07TBB" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2c16233ee11so12492941eec.1 for ; Sat, 25 Apr 2026 15:51:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157467; x=1777762267; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=VLDLlf+sgEgN/cBLQvW28zSQKGyhqbeTTGXjAT4IZ18=; b=btB07TBB3iUQxNLZ0oYb9JeECq99I7mDoVqv6OWjP5XLOJOSBI96u2V+iJbB9i2cys ylvoyZL5zSzQytaLQXvm7LChp2PsxDDg/S0JxkIGOnyv/KaAOH4HcuDKKudXxl+D/Q3a gHuYJudUuUy23BX0FUokCbhJ9ER3ei1eL9ADL38tVmaczNtMcfmwyBfLplE6qeYhKoMu +tC541m9JH1n47vtXH+EIvUQiAYbYysThM/2tVA9Kj1UFxWeg1/kR1uAYUJKaEF0yppr lBUWSYzEMM41IRpLkjRzH2KXA9HL/wA8QBkXO9vjES+ylf3tuY79WMKa59LM+XZQSnH5 wkjw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157467; x=1777762267; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=VLDLlf+sgEgN/cBLQvW28zSQKGyhqbeTTGXjAT4IZ18=; b=Y/Jut90nZNFoRpPa4vDOImV5jeRETn9zFrwn79LAVwEYtjGCa3uSWcPFQnaR9yKHMd uDu3eBMMs2HWDJn/Fleg5l5KndRgZ5GHAiSWHY0IkbqGtFE+ccnbvf3h39zFk+shXSoV nnrsl/ZNhw/Rj3/tRLIzYQDVBpv1AEMSIXdQINf9JMZhneO9RePoDUvEujXLQLYtnmvU xWCuHwMxG308JWE2Hs+5ALL/3N05560p3Xydju0gTnj3kRO2i/ipaVxPnz+sLLMH/L+h IJVlZY7SI//FXeOdZsRaBYqdTtaWu0cr2kAIPpXNM/FYafuEsf4/xvhq5QkRTPvFNjwK is3g== X-Forwarded-Encrypted: i=1; AFNElJ9iVqKHHTogMi8gGNhtOj/FLx1FvJm9+vmN7+4neynZrVdjz6BJPWyQ4VdkBDC/vtpaxZgAX9L3rDowSME=@vger.kernel.org X-Gm-Message-State: AOJu0Yy+Zxn57IjCQT3qF4Z+VKuyzVeAPlhQ5bhqBYwLcZWYm8TGo08g HmTHD80UCz3GZuYnWzxYHCrK49B/kwLtfOdy7yG6VTwD3IeW+mIM/KI81skguSo5nMkrE5L8D4V /UWSa3ugUtQ== X-Received: from dlbqq11.prod.google.com ([2002:a05:7022:ed0b:b0:12a:7182:6cb]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:4395:b0:128:d24a:a5ba with SMTP id a92af1059eb24-12c73f90c45mr19012420c88.20.1777157466999; Sat, 25 Apr 2026 15:51:06 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:18 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-27-irogers@google.com> Subject: [PATCH v7 26/59] perf mem-phys-addr: Port mem-phys-addr to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Give an example of using the perf python session API to load a perf.data file and perform the behavior of tools/perf/scripts/python/mem-phys-addr.py. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: Added command line '-i' option and cleaned up pylint issues. --- tools/perf/python/mem-phys-addr.py | 117 +++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100755 tools/perf/python/mem-phys-addr.py diff --git a/tools/perf/python/mem-phys-addr.py b/tools/perf/python/mem-phy= s-addr.py new file mode 100755 index 000000000000..ba874d7a2011 --- /dev/null +++ b/tools/perf/python/mem-phys-addr.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +"""mem-phys-addr.py: Resolve physical address samples""" +import argparse +import bisect +import collections +from dataclasses import dataclass +import re +from typing import (Dict, Optional) + +import perf + +@dataclass(frozen=3DTrue) +class IomemEntry: + """Read from a line in /proc/iomem""" + begin: int + end: int + indent: int + label: str + +# Physical memory layout from /proc/iomem. Key is the indent and then +# a list of ranges. +iomem: Dict[int, list[IomemEntry]] =3D collections.defaultdict(list) +# Child nodes from the iomem parent. +children: Dict[IomemEntry, set[IomemEntry]] =3D collections.defaultdict(se= t) +# Maximum indent seen before an entry in the iomem file. +max_indent: int =3D 0 +# Count for each range of memory. +load_mem_type_cnt: Dict[IomemEntry, int] =3D collections.Counter() +# Perf event name set from the first sample in the data. +event_name: Optional[str] =3D None + +def parse_iomem(iomem_path: str): + """Populate iomem from iomem file""" + global max_indent + with open(iomem_path, 'r', encoding=3D'ascii') as f: + for line in f: + indent =3D 0 + while line[indent] =3D=3D ' ': + indent +=3D 1 + max_indent =3D max(max_indent, indent) + m =3D re.split('-|:', line, maxsplit=3D2) + begin =3D int(m[0], 16) + end =3D int(m[1], 16) + label =3D m[2].strip() + entry =3D IomemEntry(begin, end, indent, label) + # Before adding entry, search for a parent node using its begi= n. + if indent > 0: + parent =3D find_memory_type(begin) + assert parent, f"Given indent expected a parent for {label= }" + children[parent].add(entry) + iomem[indent].append(entry) + +def find_memory_type(phys_addr) -> Optional[IomemEntry]: + """Search iomem for the range containing phys_addr with the maximum in= dent""" + for i in range(max_indent, -1, -1): + if i not in iomem: + continue + position =3D bisect.bisect_right(iomem[i], phys_addr, + key=3Dlambda entry: entry.begin) + if position is None: + continue + iomem_entry =3D iomem[i][position-1] + if iomem_entry.begin <=3D phys_addr <=3D iomem_entry.end: + return iomem_entry + print(f"Didn't find {phys_addr}") + return None + +def print_memory_type(): + """Print the resolved memory types and their counts.""" + print(f"Event: {event_name}") + print(f"{'Memory type':<40} {'count':>10} {'percentage':>10}") + print(f"{'-' * 40:<40} {'-' * 10:>10} {'-' * 10:>10}") + total =3D sum(load_mem_type_cnt.values()) + # Add count from children into the parent. + for i in range(max_indent, -1, -1): + if i not in iomem: + continue + for entry in iomem[i]: + for child in children[entry]: + if load_mem_type_cnt[child] > 0: + load_mem_type_cnt[entry] +=3D load_mem_type_cnt[child] + + def print_entries(entries): + """Print counts from parents down to their children""" + for entry in sorted(entries, + key =3D lambda entry: load_mem_type_cnt[entry], + reverse =3D True): + count =3D load_mem_type_cnt[entry] + if count > 0: + mem_type =3D ' ' * entry.indent + f"{entry.begin:x}-{entry= .end:x} : {entry.label}" + percent =3D 100 * count / total + print(f"{mem_type:<40} {count:>10} {percent:>10.1f}") + print_entries(children[entry]) + + print_entries(iomem[0]) + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser(description=3D"Resolve physical address= samples") + ap.add_argument("-i", "--input", default=3D"perf.data", help=3D"Input = file name") + ap.add_argument("--iomem", default=3D"/proc/iomem", help=3D"Path to io= mem file") + args =3D ap.parse_args() + + def process_event(sample): + """Process a single sample event.""" + phys_addr =3D sample.sample_phys_addr + entry =3D find_memory_type(phys_addr) + if entry: + load_mem_type_cnt[entry] +=3D 1 + + global event_name + if event_name is None: + event_name =3D str(sample.evsel) + + parse_iomem(args.iomem) + perf.session(perf.data(args.input), sample=3Dprocess_event).process_ev= ents() + print_memory_type() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 197EE39F17C for ; Sat, 25 Apr 2026 22:51:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157472; cv=none; b=bHfqKuvs4ehoCxBWN89u26Mt4leIAJJePCrydf9foTpB+AzvWwdJy7OKXuP8tDJ7fKyeePpnnf5qWZ+OGOnsJ7vkakRzDEef0e+ZLaluozEDeXY1D58UdtFUBLWgz8r2nSA9yy1LDTxbwqoxgGdGIqzaRGDRmiNhoqwb1uM4PGQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157472; c=relaxed/simple; bh=FsZbTBhM56M9pIEzMOPTGuMLNalT4O3LRdQk+/cVt2E=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=AA7W/enXpUD/cCs62wjoGOFmKbgeIS0HGmi2kUske8HXcwrjSs7fXr3ZE5CiGZ688036QokYhKqISFiUUdiFkcaG1wLgAlOZ2h++RKn6QpOOMdEagbbMGwSBDGnB+qgToc9U+p35pazwsVtCITR7ZapBZCWQkn2QM5cxg2YxyTo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=MubysIR/; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="MubysIR/" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2bda35eab74so7738889eec.0 for ; Sat, 25 Apr 2026 15:51:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157469; x=1777762269; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=uUuqjywSMoBiuUZB5eMXpVGDru/SJ2JbsKc572+eD7Q=; b=MubysIR/kSxBn6QgX7eHXUXIyP84eSGa3ztp0el2XbREFRNc4f307ZWkNA9jsDnMon lYa82GjkRwoRUtXQgyxo5Yy2yQKawr3i62zAXHNLCxWb3o1vV4/89QewY78oIOAuLuyl bk5kNp/O3ANb4j7Zm97Au5W4v/w/C5SYBcrROKVeCpQ03Cs5CtBRzdLw/NEuLrTKlbjZ SJjq2rm7giR96uvIYhiH9HZM505QxVQ9nWBMohKCyki9r5e0sKkoivu6xGl8s0CGNrL/ EvbEvEmYQZbsgR/9cLqMnwQPGnmw7MgUbdnwgNKs7ZXZwrbFIKmDpxsHsDxznlal8L8o fMsA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157469; x=1777762269; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=uUuqjywSMoBiuUZB5eMXpVGDru/SJ2JbsKc572+eD7Q=; b=bEOdYJURHiF24Bwm5um2+3rffV244Is+uHMCVY3qWH39FMeRlbxYT+yyNk/bW31LhO ESL7jqyczKjsBavy2YtAtYQ9Ze+i1Q0HEE+NtdcizXWlpjbEGUUVd/33FM/Yk0JLVYmc qzMxo38RBYn4YAK3qhH5khsU4lS7W3qNY2pzUxxiM0r639xOhHAME66x2LI/hDHajYMU 1guEvvlP8MJef0CdLat8p50z1J2F35eq0hnoJxWiCrd5mIO/Km6H6uk+/99IrW8qawP0 8MH7FoTa+QNm7aZdtA7xwAQ2AcaglALq4bBXGR7KSh1OCgtRrWmKxDXKap7pIMDb264s AsZA== X-Forwarded-Encrypted: i=1; AFNElJ89QlPNRcMIntUcQKhEU3xjrbP6wamz9lRIDhuGJOqp3YeeKfPty67gX0X/l9AZv/Pjg+Cj6axMZpK/BNE=@vger.kernel.org X-Gm-Message-State: AOJu0YwreojzBItpAWcNZG2f3tTcamynBnu7fGUQ4iLhamHp6BPE6jym mwepvFk+MjQ8TeRmlYh/Aw58unFLZP9KW9pQNwS8mQpEX+ulamqw5TH2+nXP76Ogz00frU/1nNS y6vu8SdcdPQ== X-Received: from dybmx9.prod.google.com ([2002:a05:7300:d409:b0:2d7:d4ce:8994]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7301:3d19:b0:2d1:9b35:4edb with SMTP id 5a478bee46e88-2e41a1e3fb6mr14598532eec.0.1777157469044; Sat, 25 Apr 2026 15:51:09 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:19 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-28-irogers@google.com> Subject: [PATCH v7 27/59] perf syscall-counts: Port syscall-counts to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Rewrite tools/perf/scripts/python/syscall-counts.py to use the python module and various style changes. By avoiding the overheads in the `perf script` execution the performance improves by more than 4x as shown in the following (with PYTHON_PATH and PERF_EXEC_PATH set as necessary): ``` $ perf record -e raw_syscalls:sys_enter -a sleep 1 ... $ time perf script tools/perf/scripts/python/syscall-counts.py perf Install the python-audit package to get syscall names. For example: # apt-get install python3-audit (Ubuntu) # yum install python3-audit (Fedora) etc. Press control+C to stop and show the summary Warning: 1 out of order events recorded. syscall events for perf: event count -------------------------------------- ------------ 1 538989 16 32 203 17 3 2 257 1 204 1 15 1 7 1 0 1 real 0m3.887s user 0m3.578s sys 0m0.308s $ time python3 tools/perf/python/syscall-counts.py perf Warning: 1 out of order events recorded. syscall events for perf: event count -------------------------------------- ------------ write 538989 ioctl 32 sched_setaffinity 17 close 2 openat 1 sched_getaffinity 1 rt_sigreturn 1 poll 1 read 1 real 0m0.953s user 0m0.905s sys 0m0.048s ``` Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Fallback for Unknown Syscalls: If perf.syscall_name() returns None for an unmapped ID, the script now falls back to using the numeric ID string. This prevents a TypeError when applying string alignment formatting. 2. Fallback for Syscall Number Attribute: The script now checks for __syscall_nr first, and if not present (as on some older kernels), falls back to checking for nr . 3. Robust Process Resolution: Added a try-except block around session.process(sample.pid).comm() . If the process lookup fails (returning NULL/None), it falls back to "unknown" instead of letting a TypeError crash the script. 4. Support for Custom Input Files: Added a -i / --input command-line argument to allow processing arbitrarily named trace files, removing the hardcoded "perf.data" restriction. --- tools/perf/python/syscall-counts.py | 72 +++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100755 tools/perf/python/syscall-counts.py diff --git a/tools/perf/python/syscall-counts.py b/tools/perf/python/syscal= l-counts.py new file mode 100755 index 000000000000..ef2bd8c7b24c --- /dev/null +++ b/tools/perf/python/syscall-counts.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +Displays system-wide system call totals, broken down by syscall. + +If a [comm] arg is specified, only syscalls called by [comm] are displayed. +""" + +import argparse +from collections import defaultdict +from typing import DefaultDict +import perf + +syscalls: DefaultDict[int, int] =3D defaultdict(int) +for_comm =3D None +session =3D None + + +def print_syscall_totals(): + """Print aggregated statistics.""" + if for_comm is not None: + print(f"\nsyscall events for {for_comm}:\n") + else: + print("\nsyscall events:\n") + + print(f"{'event':<40} {'count':>10}") + print("---------------------------------------- -----------") + + for sc_id, val in sorted(syscalls.items(), + key=3Dlambda kv: (kv[1], kv[0]), reverse=3DTr= ue): + name =3D perf.syscall_name(sc_id) or str(sc_id) + print(f"{name:<40} {val:>10}") + + +def process_event(sample): + """Process a single sample event.""" + event_name =3D str(sample.evsel) + if event_name =3D=3D "evsel(raw_syscalls:sys_enter)": + sc_id =3D getattr(sample, "id", -1) + elif event_name.startswith("evsel(syscalls:sys_enter_"): + sc_id =3D getattr(sample, "__syscall_nr", None) + if sc_id is None: + sc_id =3D getattr(sample, "nr", -1) + else: + return + + if sc_id =3D=3D -1: + return + + comm =3D "unknown" + try: + if session: + proc =3D session.find_thread(sample.sample_pid) + if proc: + comm =3D proc.comm() + except (TypeError, AttributeError): + pass + + if for_comm and comm !=3D for_comm: + return + syscalls[sc_id] +=3D 1 + + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser() + ap.add_argument("comm", nargs=3D"?", help=3D"Only report syscalls for = comm") + ap.add_argument("-i", "--input", default=3D"perf.data", help=3D"Input = file name") + args =3D ap.parse_args() + for_comm =3D args.comm + session =3D perf.session(perf.data(args.input), sample=3Dprocess_event) + session.process_events() + print_syscall_totals() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4931A3A3E7E for ; Sat, 25 Apr 2026 22:51:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157474; cv=none; b=u+vVwgWAQTFUUkJTnzwfohttuJacwjn6EFjHc4Z6BSeA7jFPNIRY9RWRL8GwaGhnYcLB+CKmREKfCUQxpdy14kST9XNdcAOgUVomghhfIUai2mnHN1RjqFayjKyXb9xrtXF+eFCuGXIDp1+lZoI31IA7pjnqGWIGMlmOh1gfJLM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157474; c=relaxed/simple; bh=tx0Y+ZL/2KY67zQgnrtOWDh6Wuqzo7a1zM65iqPR+1c=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=myTixri+k9tCHBjOyNw4RNFTKOwqIt4C7PV/N1mVoMlKPx15ehx3oOPO7jSsa/ms76nZpCxNpL4Ac2TKbZqR2wXN1F1fzhl4In+dqCo/6ApZiGHuDhrq2pkTRRO1dpvF4WOsQZRZX9PIBvQYmiOxT7ui/ZKMZCzvAJ4EkdFFirY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=ET6ZpZ04; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="ET6ZpZ04" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2ba8013a9e3so10459536eec.0 for ; Sat, 25 Apr 2026 15:51:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157471; x=1777762271; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=FIUPQnj9Sm31hxCjwi2GzFaWFVwRkKGchfSFKojE+/g=; b=ET6ZpZ04ISwQpemA8jXF8ad5D8atVAphHh4aHE7nvBcpH6FUFnjh2wz+QiYIWqoPsg HKWAeWoIY75jMwWdK7l/bQe/K5uKethZeWoyAbVyNFp0wHmIHVCi6lCO9/XNjIONQnBT xGo0kePPVFuTuPxgX1WI3KsIP7akcOaZ/gp6SaCNvNgYULcS7/Ot6Sy8f6KmegWe4wFE 9RAU3s7dfCOLHDfLOiP16HDCCaD+GEkxOez8LmSoOkrW0hQnmXhZWfHObomLEk/QOTWb +Ye/iVyaPN48g2xj20fFn5JDfJ7gqc6wqP/guOUqK8HCdXnm53azpHeh3I5xTjM7ScNn BIOA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157471; x=1777762271; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=FIUPQnj9Sm31hxCjwi2GzFaWFVwRkKGchfSFKojE+/g=; b=XxLv9U7C+RHTOFG0sQDR9LfywIExlFdUeQZWMUQWF4oTzHVYYjA+MVsDExLYMyMl+Y YLcabT0/15Qrqgf21JFZqY2XPhBAJs86KVl4GeEc4EsiniNa7XHOTZyO3KvpeJH010Xi FWPnenqbIctOiR+Pv3ZISJHTMysYacnNbsilKCb+PgHAlokZ4wMs/fQgjz8JogZrR60c dnd+VhE9FrTqN9ImqLjORxPYyBE1QOldwsl0NBW/VTAMTyDSWCJuIq2bqQJW6/jkjP9m w88rmJId1oJ/ojhKC3kat1jsNtJQwrJ30SMnxvZa7sNHUGw4vmuMggD/2jUOr7RtLRo4 Ps1w== X-Forwarded-Encrypted: i=1; AFNElJ+sGpFyFnCebuN8YSJr2Osac24lLMYJxQKvZ1u/lNMIIs9JeDN+U95qo4Voi0JwvDmT1P27KG1io3sG0iA=@vger.kernel.org X-Gm-Message-State: AOJu0YzqcCAeIs0s0MeIl5QcsUHITFhhlDlwR1u23QjdIEIJ5rH0fc/6 lmc/ljvghRMuojbUvSfVJiJDouyurpJOnU9ovWMt5ozgTvOyYdvXfIyXA9jmv9Yw/jB56Nl8esL bWnvU2enkjQ== X-Received: from dlbto5.prod.google.com ([2002:a05:7022:3b05:b0:12c:1f83:7b4c]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:eac8:b0:128:d375:f1cc with SMTP id a92af1059eb24-12c73f759e5mr20700173c88.12.1777157471136; Sat, 25 Apr 2026 15:51:11 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:20 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-29-irogers@google.com> Subject: [PATCH v7 28/59] perf syscall-counts-by-pid: Port syscall-counts-by-pid to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Rewrite tools/perf/scripts/python/syscall-counts-by-pid.py to use the python module and various style changes. By avoiding the overheads in the `perf script` execution the performance improves by more than 3.8x as shown in the following (with PYTHON_PATH and PERF_EXEC_PATH set as necessary): ``` $ perf record -e raw_syscalls:sys_enter -a sleep 1 ... $ time perf script tools/perf/scripts/python/syscall-counts-by-pid.py perf Install the python-audit package to get syscall names. For example: # apt-get install python3-audit (Ubuntu) # yum install python3-audit (Fedora) etc. Press control+C to stop and show the summary Warning: 1 out of order events recorded. syscall events for perf: comm [pid]/syscalls count --------------------------------------- ---------- perf [3886080] 1 538989 16 32 203 17 3 2 257 1 204 1 15 1 0 1 perf [3886082] 7 1 real 0m3.852s user 0m3.512s sys 0m0.336s $ time python3 tools/perf/python/syscall-counts-by-pid.py perf Warning: 1 out of order events recorded. syscall events for perf: comm [pid]/syscalls count --------------------------------------- ----------- perf [3886080] write 538989 ioctl 32 sched_setaffinity 17 close 2 openat 1 sched_getaffinity 1 rt_sigreturn 1 read 1 perf [3886082] poll 1 real 0m1.011s user 0m0.963s sys 0m0.048s ``` Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Removed Unused Variable: Removed id_keys which was assigned but never read. 2. Fallback for Unknown Syscalls: If perf.syscall_name() returns None for an unmapped ID, it now falls back to the numeric ID string to prevent TypeError crashes during string formatting. 3. Fallback for Syscall Number Attribute: It now checks for __syscall_nr first, and if missing, falls back to checking for nr . 4. Robust Process Resolution: Added a try-except block around session.process(sample.pid).comm() to handle untracked PIDs gracefully instead of crashing on a TypeError . 5. Restored PID Filtering: The script now attempts to parse the positional argument as an integer to filter by Process ID. If that fails, it treats it as a command name (COMM) string to filter by, restoring behavior from the original legacy script. 6. Support for Custom Input Files: Added a -i / --input command-line argument to support arbitrarily named trace files, removing the hardcoded "perf.data" restriction. --- tools/perf/python/syscall-counts-by-pid.py | 88 ++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100755 tools/perf/python/syscall-counts-by-pid.py diff --git a/tools/perf/python/syscall-counts-by-pid.py b/tools/perf/python= /syscall-counts-by-pid.py new file mode 100755 index 000000000000..ff962334a143 --- /dev/null +++ b/tools/perf/python/syscall-counts-by-pid.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +Displays system-wide system call totals, broken down by syscall. +If a [comm] arg is specified, only syscalls called by [comm] are displayed. +""" + +import argparse +from collections import defaultdict +import perf + +syscalls: dict[tuple[str, int, int], int] =3D defaultdict(int) +for_comm =3D None +for_pid =3D None +session =3D None + + +def print_syscall_totals(): + """Print aggregated statistics.""" + if for_comm is not None: + print(f"\nsyscall events for {for_comm}:\n") + elif for_pid is not None: + print(f"\nsyscall events for PID {for_pid}:\n") + else: + print("\nsyscall events:\n") + + print(f"{'comm [pid]/syscalls':<40} {'count':>10}") + print("---------------------------------------- -----------") + + sorted_keys =3D sorted(syscalls.keys(), key=3Dlambda k: (k[0], k[1], k= [2])) + current_comm_pid =3D None + for comm, pid, sc_id in sorted_keys: + if current_comm_pid !=3D (comm, pid): + print(f"\n{comm} [{pid}]") + current_comm_pid =3D (comm, pid) + name =3D perf.syscall_name(sc_id) or str(sc_id) + print(f" {name:<38} {syscalls[(comm, pid, sc_id)]:>10}") + + +def process_event(sample): + """Process a single sample event.""" + event_name =3D str(sample.evsel) + if event_name =3D=3D "evsel(raw_syscalls:sys_enter)": + sc_id =3D getattr(sample, "id", -1) + elif event_name.startswith("evsel(syscalls:sys_enter_"): + sc_id =3D getattr(sample, "__syscall_nr", None) + if sc_id is None: + sc_id =3D getattr(sample, "nr", -1) + else: + return + + if sc_id =3D=3D -1: + return + + pid =3D sample.sample_pid + + if for_pid and pid !=3D for_pid: + return + + comm =3D "unknown" + try: + if session: + proc =3D session.find_thread(pid) + if proc: + comm =3D proc.comm() + except (TypeError, AttributeError): + pass + + if for_comm and comm !=3D for_comm: + return + syscalls[(comm, pid, sc_id)] +=3D 1 + + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser() + ap.add_argument("filter", nargs=3D"?", help=3D"COMM or PID to filter b= y") + ap.add_argument("-i", "--input", default=3D"perf.data", help=3D"Input = file name") + args =3D ap.parse_args() + + if args.filter: + try: + for_pid =3D int(args.filter) + except ValueError: + for_comm =3D args.filter + + session =3D perf.session(perf.data(args.input), sample=3Dprocess_event) + session.process_events() + print_syscall_totals() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f74.google.com (mail-dl1-f74.google.com [74.125.82.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2ADEE3A3E75 for ; Sat, 25 Apr 2026 22:51:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157475; cv=none; b=XFNnD4vZdX8fzOExQeTt3QdEPiAECaMzEcPcItdI8zCl0kQVfCm3u4dMNxyjk6bPLrG3n5ZmPRxOzgawbHhTpg987zmQezbG/o//gYJUEXV+EeZ8Y5W4F2CfK8WcqTlXb/Zz2tT65NFRG8I8K/3T5cCuQleTllJEDCWelo9ZNbo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157475; c=relaxed/simple; bh=FRJkB1Cquz79cOSRZrWQZxDQ+1Gx96tDBQLRgM6VsPA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=SvDetVOJ5/U1tcLCZnR5RUjpU7jY7ChA+WelJoahGQlac7Jr2gMu52P492sjLvDTPMDcTbKh6QmOcLFAc/Ynl8fQaQRQRY+62TfwbxocfryQpVYBT5eT1L4Se1sAok/YqJgRhUHxE8preEm/g03wZQmNK3Pzgv70uNqBTBdcJXY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=OGoE4g7J; arc=none smtp.client-ip=74.125.82.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="OGoE4g7J" Received: by mail-dl1-f74.google.com with SMTP id a92af1059eb24-12db218e265so14789354c88.0 for ; Sat, 25 Apr 2026 15:51:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157473; x=1777762273; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=GkS04/94NBW5ShfL0EcvB/IZEgEtB93nGkm6zSZzY+Q=; b=OGoE4g7JGm7Qi/1ASQrLmoQ8+iVOn3apmxiCvR3gptEZuqcJ3x9ofWc3PcwnOPqCwe plCB/Cw5FUkVFw14O1R6c9uVyy4oTW3JFEQtgMw6Xx9JsyO7nq2WbCNDtmsJFXAEPUGP CJpE43YmYVtme08IE6ygA4nQ9E9wRnQkCq/8bKh7tqf7wcJttpgrrRhv0YI8c3wv3xbW 8aIev3Dn7/TOasnWoj/hIRUcPJl/FdOLTGqmLK28rryhreyc++FOUU1iREEOnly32Dvv Ohhvxkrl0tCPxnzoBWIZinspjbxHGAsOF3UKgUxcNo+OAFoGD9qWQaNtA5MDUT83HVTZ A1SA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157473; x=1777762273; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=GkS04/94NBW5ShfL0EcvB/IZEgEtB93nGkm6zSZzY+Q=; b=PfcCUNPeoMa3/dmKrg7blhw04KDKj1JEI5BURAam1++M9ows7cWksEQsJMwmgl4fRP drPLoK/2XvnvncCIqVtXOMUfXgVR82vKE2oOxZujpM//3ObOMnmi/U35E1N4feUSGdzn akFaRWekLfPB4QGkRyN7cFzpYN0KR6F1OXoW2LirewVWqb+MeJmJlJ0j4fpzKouqKK5z Qa+plptY32jQ0ILmMzdFvpoIQH507kaY9CuL6cuU9PKc8RYgSrLBLWYJzvDF6cAHn1Cl wauYq2Pof8IP6jynVxkdWnjRM/d/Xrq1xZDlgchkWGWmzpZ0CAf+sUDwSwnxtiOqpDLm 0T8Q== X-Forwarded-Encrypted: i=1; AFNElJ8w+f7MW07PLPWbg7qqOd2BQmweShznQb2Eh1WPMpvCH2mnNx/ew+SUjgSvM1a9l6XYvu2l6rZmVILLXzo=@vger.kernel.org X-Gm-Message-State: AOJu0YxHbYt0wI8b9+a8ZA5pcgW39TJQpNfestJogrivbBlP34C71qXp qyDNog4GtaWvCmsDBurknsaXjSL8evEpMzDeL/sEb+Nucz9+0EOxUpJJfgG4/0+2s1YIO932z3g l6vFkXrAn7A== X-Received: from dlbtz15.prod.google.com ([2002:a05:7022:eacf:b0:12c:4b32:3177]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:f40b:b0:12d:ce36:892a with SMTP id a92af1059eb24-12dce3691famr1839408c88.9.1777157473158; Sat, 25 Apr 2026 15:51:13 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:21 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-30-irogers@google.com> Subject: [PATCH v7 29/59] perf futex-contention: Port futex-contention to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Rewrite tools/perf/scripts/python/futex-contention.py to use the python module and various style changes. By avoiding the overheads in the `perf script` execution the performance improves by more than 3.2x as shown in the following (with PYTHON_PATH and PERF_EXEC_PATH set as necessary): ``` $ perf record -e syscalls:sys_*_futex -a sleep 1 ... $ time perf script tools/perf/scripts/python/futex-contention.py Install the python-audit package to get syscall names. For example: # apt-get install python3-audit (Ubuntu) # yum install python3-audit (Fedora) etc. Press control+C to stop and show the summary aaa/4[2435653] lock 7f76b380c878 contended 1 times, 1099 avg ns [max: 1099 = ns, min 1099 ns] ... real 0m1.007s user 0m0.935s sys 0m0.072s $ time python3 tools/perf/python/futex-contention.py ... real 0m0.314s user 0m0.259s sys 0m0.056s ``` Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Fixed Module Import Failure: Corrected the type annotations from [int, int] to Tuple[int, int] . The previous code would raise a TypeError at module import time because lists cannot be used as types in dictionary annotations. 2. Prevented Out-Of-Memory Crashes: Replaced the approach of storing every single duration in a list with a LockStats class that maintains running aggregates (count, total time, min, max). This ensures O(1) memory usage per lock/thread pair rather than unbounded memory growth. 3. Support for Custom Input Files: Added a -i / --input command-line argument to support processing arbitrarily named trace files, removing the hardcoded "perf.data" restriction. 4. Robust Process Lookup: Added a check to ensure session is initialized before calling session. process() , preventing potential NoneType attribute errors if events are processed during initialization. --- tools/perf/python/futex-contention.py | 87 +++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100755 tools/perf/python/futex-contention.py diff --git a/tools/perf/python/futex-contention.py b/tools/perf/python/fute= x-contention.py new file mode 100755 index 000000000000..1fc87ec0e6e5 --- /dev/null +++ b/tools/perf/python/futex-contention.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +"""Measures futex contention.""" + +import argparse +from collections import defaultdict +from typing import Dict, Tuple +import perf + +class LockStats: + """Aggregate lock contention information.""" + def __init__(self) -> None: + self.count =3D 0 + self.total_time =3D 0 + self.min_time =3D 0 + self.max_time =3D 0 + + def add(self, duration: int) -> None: + """Add a new duration measurement.""" + self.count +=3D 1 + self.total_time +=3D duration + if self.count =3D=3D 1: + self.min_time =3D duration + self.max_time =3D duration + else: + self.min_time =3D min(self.min_time, duration) + self.max_time =3D max(self.max_time, duration) + + def avg(self) -> float: + """Return average duration.""" + return self.total_time / self.count if self.count > 0 else 0.0 + +process_names: Dict[int, str] =3D {} +start_times: Dict[int, Tuple[int, int]] =3D {} +session =3D None +durations: Dict[Tuple[int, int], LockStats] =3D defaultdict(LockStats) + +FUTEX_WAIT =3D 0 +FUTEX_WAKE =3D 1 +FUTEX_PRIVATE_FLAG =3D 128 +FUTEX_CLOCK_REALTIME =3D 256 +FUTEX_CMD_MASK =3D ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME) + + +def process_event(sample: perf.sample_event) -> None: + """Process a single sample event.""" + def handle_start(tid: int, uaddr: int, op: int, start_time: int) -> No= ne: + if (op & FUTEX_CMD_MASK) !=3D FUTEX_WAIT: + return + if tid not in process_names: + try: + if session: + process =3D session.find_thread(tid) + if process: + process_names[tid] =3D process.comm() + except (TypeError, AttributeError): + return + start_times[tid] =3D (uaddr, start_time) + + def handle_end(tid: int, end_time: int) -> None: + if tid not in start_times: + return + (uaddr, start_time) =3D start_times[tid] + del start_times[tid] + durations[(tid, uaddr)].add(end_time - start_time) + + event_name =3D str(sample.evsel) + if event_name =3D=3D "evsel(syscalls:sys_enter_futex)": + uaddr =3D getattr(sample, "uaddr", 0) + op =3D getattr(sample, "op", 0) + handle_start(sample.sample_tid, uaddr, op, sample.sample_time) + elif event_name =3D=3D "evsel(syscalls:sys_exit_futex)": + handle_end(sample.sample_tid, sample.sample_time) + + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser(description=3D"Measure futex contention= ") + ap.add_argument("-i", "--input", default=3D"perf.data", help=3D"Input = file name") + args =3D ap.parse_args() + + session =3D perf.session(perf.data(args.input), sample=3Dprocess_event) + session.process_events() + + for ((t, u), stats) in sorted(durations.items()): + avg_ns =3D stats.avg() + print(f"{process_names.get(t, 'unknown')}[{t}] lock {u:x} contende= d {stats.count} times, " + f"{avg_ns:.0f} avg ns [max: {stats.max_time} ns, min {stats.= min_time} ns]") --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2B2CD3A542E for ; Sat, 25 Apr 2026 22:51:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157481; cv=none; b=a/KfwYrZZ3Tuykvf5SzClOamxQBYqdNMfE2c9wB0zOs+cyxD6d/z/wSF+DtavwUJetwWyYcR24A/e6t7MtFJiyTV5gYxaBOzSlmOBMIxD42506b49+6rf0O6+FuqnqNoUVuDoQNtZa7iVs84xnCiPqj8jQApqMYfTjKUjy69FMg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157481; c=relaxed/simple; bh=4/G4WOpWLeIY4Q1/giU7GTp/xIECo8qdANOUyVELEPU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=QcA4gBBTupA0l9FD2gq0Z59euC8yAq9d+btIJysQ2p17WNGyCW/OWoiM4LKC/HeWrlAwZ2P6PdOjDbN1Lo7c8il8ZzZjSt2e3/HvEGQdbup69S+VYvH59UEBcsXTqv8mtleSa6PoKWf1WCGoPRz5adhV9/KdUT17HRKQ6tkYfcQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=qmq9VulB; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="qmq9VulB" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2da19227bc1so20923470eec.1 for ; Sat, 25 Apr 2026 15:51:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157475; x=1777762275; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=efNil+PNmmmvTcZKEec15sBNEnsydHEhEYd+ZnlvuZ8=; b=qmq9VulBdBFM/9W8z8q8r0EjIMgs2o4mVL5PDo4URTd2aeOotyZUSwn4N7qaPPDhL1 e0V4zpuF6tcFf2IJJzlA3DlXZen0QsIHW2oWGldNsNRKbOlGE3/4p9AWLui2vihtKj9c A2uo5N2RyIHkBCtYk0jiXowsskLE79ZzXy16CyhYh37uYoQLJDZsiRXrciAhgpe7mFYG v4fqEL5ZGWoqT+BctJBp8obgMEJeKqbnBb7/cluFp+SVro6QLwbolVLd8efZcZ8jerA/ 8aRp5YQyLrcBbKzxnLbePVT40KgyWK3hbxrIlLtzB43hd2e1KZwQ7vUd7wbNhouSc/pH V0Bg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157475; x=1777762275; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=efNil+PNmmmvTcZKEec15sBNEnsydHEhEYd+ZnlvuZ8=; b=kNKZqbudwIcyUstHbEiK3kgKmZKJqokyknyibU340n/PaGd0LTNUo2EDYRW9WNl0Hd N7f0HfGagSoHdxRGKiT47mv+1TC6ryOwpLvy8Re2fkAn7xBYQrVbqb7bJCBhohFAjE67 1HzfcUUeyCHmcUrA3h1kFXhMaGs3gMbRvKguJcy4mtzqGWHhaMMaiKUG0ZH3RffNPuDP ZYlZad08Opd2VrtU0eww1vvTvzPKd94p2ZWoYKjZxRFySP3Vo++LXr9rxzZYCawAGkR9 9ofsiQkucK6cyu7SVYahp4SH7UqpP8xT6qtl7MhhB9UL/REwaYYBPJttiZUZ6wVwghbT a5Fg== X-Forwarded-Encrypted: i=1; AFNElJ/t47w7ayhtQNYQit61bBM/qzOodctZLbTljDA1T5AH9FAgSRKO5fqd9Bd/XBIBgClgDn9sbW1bFf03Vb0=@vger.kernel.org X-Gm-Message-State: AOJu0Yxd+shmQ5EOmF1j7vkkesRMQoDVKkQ+1wHjQUX+nZPaIMQc++fn 7f8rRwKIPIURliy1BcgkcP4h8SkUH+aEcgSfMkTFlYzbBht01zY1cvwU2uDUu7ifp2//jFA3Qcp I2jx4ZyWtDA== X-Received: from dycf28.prod.google.com ([2002:a05:7300:50dc:b0:2d9:6c52:c24a]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7301:4186:b0:2de:cc07:e99 with SMTP id 5a478bee46e88-2e464dafc4cmr18867631eec.7.1777157475156; Sat, 25 Apr 2026 15:51:15 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:22 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-31-irogers@google.com> Subject: [PATCH v7 30/59] perf flamegraph: Port flamegraph to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a port of the flamegraph script that uses the perf python module directly. This approach improves performance by avoiding intermediate dictionaries for event fields. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v5: 1. Fix Event Filtering: Corrected event filtering check to search for a substring match within the parsed event string, preventing all events from being dropped due to the `evsel(...)` wrapper. v6: - Fixed terminal injection risk by not printing unverified content in prompt. --- tools/perf/python/flamegraph.py | 250 ++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100755 tools/perf/python/flamegraph.py diff --git a/tools/perf/python/flamegraph.py b/tools/perf/python/flamegraph= .py new file mode 100755 index 000000000000..b0eb5844b772 --- /dev/null +++ b/tools/perf/python/flamegraph.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +flamegraph.py - create flame graphs from perf samples using perf python mo= dule +""" + +import argparse +import hashlib +import json +import os +import subprocess +import sys +import urllib.request +from typing import Dict, Optional, Union +import perf + +MINIMAL_HTML =3D """ + + + +
+ + + +""" + +class Node: + """A node in the flame graph tree.""" + def __init__(self, name: str, libtype: str): + self.name =3D name + self.libtype =3D libtype + self.value: int =3D 0 + self.children: dict[str, Node] =3D {} + + def to_json(self) -> Dict[str, Union[str, int, list[Dict]]]: + """Convert the node to a JSON-serializable dictionary.""" + return { + "n": self.name, + "l": self.libtype, + "v": self.value, + "c": [x.to_json() for x in self.children.values()] + } + + +class FlameGraphCLI: + """Command-line interface for generating flame graphs.""" + def __init__(self, args): + self.args =3D args + self.stack =3D Node("all", "root") + self.session =3D None + + @staticmethod + def get_libtype_from_dso(dso: Optional[str]) -> str: + """Determine the library type from the DSO name.""" + if dso and (dso =3D=3D "[kernel.kallsyms]" or dso.endswith("/vmlin= ux") or dso =3D=3D "[kernel]"): + return "kernel" + return "" + + @staticmethod + def find_or_create_node(node: Node, name: str, libtype: str) -> Node: + """Find a child node with the given name or create a new one.""" + if name in node.children: + return node.children[name] + child =3D Node(name, libtype) + node.children[name] =3D child + return child + + def process_event(self, sample) -> None: + """Process a single perf sample event.""" + if self.args.event_name and self.args.event_name not in str(sample= .evsel): + return + + pid =3D sample.sample_pid + dso_type =3D "" + try: + thread =3D self.session.find_thread(sample.sample_tid) + comm =3D thread.comm() + except Exception: + comm =3D "[unknown]" + + if pid =3D=3D 0: + comm =3D "swapper" + dso_type =3D "kernel" + else: + comm =3D f"{comm} ({pid})" + + node =3D self.find_or_create_node(self.stack, comm, dso_type) + + callchain =3D sample.callchain + if callchain: + # We want to traverse from root to leaf. + # perf callchain iterator gives leaf to root. + # We collect them and reverse. + frames =3D list(callchain) + for entry in reversed(frames): + name =3D entry.symbol or "[unknown]" + libtype =3D self.get_libtype_from_dso(entry.dso) + node =3D self.find_or_create_node(node, name, libtype) + else: + # Fallback if no callchain + name =3D getattr(sample, "symbol", "[unknown]") + libtype =3D self.get_libtype_from_dso(getattr(sample, "dso", "= [unknown]")) + node =3D self.find_or_create_node(node, name, libtype) + + node.value +=3D 1 + + def get_report_header(self) -> str: + """Get the header from the perf report.""" + try: + input_file =3D self.args.input or "perf.data" + output =3D subprocess.check_output(["perf", "report", "--heade= r-only", "-i", input_file]) + result =3D output.decode("utf-8") + if self.args.event_name: + result +=3D "\nFocused event: " + self.args.event_name + return result + except Exception: + return "" + + def run(self) -> None: + """Run the flame graph generation.""" + input_file =3D self.args.input or "perf.data" + if not os.path.exists(input_file): + print(f"Error: {input_file} not found. (try 'perf record' firs= t)", file=3Dsys.stderr) + sys.exit(1) + + try: + self.session =3D perf.session(perf.data(input_file), + sample=3Dself.process_event) + except Exception as e: + print(f"Error opening session: {e}", file=3Dsys.stderr) + sys.exit(1) + + self.session.process_events() + + stacks_json =3D json.dumps(self.stack, default=3Dlambda x: x.to_js= on()) + # Escape HTML special characters to prevent XSS + stacks_json =3D stacks_json.replace("<", "\\u003c") \ + .replace(">", "\\u003e").replace("&", "\\u0026") + + if self.args.format =3D=3D "html": + report_header =3D self.get_report_header() + options =3D { + "colorscheme": self.args.colorscheme, + "context": report_header + } + options_json =3D json.dumps(options) + options_json =3D options_json.replace("<", "\\u003c") \ + .replace(">", "\\u003e").replace("&", "\\u0026") + + template =3D self.args.template + template_md5sum =3D None + output_str =3D None + + if not os.path.isfile(template): + if template.startswith("http://") or template.startswith("= https://"): + if not self.args.allow_download: + print("Warning: Downloading templates is disabled.= " + "Use --allow-download.", file=3Dsys.stderr) + template =3D None + else: + print(f"Warning: Template file '{template}' not found.= ", file=3Dsys.stderr) + if self.args.allow_download: + print("Using default CDN template.", file=3Dsys.st= derr) + template =3D ( + "https://cdn.jsdelivr.net/npm/d3-flame-graph@4= .1.3/dist/templates/" + "d3-flamegraph-base.html" + ) + template_md5sum =3D "143e0d06ba69b8370b9848dcd6ae3= f36" + else: + template =3D None + + use_minimal =3D False + try: + if not template: + use_minimal =3D True + elif template.startswith("http"): + with urllib.request.urlopen(template) as url_template: + output_str =3D "".join([l.decode("utf-8") for l in= url_template.readlines()]) + else: + with open(template, "r", encoding=3D"utf-8") as f: + output_str =3D f.read() + except Exception as err: + print(f"Error reading template {template}: {err}\n", file= =3Dsys.stderr) + use_minimal =3D True + + if use_minimal: + print("Using internal minimal HTML that refers to d3's web= site. JavaScript " + + "loaded this way from a local file may be blocked un= less your " + + "browser has relaxed permissions. Run with '--allow-= download' to fetch" + + "the full D3 HTML template.", file=3Dsys.stderr) + output_str =3D MINIMAL_HTML + + elif template_md5sum: + assert output_str is not None + download_md5sum =3D hashlib.md5(output_str.encode("utf-8")= ).hexdigest() + if download_md5sum !=3D template_md5sum: + s =3D None + while s not in ["y", "n"]: + s =3D input(f"""Unexpected template md5sum. +{download_md5sum} !=3D {template_md5sum}, for: +{template} +continue?[yn] """).lower() + if s =3D=3D "n": + sys.exit(1) + + assert output_str is not None + output_str =3D output_str.replace("/** @options_json **/", opt= ions_json) + output_str =3D output_str.replace("/** @flamegraph_json **/", = stacks_json) + output_fn =3D self.args.output or "flamegraph.html" + else: + output_str =3D stacks_json + output_fn =3D self.args.output or "stacks.json" + + if output_fn =3D=3D "-": + sys.stdout.write(output_str) + else: + print(f"dumping data to {output_fn}") + with open(output_fn, "w", encoding=3D"utf-8") as out: + out.write(output_str) + + +if __name__ =3D=3D "__main__": + parser =3D argparse.ArgumentParser(description=3D"Create flame graphs = using perf python module.") + parser.add_argument("-f", "--format", default=3D"html", choices=3D["js= on", "html"], + help=3D"output file format") + parser.add_argument("-o", "--output", help=3D"output file name") + parser.add_argument("--template", + default=3D"/usr/share/d3-flame-graph/d3-flamegraph= -base.html", + help=3D"path to flame graph HTML template") + parser.add_argument("--colorscheme", default=3D"blue-green", + help=3D"flame graph color scheme", choices=3D["blu= e-green", "orange"]) + parser.add_argument("-i", "--input", help=3D"input perf.data file") + parser.add_argument("--allow-download", default=3DFalse, action=3D"sto= re_true", + help=3D"allow unprompted downloading of HTML templ= ate") + parser.add_argument("-e", "--event", default=3D"", dest=3D"event_name"= , type=3Dstr, + help=3D"specify the event to generate flamegraph f= or") + + cli_args =3D parser.parse_args() + cli =3D FlameGraphCLI(cli_args) + cli.run() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B710E3A5E72 for ; Sat, 25 Apr 2026 22:51:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157482; cv=none; b=oEZ3dqrWZtPPOf70dg3PQqilMiIaDg2M6D+LLqYZB0/WhsPGPHsVQg4X9w6xuxzrUKY3VJlWeHWyIQ+IdlV/YOEfnMW4Rhj3qHMaIvbWxa9JEQYNxR8UwQdcybmPYY3/DFK4mJoQQDoyNhks84BzqyQEmUrOwj52zXhjV/1i4HE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157482; c=relaxed/simple; bh=y2GLChekV4PMMYmANDUuSYZhvvMXHJinf6rXMFkaxwE=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=dJua3ViuDqyF/QkGQU8P/KOzDxIT69KobSRmeM0UJyh3CaHm/rR9fnpgYh4wXpC6WLvKr99aH8n5R27qVmvaPMMO2O9DyqqML59+WzqEDnRzeBi1vgIdgAyh7OReBRslx9PFhOAOnnSXskeMcFF5xgcAu/L5orsYAIYIQcGletQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=g1TTvjUk; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="g1TTvjUk" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2da19227bc1so20923539eec.1 for ; Sat, 25 Apr 2026 15:51:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157478; x=1777762278; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=NR1gzF3F6kI5f0yQJQV926hkNwQ4+t7RNPW4Qp/LJSM=; b=g1TTvjUkAQnfsp5x27ikL6wpIO0dlN+Sh5nfsD9MKtVFp6WnqHXo1+nY+4/4354nal afYjTq8B5MUVmAe7qt2+V77SyrmFLYozzWOUzrJxDLX7gxlWzefaCjbgDT6crdkjXque vhkKl5LRuO47lRl1GkP8RSyEAyftUuYD2oPIgOO6UwrOH+p+x349Uk+GyAwBstSgvTFJ fvANfma+7rYEMYXmmhCK+IfRQhCQEbQDo5Ss7pOnkumL2bsV9XhSPu52UiMsu8wwBEeM XDIrPpvA01wbHe3fCRPyJAz33Ss/geoOGurJI+4rvUW0it95rXgmUqw9FP133oLyOH5E boew== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157478; x=1777762278; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=NR1gzF3F6kI5f0yQJQV926hkNwQ4+t7RNPW4Qp/LJSM=; b=WoJ/BsqO3m/o2Q+D3RAxk1NNH2DwhuhBdzmHMnwVnCOkhqdV5twcmXyKXAGN3bHQTq PORU2YXmZmFAXbi/6bRcwqu9mCe9jj6b68q4pS5NYGCaqnQJA9Muetf+Bb5vFCamGcZr 7/vZkGbvPptMw0kZ0iGN9tadTeSDiVvhgnRf3csjlX69Chk4ZCIZQ+Z0Kqng7Z3Q30S+ AEDt58Nz9IigC1Y7WW0Smk3DohEQjM0ELmTyYxBxwSpw8IR2qDgdZuYg8+9QgIghlECf LepW8lQvNC0A6XmFxev/rn6llvLClPrNwH66BsfTOSEKQkHShTHEHgpoZeDqsZU1JxoC qP4Q== X-Forwarded-Encrypted: i=1; AFNElJ+y6c7Trrn9saxmhl/DT65vEO4nabkegwgtGRbIdsMIJbwa/G0IioQQgrodHue5KSKA0xcYB1Ycz1LJH8g=@vger.kernel.org X-Gm-Message-State: AOJu0YwkF+kM4JDFNumfVS4aJpn+lpDaqPlXCeIQvTZjqvTsLAUCsjli KoWS3w3fraX4PVj1utzxKAMXBxtPV9Ka7WnvCNn/HfIu/gvyslYOHEXAni2gPu/S5rcxI8JfgDj Q7OTLCCCEfg== X-Received: from dycnr28-n1.prod.google.com ([2002:a05:7300:e9dc:10b0:2d5:d26c:d4bc]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7301:129b:b0:2ea:ed7c:912f with SMTP id 5a478bee46e88-2eaed7c9a68mr2186737eec.27.1777157477301; Sat, 25 Apr 2026 15:51:17 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:23 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-32-irogers@google.com> Subject: [PATCH v7 31/59] perf gecko: Port gecko to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a port of the gecko script that uses the perf python module directly. This approach is significantly faster than using perf script callbacks as it avoids creating intermediate dictionaries for all event fields. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Improved Portability: Replaced the non-portable uname -op call with platform.system() and platform.machine() , preventing potential crashes on non-Linux platforms like macOS or BSD. 2. Robust Fallbacks: Fixed getattr calls for symbol and dso to explicitly handle None values, preventing literal "None (in None)" strings in the output when resolution fails. 3. Network Security: Bound the HTTP server to 127.0.0.1 (localhost) instead of 0.0.0.0 (all interfaces), ensuring the current directory is not exposed to the local network. 4. Avoided Port Conflicts: Switched from hardcoded port 8000 to port 0, allowing the operating system to automatically select an available free port. 5. Fixed Race Condition: Moved HTTPServer creation to the main thread, ensuring the server is bound and listening before the browser is launched to fetch the file. 6. Browser Spec Compliance: Used 127.0.0.1 instead of localhost in the generated URL to ensure modern browsers treat the connection as a secure origin, avoiding mixed content blocks. v6: - Fixed CWD exposure and symlink attack risks by using a secure temporary directory for the HTTP server. --- tools/perf/python/gecko.py | 385 +++++++++++++++++++++++++++++++++++++ 1 file changed, 385 insertions(+) create mode 100755 tools/perf/python/gecko.py diff --git a/tools/perf/python/gecko.py b/tools/perf/python/gecko.py new file mode 100755 index 000000000000..1f152e1eca52 --- /dev/null +++ b/tools/perf/python/gecko.py @@ -0,0 +1,385 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +gecko.py - Convert perf record output to Firefox's gecko profile format +""" + +import argparse +import functools +import json +import os +import platform +import sys +import tempfile +import threading +import urllib.parse +import webbrowser +from dataclasses import dataclass, field +from http.server import HTTPServer, SimpleHTTPRequestHandler +from typing import Dict, List, NamedTuple, Optional, Tuple + +import perf + + +# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7= 457fee1d66cd4e2737/src/types/gecko-profile.js#L156 +class Frame(NamedTuple): + """A single stack frame in the gecko profile format.""" + string_id: int + relevantForJS: bool + innerWindowID: int + implementation: None + optimizations: None + line: None + column: None + category: int + subcategory: Optional[int] + + +# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7= 457fee1d66cd4e2737/src/types/gecko-profile.js#L216 +class Stack(NamedTuple): + """A single stack in the gecko profile format.""" + prefix_id: Optional[int] + frame_id: int + + +# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7= 457fee1d66cd4e2737/src/types/gecko-profile.js#L90 +class Sample(NamedTuple): + """A single sample in the gecko profile format.""" + stack_id: Optional[int] + time_ms: float + responsiveness: int + + +@dataclass +class Tables: + """Interned tables for the gecko profile format.""" + frame_table: List[Frame] =3D field(default_factory=3Dlist) + string_table: List[str] =3D field(default_factory=3Dlist) + string_map: Dict[str, int] =3D field(default_factory=3Ddict) + stack_table: List[Stack] =3D field(default_factory=3Dlist) + stack_map: Dict[Tuple[Optional[int], int], int] =3D field(default_fact= ory=3Ddict) + frame_map: Dict[str, int] =3D field(default_factory=3Ddict) + + +@dataclass +class Thread: + """A builder for a profile of the thread.""" + comm: str + pid: int + tid: int + user_category: int + kernel_category: int + samples: List[Sample] =3D field(default_factory=3Dlist) + tables: Tables =3D field(default_factory=3DTables) + + def _intern_stack(self, frame_id: int, prefix_id: Optional[int]) -> in= t: + """Gets a matching stack, or saves the new stack. Returns a Stack = ID.""" + key =3D (prefix_id, frame_id) + stack_id =3D self.tables.stack_map.get(key) + if stack_id is None: + stack_id =3D len(self.tables.stack_table) + self.tables.stack_table.append(Stack(prefix_id=3Dprefix_id, fr= ame_id=3Dframe_id)) + self.tables.stack_map[key] =3D stack_id + return stack_id + + def _intern_string(self, string: str) -> int: + """Gets a matching string, or saves the new string. Returns a Stri= ng ID.""" + string_id =3D self.tables.string_map.get(string) + if string_id is not None: + return string_id + string_id =3D len(self.tables.string_table) + self.tables.string_table.append(string) + self.tables.string_map[string] =3D string_id + return string_id + + def _intern_frame(self, frame_str: str) -> int: + """Gets a matching stack frame, or saves the new frame. Returns a = Frame ID.""" + frame_id =3D self.tables.frame_map.get(frame_str) + if frame_id is not None: + return frame_id + frame_id =3D len(self.tables.frame_table) + self.tables.frame_map[frame_str] =3D frame_id + string_id =3D self._intern_string(frame_str) + + category =3D self.user_category + if (frame_str.find('kallsyms') !=3D -1 or + frame_str.find('/vmlinux') !=3D -1 or + frame_str.endswith('.ko)')): + category =3D self.kernel_category + + self.tables.frame_table.append(Frame( + string_id=3Dstring_id, + relevantForJS=3DFalse, + innerWindowID=3D0, + implementation=3DNone, + optimizations=3DNone, + line=3DNone, + column=3DNone, + category=3Dcategory, + subcategory=3DNone, + )) + return frame_id + + def add_sample(self, comm: str, stack: List[str], time_ms: float) -> N= one: + """Add a timestamped stack trace sample to the thread builder.""" + if self.comm !=3D comm: + self.comm =3D comm + + prefix_stack_id: Optional[int] =3D None + for frame in stack: + frame_id =3D self._intern_frame(frame) + prefix_stack_id =3D self._intern_stack(frame_id, prefix_stack_= id) + + if prefix_stack_id is not None: + self.samples.append(Sample(stack_id=3Dprefix_stack_id, + time_ms=3Dtime_ms, + responsiveness=3D0)) + + def to_json_dict(self) -> Dict: + """Converts current Thread to GeckoThread JSON format.""" + return { + "tid": self.tid, + "pid": self.pid, + "name": self.comm, + "markers": { + "schema": { + "name": 0, + "startTime": 1, + "endTime": 2, + "phase": 3, + "category": 4, + "data": 5, + }, + "data": [], + }, + "samples": { + "schema": { + "stack": 0, + "time": 1, + "responsiveness": 2, + }, + "data": self.samples + }, + "frameTable": { + "schema": { + "location": 0, + "relevantForJS": 1, + "innerWindowID": 2, + "implementation": 3, + "optimizations": 4, + "line": 5, + "column": 6, + "category": 7, + "subcategory": 8, + }, + "data": self.tables.frame_table, + }, + "stackTable": { + "schema": { + "prefix": 0, + "frame": 1, + }, + "data": self.tables.stack_table, + }, + "stringTable": self.tables.string_table, + "registerTime": 0, + "unregisterTime": None, + "processType": "default", + } + + +class CORSRequestHandler(SimpleHTTPRequestHandler): + """Enable CORS for requests from profiler.firefox.com.""" + def end_headers(self): + self.send_header('Access-Control-Allow-Origin', 'https://profiler.= firefox.com') + super().end_headers() + + +@dataclass +class CategoryData: + """Category configuration for the gecko profile.""" + user_index: int =3D 0 + kernel_index: int =3D 1 + categories: List[Dict] =3D field(default_factory=3Dlist) + + +class GeckoCLI: + """Command-line interface for converting perf data to Gecko format.""" + def __init__(self, args): + self.args =3D args + self.tid_to_thread: Dict[int, Thread] =3D {} + self.start_time_ms: Optional[float] =3D None + self.session =3D None + self.product =3D f"{platform.system()} {platform.machine()}" + self.cat_data =3D CategoryData( + categories=3D[ + { + "name": 'User', + "color": args.user_color, + "subcategories": ['Other'] + }, + { + "name": 'Kernel', + "color": args.kernel_color, + "subcategories": ['Other'] + }, + ] + ) + + def process_event(self, sample) -> None: + """Process a single perf sample event.""" + if self.args.event_name and self.args.event_name not in str(sample= .evsel): + return + + # sample_time is in nanoseconds. Gecko wants milliseconds. + time_ms =3D sample.sample_time / 1000000.0 + pid =3D sample.sample_pid + tid =3D sample.sample_tid + + if self.start_time_ms is None: + self.start_time_ms =3D time_ms + + try: + thread_info =3D self.session.find_thread(tid) + comm =3D thread_info.comm() + except Exception: + comm =3D "[unknown]" + + stack =3D [] + callchain =3D sample.callchain + if callchain: + for entry in callchain: + symbol =3D entry.symbol or "[unknown]" + dso =3D entry.dso or "[unknown]" + stack.append(f"{symbol} (in {dso})") + # Reverse because Gecko wants root first. + stack.reverse() + else: + # Fallback if no callchain is present + try: + # If the perf module exposes symbol/dso directly on sample + # when callchain is missing, we use them. + symbol =3D getattr(sample, 'symbol', '[unknown]') or '[unk= nown]' + dso =3D getattr(sample, 'dso', '[unknown]') or '[unknown]' + stack.append(f"{symbol} (in {dso})") + except AttributeError: + stack.append("[unknown] (in [unknown])") + + thread =3D self.tid_to_thread.get(tid) + if thread is None: + thread =3D Thread(comm=3Dcomm, pid=3Dpid, tid=3Dtid, + user_category=3Dself.cat_data.user_index, + kernel_category=3Dself.cat_data.kernel_index) + self.tid_to_thread[tid] =3D thread + thread.add_sample(comm=3Dcomm, stack=3Dstack, time_ms=3Dtime_ms) + + def run(self) -> None: + """Run the conversion process.""" + input_file =3D self.args.input or "perf.data" + if not os.path.exists(input_file): + print(f"Error: {input_file} not found.", file=3Dsys.stderr) + sys.exit(1) + + try: + self.session =3D perf.session(perf.data(input_file), sample=3D= self.process_event) + except Exception as e: + print(f"Error opening session: {e}", file=3Dsys.stderr) + sys.exit(1) + + self.session.process_events() + + threads =3D [t.to_json_dict() for t in self.tid_to_thread.values()] + + gecko_profile =3D { + "meta": { + "interval": 1, + "processType": 0, + "product": self.product, + "stackwalk": 1, + "debug": 0, + "gcpoison": 0, + "asyncstack": 1, + "startTime": self.start_time_ms, + "shutdownTime": None, + "version": 24, + "presymbolicated": True, + "categories": self.cat_data.categories, + "markerSchema": [], + }, + "libs": [], + "threads": threads, + "processes": [], + "pausedRanges": [], + } + + output_file =3D self.args.save_only + if output_file is None: + self._write_and_launch(gecko_profile) + else: + print(f'[ perf gecko: Captured and wrote into {output_file} ]') + with open(output_file, 'w', encoding=3D'utf-8') as f: + json.dump(gecko_profile, f, indent=3D2) + + def _write_and_launch(self, profile: Dict) -> None: + """Write the profile to a file and launch the Firefox profiler.""" + print("Starting Firefox Profiler on your default browser...") + =20 + with tempfile.TemporaryDirectory() as tmp_dir_name: + filename =3D os.path.join(tmp_dir_name, 'gecko_profile.json') + =20 + with open(filename, 'w', encoding=3D'utf-8') as f: + json.dump(profile, f, indent=3D2) + =20 + handler =3D functools.partial(CORSRequestHandler, directory=3D= tmp_dir_name) + try: + httpd =3D HTTPServer(('127.0.0.1', 0), handler) + except OSError as e: + print(f"Error starting HTTP server: {e}", file=3Dsys.stder= r) + sys.exit(1) + =20 + port =3D httpd.server_port + =20 + def start_server(): + httpd.serve_forever() + =20 + thread =3D threading.Thread(target=3Dstart_server, daemon=3DTr= ue) + thread.start() + =20 + # Open the browser + safe_string =3D urllib.parse.quote_plus(f'http://127.0.0.1:{po= rt}/gecko_profile.json') + url =3D f'https://profiler.firefox.com/from-url/{safe_string}' + webbrowser.open(url) + =20 + print(f'[ perf gecko: Captured and wrote into {filename} ]') + print("Press Ctrl+C to stop the local server.") + try: + # Keep the main thread alive so the daemon thread can serv= e requests + stop_event =3D threading.Event() + while True: + stop_event.wait(1) + except KeyboardInterrupt: + print("\nStopping server...") + httpd.shutdown() + + +if __name__ =3D=3D "__main__": + parser =3D argparse.ArgumentParser( + description=3D"Convert perf.data to Firefox's Gecko Profile format" + ) + parser.add_argument('--user-color', default=3D'yellow', + help=3D'Color for the User category', + choices=3D['yellow', 'blue', 'purple', 'green', 'o= range', 'red', + 'grey', 'magenta']) + parser.add_argument('--kernel-color', default=3D'orange', + help=3D'Color for the Kernel category', + choices=3D['yellow', 'blue', 'purple', 'green', 'o= range', 'red', + 'grey', 'magenta']) + parser.add_argument('--save-only', + help=3D'Save the output to a file instead of openi= ng Firefox\'s profiler') + parser.add_argument("-i", "--input", help=3D"input perf.data file") + parser.add_argument("-e", "--event", default=3D"", dest=3D"event_name"= , type=3Dstr, + help=3D"specify the event to generate gecko profil= e for") + + cli_args =3D parser.parse_args() + cli =3D GeckoCLI(cli_args) + cli.run() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7EFD239B482 for ; Sat, 25 Apr 2026 22:51:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157483; cv=none; b=l7m9bydaHydsd/5QfVLvAR+57+1WL6eRwg7DQt+mhM9ojyn2XwXXnSiCKPHIDTDZIZdzN5mgIp+MVD9XKXHe5Z0N+7tZNUSiPjjZVrEIcyL1PB/0FJOkKfcMkFeiP9htgq2S3193XfD/BluW+5mJ2awaKtZoYkaLPemCdcPcYF4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157483; c=relaxed/simple; bh=85dZ9aAayO9i8dMzyv7DpEJFQ7DTaIYbs+d/4v5mn1g=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=QyA/szwyJKTXwzzUASgmh5WlDUaifvjjvBGEmTF9XO0/5W0lbS58YQsff/ewVfKeAuJ7i2JRejmXC6S1HjxZrKRO+78W5T5P4jQFeoNBWoNkpYs7c2RdgukkJ3XdFtx38EPMdxMcU6OmkbcDMHuBwfn9d1hA4ZUiXBQmYPD5YKY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=DDPhT+XE; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="DDPhT+XE" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2ba8013a9e3so10459618eec.0 for ; Sat, 25 Apr 2026 15:51:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157480; x=1777762280; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=itAjjttTC6ExRFqiT4XahID1dD5ZJo1i7yacdRBRaZI=; b=DDPhT+XE6OCg/ZcdiAU0TWVwP3fBNkRuo7NesuvEVhtgMUgihnpjxklkAAJbB1bXw1 dyVTbxKuCKFTItsptJkGpa4cIZRbPL2hxF/x9ZYUbzqZ49bcZ8YBw+2v9hmNPXd/i+mh l5B/IzVd1nH/3wXSTKanrvEM4D9fyNOKYY8t0vuo9cWOU8eQNUZLcWe3oPIBG04RmHEa G7Dd+0v77a+x+XA4e8MbuJ7Hyy7F4RJnHxZaD3oNbmz+PuPNUXgHah7OIDH5pLrAa8a4 6z0IVb/e5zRwUIn+X/QnGGw9P5wanZh8Wr1NcwYmVcHXZnTsPcoMqjuRQ9bEgPrtbApk FBtg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157480; x=1777762280; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=itAjjttTC6ExRFqiT4XahID1dD5ZJo1i7yacdRBRaZI=; b=LXrjxY9Q6KuWeqW8KVMnE1t/3LxgIAlpQ0E2Gf2k9Rr9USzlq2hshJoeN2QJ687srN sBjrar9c0ni5NroziIezrW5ur1jyfx2Klxhxw58+6rPRxaaVZIK2rwtRTD1iB2IYWEOf o/JRHphGwSWdQAG+OPkDlcw7Hh1I0r8NEnmvFJn5aiW63PZnK7/1DNkKQeZgjDCyYD9m +SQIGQnEYmDevXTGPfCbJ3LfugVJpjpjvIIjo3C9tQ3dq4jRNmlEkraQAap17lc+z656 ZXVnwkyM5YcQQAgkdsZ2iqOKjlHYN8ossy5Q9tkwcyfT0fHM+7UwFAqu95TqR8wi56jJ w0Gw== X-Forwarded-Encrypted: i=1; AFNElJ8SqHeBi6qOt/0Tzzwa4ZgNY00UNB+odE9WOS0NN4WPMdGAaHGSGTfVnZR/Cj2GDGGCY63nMQW5apxwK04=@vger.kernel.org X-Gm-Message-State: AOJu0YwkVPN2nPGH8mFYzI3yOXqvwwlMYisbcYtGLyyPQzGbm7A2zCEC yEbwIZCoOZGdz61CCOYBuqjZuLXyQfQvgnqo1fOWdjQyB2FPylk68/RX49v7H6GJl4Ih6ivWimo KLrilyiAk0g== X-Received: from dlbpu6.prod.google.com ([2002:a05:7022:e886:b0:12c:8eb0:61d9]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:50d:b0:12a:747e:5b5c with SMTP id a92af1059eb24-12c73fa3af3mr19374737c88.24.1777157479499; Sat, 25 Apr 2026 15:51:19 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:24 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-33-irogers@google.com> Subject: [PATCH v7 32/59] perf arm-cs-trace-disasm: Port arm-cs-trace-disasm to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a port of the arm-cs-trace-disasm script that uses the perf python module directly. This approach is significantly faster than using perf script callbacks as it avoids creating intermediate dictionaries for all event fields. Update the testing to use the ported script. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Added Missing Import: Added import perf at the top of arm-cs-trace-disasm.py . 2. Fixed Unpacking Error: Updated the call to sample.srccode() to expect a 3-tuple instead of a 4-tuple, matching the return type in the C extension. 3. Fixed Termination Logic: Replaced return with sys.exit(0) when the stop_time or stop_sample limits are reached to properly terminate the processing loop. 4. Fixed Test Path: Updated script_path in test_arm_coresight_disasm.sh to point to ../../python/arm-cs-trace-disasm.py instead of the old legacy path. --- tools/perf/python/arm-cs-trace-disasm.py | 338 ++++++++++++++++++ .../tests/shell/test_arm_coresight_disasm.sh | 12 +- 2 files changed, 345 insertions(+), 5 deletions(-) create mode 100755 tools/perf/python/arm-cs-trace-disasm.py diff --git a/tools/perf/python/arm-cs-trace-disasm.py b/tools/perf/python/a= rm-cs-trace-disasm.py new file mode 100755 index 000000000000..92c97cca6f66 --- /dev/null +++ b/tools/perf/python/arm-cs-trace-disasm.py @@ -0,0 +1,338 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +arm-cs-trace-disasm.py: ARM CoreSight Trace Dump With Disassember using pe= rf python module +""" + +import os +from os import path +import re +from subprocess import check_output +import argparse +import platform +import sys +from typing import Dict, List, Optional + +import perf + +# Initialize global dicts and regular expression +DISASM_CACHE: Dict[str, List[str]] =3D {} +CPU_DATA: Dict[str, int] =3D {} +DISASM_RE =3D re.compile(r"^\s*([0-9a-fA-F]+):") +DISASM_FUNC_RE =3D re.compile(r"^\s*([0-9a-fA-F]+)\s.*:") +CACHE_SIZE =3D 64*1024 +SAMPLE_IDX =3D -1 + +GLB_SOURCE_FILE_NAME: Optional[str] =3D None +GLB_LINE_NUMBER: Optional[int] =3D None +GLB_DSO: Optional[str] =3D None + +KVER =3D platform.release() +VMLINUX_PATHS =3D [ + f"/usr/lib/debug/boot/vmlinux-{KVER}.debug", + f"/usr/lib/debug/lib/modules/{KVER}/vmlinux", + f"/lib/modules/{KVER}/build/vmlinux", + f"/usr/lib/debug/boot/vmlinux-{KVER}", + f"/boot/vmlinux-{KVER}", + "/boot/vmlinux", + "vmlinux" +] + +def default_objdump() -> str: + """Return the default objdump path from perf config or 'objdump'.""" + try: + config =3D perf.config_get("annotate.objdump") + return str(config) if config else "objdump" + except (AttributeError, TypeError): + return "objdump" + +def find_vmlinux() -> Optional[str]: + """Find the vmlinux file in standard paths.""" + if hasattr(find_vmlinux, "path"): + return getattr(find_vmlinux, "path") + + for v in VMLINUX_PATHS: + if os.access(v, os.R_OK): + setattr(find_vmlinux, "path", v) + return v + setattr(find_vmlinux, "path", None) + return None + +def get_dso_file_path(dso_name: str, dso_build_id: str, vmlinux: Optional[= str]) -> str: + """Return the path to the DSO file.""" + if dso_name in ("[kernel.kallsyms]", "vmlinux"): + if vmlinux: + return vmlinux + return find_vmlinux() or dso_name + + if dso_name =3D=3D "[vdso]": + append =3D "/vdso" + else: + append =3D "/elf" + + buildid_dir =3D os.environ.get('PERF_BUILDID_DIR') + if not buildid_dir: + buildid_dir =3D os.path.join(os.environ.get('HOME', ''), '.debug') + + dso_path =3D buildid_dir + "/" + dso_name + "/" + dso_build_id + append + # Replace duplicate slash chars to single slash char + dso_path =3D dso_path.replace('//', '/', 1) + return dso_path + +def read_disam(dso_fname: str, dso_start: int, start_addr: int, + stop_addr: int, objdump: str) -> List[str]: + """Read disassembly from a DSO file using objdump.""" + addr_range =3D f"{start_addr}:{stop_addr}:{dso_fname}" + + # Don't let the cache get too big, clear it when it hits max size + if len(DISASM_CACHE) > CACHE_SIZE: + DISASM_CACHE.clear() + + if addr_range in DISASM_CACHE: + disasm_output =3D DISASM_CACHE[addr_range] + else: + start_addr =3D start_addr - dso_start + stop_addr =3D stop_addr - dso_start + disasm =3D [objdump, "-d", "-z", + f"--start-address=3D{start_addr:#x}", + f"--stop-address=3D{stop_addr:#x}"] + disasm +=3D [dso_fname] + disasm_output =3D check_output(disasm).decode('utf-8').split('\n') + DISASM_CACHE[addr_range] =3D disasm_output + + return disasm_output + +def print_disam(dso_fname: str, dso_start: int, start_addr: int, + stop_addr: int, objdump: str) -> None: + """Print disassembly for a given address range.""" + for line in read_disam(dso_fname, dso_start, start_addr, stop_addr, ob= jdump): + m =3D DISASM_FUNC_RE.search(line) + if m is None: + m =3D DISASM_RE.search(line) + if m is None: + continue + print(f"\t{line}") + +def print_sample(sample: perf.sample_event) -> None: + """Print sample details.""" + print(f"Sample =3D {{ cpu: {sample.sample_cpu:04d} addr: {sample.sampl= e_addr:016x} " + f"phys_addr: {sample.sample_phys_addr:016x} ip: {sample.sample_i= p:016x} " + f"pid: {sample.sample_pid} tid: {sample.sample_tid} period: {sam= ple.sample_period} " + f"time: {sample.sample_time} index: {SAMPLE_IDX}}}") + +def common_start_str(comm: str, sample: perf.sample_event) -> str: + """Return common start string for sample output.""" + sec =3D int(sample.sample_time / 1000000000) + ns =3D sample.sample_time % 1000000000 + cpu =3D sample.sample_cpu + pid =3D sample.sample_pid + tid =3D sample.sample_tid + return f"{comm:>16s} {pid:5u}/{tid:<5u} [{cpu:04d}] {sec:9d}.{ns:09d} = " + +def print_srccode(comm: str, sample: perf.sample_event, symbol: str, dso: = str) -> None: + """Print source code and symbols for a sample.""" + ip =3D sample.sample_ip + if symbol =3D=3D "[unknown]": + start_str =3D common_start_str(comm, sample) + f"{ip:x}".rjust(16)= .ljust(40) + else: + symoff =3D 0 + sym_start =3D sample.sym_start + if sym_start is not None: + symoff =3D ip - sym_start + offs =3D f"+{symoff:#x}" if symoff !=3D 0 else "" + start_str =3D common_start_str(comm, sample) + (symbol + offs).lju= st(40) + + global GLB_SOURCE_FILE_NAME, GLB_LINE_NUMBER, GLB_DSO + + source_file_name, line_number, source_line =3D sample.srccode() + if source_file_name: + if GLB_LINE_NUMBER =3D=3D line_number and GLB_SOURCE_FILE_NAME =3D= =3D source_file_name: + src_str =3D "" + else: + if len(source_file_name) > 40: + src_file =3D f"...{source_file_name[-37:]} " + else: + src_file =3D source_file_name.ljust(41) + + if source_line is None: + src_str =3D f"{src_file}{line_number:>4d} " + else: + src_str =3D f"{src_file}{line_number:>4d} {source_line}" + GLB_DSO =3D None + elif dso =3D=3D GLB_DSO: + src_str =3D "" + else: + src_str =3D dso + GLB_DSO =3D dso + + GLB_LINE_NUMBER =3D line_number + GLB_SOURCE_FILE_NAME =3D source_file_name + + print(start_str, src_str) + +class TraceDisasm: + """Class to handle trace disassembly.""" + def __init__(self, cli_options: argparse.Namespace): + self.options =3D cli_options + self.sample_idx =3D -1 + self.session: Optional[perf.session] =3D None + + def process_event(self, sample: perf.sample_event) -> None: + """Process a single perf event.""" + self.sample_idx +=3D 1 + global SAMPLE_IDX + SAMPLE_IDX =3D self.sample_idx + + if self.options.start_time and sample.sample_time < self.options.s= tart_time: + return + if self.options.stop_time and sample.sample_time > self.options.st= op_time: + sys.exit(0) + if self.options.start_sample and self.sample_idx < self.options.st= art_sample: + return + if self.options.stop_sample and self.sample_idx > self.options.sto= p_sample: + sys.exit(0) + + ev_name =3D str(sample.evsel) + if self.options.verbose: + print(f"Event type: {ev_name}") + print_sample(sample) + + dso =3D sample.dso or '[unknown]' + symbol =3D sample.symbol or '[unknown]' + dso_bid =3D sample.dso_bid or '[unknown]' + dso_start =3D sample.map_start + dso_end =3D sample.map_end + map_pgoff =3D sample.map_pgoff or 0 + + comm =3D "[unknown]" + try: + if self.session: + thread_info =3D self.session.find_thread(sample.sample_tid) + if thread_info: + comm =3D thread_info.comm() + except (TypeError, AttributeError): + pass + + cpu =3D sample.sample_cpu + addr =3D sample.sample_addr + + if CPU_DATA.get(str(cpu) + 'addr') is None: + CPU_DATA[str(cpu) + 'addr'] =3D addr + return + + if dso =3D=3D '[unknown]': + return + + if dso_start is None or dso_end is None: + print(f"Failed to find valid dso map for dso {dso}") + return + + if ev_name.startswith("instructions"): + print_srccode(comm, sample, symbol, dso) + return + + if not ev_name.startswith("branches"): + return + + self._process_branch(sample, comm, symbol, dso, dso_bid, dso_start= , dso_end, map_pgoff) + + def _process_branch(self, sample: perf.sample_event, comm: str, symbol= : str, dso: str, + dso_bid: str, dso_start: int, dso_end: int, map_pg= off: int) -> None: + """Helper to process branch events.""" + cpu =3D sample.sample_cpu + ip =3D sample.sample_ip + addr =3D sample.sample_addr + + start_addr =3D CPU_DATA[str(cpu) + 'addr'] + stop_addr =3D ip + 4 + + # Record for previous sample packet + CPU_DATA[str(cpu) + 'addr'] =3D addr + + # Filter out zero start_address. Optionally identify CS_ETM_TRACE_= ON packet + if start_addr =3D=3D 0: + if stop_addr =3D=3D 4 and self.options.verbose: + print(f"CPU{cpu}: CS_ETM_TRACE_ON packet is inserted") + return + + if start_addr < dso_start or start_addr > dso_end: + print(f"Start address {start_addr:#x} is out of range [ {dso_s= tart:#x} .. " + f"{dso_end:#x} ] for dso {dso}") + return + + if stop_addr < dso_start or stop_addr > dso_end: + print(f"Stop address {stop_addr:#x} is out of range [ {dso_sta= rt:#x} .. " + f"{dso_end:#x} ] for dso {dso}") + return + + if self.options.objdump is not None: + if dso =3D=3D "[kernel.kallsyms]" or dso_start =3D=3D 0x400000: + dso_vm_start =3D 0 + map_pgoff_local =3D 0 + else: + dso_vm_start =3D dso_start + map_pgoff_local =3D map_pgoff + + dso_fname =3D get_dso_file_path(dso, dso_bid, self.options.vml= inux) + if path.exists(dso_fname): + print_disam(dso_fname, dso_vm_start, start_addr + map_pgof= f_local, + stop_addr + map_pgoff_local, self.options.objd= ump) + else: + print(f"Failed to find dso {dso} for address range [ " + f"{start_addr + map_pgoff_local:#x} .. {stop_addr + = map_pgoff_local:#x} ]") + + print_srccode(comm, sample, symbol, dso) + + def run(self) -> None: + """Run the trace disassembly session.""" + input_file =3D self.options.input or "perf.data" + if not os.path.exists(input_file): + print(f"Error: {input_file} not found.", file=3Dsys.stderr) + sys.exit(1) + + print('ARM CoreSight Trace Data Assembler Dump') + try: + self.session =3D perf.session(perf.data(input_file), sample=3D= self.process_event) + except Exception as e: + print(f"Error opening session: {e}", file=3Dsys.stderr) + sys.exit(1) + + self.session.process_events() + print('End') + +if __name__ =3D=3D "__main__": + def int_arg(v: str) -> int: + """Helper for integer command line arguments.""" + val =3D int(v) + if val < 0: + raise argparse.ArgumentTypeError("Argument must be a positive = integer") + return val + + arg_parser =3D argparse.ArgumentParser(description=3D"ARM CoreSight Tr= ace Dump With Disassembler") + arg_parser.add_argument("-i", "--input", help=3D"input perf.data file") + arg_parser.add_argument("-k", "--vmlinux", + help=3D"Set path to vmlinux file. Omit to auto= detect") + arg_parser.add_argument("-d", "--objdump", nargs=3D"?", const=3Ddefaul= t_objdump(), + help=3D"Show disassembly. Can also be used to = change the objdump path") + arg_parser.add_argument("-v", "--verbose", action=3D"store_true", help= =3D"Enable debugging log") + arg_parser.add_argument("--start-time", type=3Dint_arg, + help=3D"Monotonic clock time of sample to star= t from.") + arg_parser.add_argument("--stop-time", type=3Dint_arg, + help=3D"Monotonic clock time of sample to stop= at.") + arg_parser.add_argument("--start-sample", type=3Dint_arg, + help=3D"Index of sample to start from.") + arg_parser.add_argument("--stop-sample", type=3Dint_arg, + help=3D"Index of sample to stop at.") + + parsed_options =3D arg_parser.parse_args() + if (parsed_options.start_time and parsed_options.stop_time and \ + parsed_options.start_time >=3D parsed_options.stop_time): + print("--start-time must less than --stop-time") + sys.exit(2) + if (parsed_options.start_sample and parsed_options.stop_sample and \ + parsed_options.start_sample >=3D parsed_options.stop_sample): + print("--start-sample must less than --stop-sample") + sys.exit(2) + + td =3D TraceDisasm(parsed_options) + td.run() diff --git a/tools/perf/tests/shell/test_arm_coresight_disasm.sh b/tools/pe= rf/tests/shell/test_arm_coresight_disasm.sh index 0dfb4fadf531..c15cd60e1c24 100755 --- a/tools/perf/tests/shell/test_arm_coresight_disasm.sh +++ b/tools/perf/tests/shell/test_arm_coresight_disasm.sh @@ -24,7 +24,7 @@ perfdata_dir=3D$(mktemp -d /tmp/__perf_test.perf.data.XXX= XX) perfdata=3D${perfdata_dir}/perf.data file=3D$(mktemp /tmp/temporary_file.XXXXX) # Relative path works whether it's installed or running from repo -script_path=3D$(dirname "$0")/../../scripts/python/arm-cs-trace-disasm.py +script_path=3D$(dirname "$0")/../../python/arm-cs-trace-disasm.py =20 cleanup_files() { @@ -45,8 +45,9 @@ branch_search=3D"\sbl${sep}b${sep}b.ne${sep}b.eq${sep}cbz= \s" if [ -e /proc/kcore ]; then echo "Testing kernel disassembly" perf record -o ${perfdata} -e cs_etm//k --kcore -- touch $file > /dev/nul= l 2>&1 - perf script -i ${perfdata} -s python:${script_path} -- \ - -d --stop-sample=3D30 2> /dev/null > ${file} + # shellcheck source=3Dlib/setup_python.sh + . "$(dirname "$0")"/lib/setup_python.sh + $PYTHON ${script_path} -i ${perfdata} -d --stop-sample=3D30 2> /dev/null = > ${file} grep -q -e ${branch_search} ${file} echo "Found kernel branches" else @@ -57,8 +58,9 @@ fi ## Test user ## echo "Testing userspace disassembly" perf record -o ${perfdata} -e cs_etm//u -- touch $file > /dev/null 2>&1 -perf script -i ${perfdata} -s python:${script_path} -- \ - -d --stop-sample=3D30 2> /dev/null > ${file} +# shellcheck source=3Dlib/setup_python.sh +. "$(dirname "$0")"/lib/setup_python.sh +$PYTHON ${script_path} -i ${perfdata} -d --stop-sample=3D30 2> /dev/null >= ${file} grep -q -e ${branch_search} ${file} echo "Found userspace branches" =20 --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 71B473A5E84 for ; Sat, 25 Apr 2026 22:51:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157484; cv=none; b=pq91EGWdxj+sbOp93Y5gjOU+iIvgyTf+WowWd2VWOaIc5KiFOELySmmVjpcAdGthhUrV/lPRoeC+n9N8ofhwSdS/rmi1+kDjKjQmNmDyvRoT3SuIbsybH1KMp5DNEXKWYhaP1Y1w8RJ3dz/7xVsaB3IyGrlpPSqU7XY2Pvx9yOY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157484; c=relaxed/simple; bh=hDYhFYuDoPCefdlf2OIwEe6ysT4MEKgdLebygFtbti8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=AWlG/QpSk21u+4gs+6yh1nXfGbgzR7oBt/tPodosTVc4fxuNrd6MSscMj31ArxWxtaFB2jMvDzWULtWWIDcb6o1FPtDlPXFeD2aRSXILYrbSeKV4uncctyecGg1qWJmA+zFqup5zFWv9iDnHBGmL3Gl3ceFrGLqb+7l3Yx2B/gI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=gk3odBLI; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="gk3odBLI" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-12c8de02a4dso15630583c88.1 for ; Sat, 25 Apr 2026 15:51:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157482; x=1777762282; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=ERRU3KdUiox4uEsWs/CeTXwwWIrylXGw9vYnzrPC1Tw=; b=gk3odBLIai876+7uvi4GZ1+uYYH0W/nsvyAegAnhitYy5j5dv890A4++rqT+ju75Pq BxknwYlUIEn75l6BOMzZEHm1KXFAttbDlQ238uxLtlFvxluXtj6in50r3r2+T3fMWF+F 9O8/ebxwg7uy92hDj9+mom5MBUFU58/gjn6KqjXpdskN2e/AqugO4zcIFN+GmElx9LtM IXec0Q9IaCuy4JbL/3qHRdJ0CmSnhYyxBrb9WTpXaSchXfdO2p1ZenpUkEqNIYcPzloF J+Jf2LcBksp4VpjptycXK1cqKCP6pEv+EXpMGtvu9J7pNs7mhjTCSvbQ0+GbWNwmG5+1 REkw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157482; x=1777762282; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=ERRU3KdUiox4uEsWs/CeTXwwWIrylXGw9vYnzrPC1Tw=; b=OskpkvMe+EEn7XQMoKKq7GAe8oHfmXD1u0k3dLAII1ZY8a4LX3NkFfQXHK2y1HLsbl V7tbLDoOQBbEwu+Xc9dex6BYUTOmWfgP1Vnr4jvcE024e0ll0jhomX7OGcftDEApFfzD ynshQf4/XEYIbj0MXun0moMCj/YvPHeLG+HhHm9k3EzqyrPEHXZahJnsV4E2aQ7pno7c +s3YbVpxZbVpR4bNElyK+HHtgRx1FZjfCSWeM/KF0+9NtdWRezhgHlc/mDLRi2CHlJoq AFv6aAp73Rsw5phzuPmuMyHwcFuuCXuYV04LJZvXJEXanqrv0McBpSwqmEgKYkaiuJr0 w3Xg== X-Forwarded-Encrypted: i=1; AFNElJ9FzkFqyzu/KgzOVNgQ3p9EqDJv9YI/IwzBlRJ3kkgH1vkVautJs5j3lhQMBj5PhpQRxc3oAkAByy3QIG0=@vger.kernel.org X-Gm-Message-State: AOJu0YzP3XCodxMQWJjyYQG+g9GJTfs0qlUZ9Tda3OjUKuWhIbhRRHU2 hDla1JLHOZlpFrB58KHV/tU2tgdRDUZg1CwS2NUNS8bTHB+f5cmlyR3emJ67HCtloJUvbY2n2xS 5+xbO+cP4Ew== X-Received: from dlbsj13.prod.google.com ([2002:a05:7022:f90d:b0:12d:b591:4bad]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:60f:b0:127:3915:76b2 with SMTP id a92af1059eb24-12c73f9ae51mr22028481c88.27.1777157481565; Sat, 25 Apr 2026 15:51:21 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:25 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-34-irogers@google.com> Subject: [PATCH v7 33/59] perf check-perf-trace: Port check-perf-trace to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a port of the check-perf-trace script that uses the perf python module directly. This approach is significantly faster than using perf script callbacks as it avoids creating intermediate dictionaries for all event fields. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. String Match Accuracy: Replaced the substring check for `irq:softirq_ent= ry` events with a robust exact string match. v3: 1. Safe Thread Resolution: Swapped out sample.sample_pid with sample.sample_tid and safeguarded the session process lookup with a try-except block. v4: 1. Git Fixup Cleanup: Squashed the lingering fixup commit from the previous session into its proper patch. --- tools/perf/python/check-perf-trace.py | 113 ++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100755 tools/perf/python/check-perf-trace.py diff --git a/tools/perf/python/check-perf-trace.py b/tools/perf/python/chec= k-perf-trace.py new file mode 100755 index 000000000000..7c1c7632a091 --- /dev/null +++ b/tools/perf/python/check-perf-trace.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +Basic test of Python scripting support for perf. +Ported from tools/perf/scripts/python/check-perf-trace.py +""" + +import argparse +import collections +import perf + +unhandled: collections.defaultdict[str, int] =3D collections.defaultdict(i= nt) +session =3D None + +softirq_vecs =3D { + 0: "HI_SOFTIRQ", + 1: "TIMER_SOFTIRQ", + 2: "NET_TX_SOFTIRQ", + 3: "NET_RX_SOFTIRQ", + 4: "BLOCK_SOFTIRQ", + 5: "IRQ_POLL_SOFTIRQ", + 6: "TASKLET_SOFTIRQ", + 7: "SCHED_SOFTIRQ", + 8: "HRTIMER_SOFTIRQ", + 9: "RCU_SOFTIRQ", +} + +def trace_begin() -> None: + """Called at the start of trace processing.""" + print("trace_begin") + +def trace_end() -> None: + """Called at the end of trace processing.""" + print_unhandled() + +def symbol_str(event_name: str, field_name: str, value: int) -> str: + """Resolves symbol values to strings.""" + if event_name =3D=3D "irq__softirq_entry" and field_name =3D=3D "vec": + return softirq_vecs.get(value, str(value)) + return str(value) + +def flag_str(event_name: str, field_name: str, value: int) -> str: + """Resolves flag values to strings.""" + if event_name =3D=3D "kmem__kmalloc" and field_name =3D=3D "gfp_flags": + return f"0x{value:x}" + return str(value) + +def print_header(event_name: str, sample: perf.sample_event) -> None: + """Prints common header for events.""" + secs =3D sample.sample_time // 1000000000 + nsecs =3D sample.sample_time % 1000000000 + comm =3D session.find_thread(sample.sample_pid).comm() if session else= "[unknown]" + print(f"{event_name:<20} {sample.sample_cpu:5} {secs:05}.{nsecs:09} " + f"{sample.sample_pid:8} {comm:<20} ", end=3D' ') + +def print_uncommon(sample: perf.sample_event) -> None: + """Prints uncommon fields for tracepoints.""" + # Fallback to 0 if field not found (e.g. on older kernels or if not tr= acepoint) + pc =3D getattr(sample, "common_preempt_count", 0) + flags =3D getattr(sample, "common_flags", 0) + lock_depth =3D getattr(sample, "common_lock_depth", 0) + + print(f"common_preempt_count=3D{pc}, common_flags=3D{flags}, " + f"common_lock_depth=3D{lock_depth}, ") + +def irq__softirq_entry(sample: perf.sample_event) -> None: + """Handles irq:softirq_entry events.""" + print_header("irq__softirq_entry", sample) + print_uncommon(sample) + print(f"vec=3D{symbol_str('irq__softirq_entry', 'vec', sample.vec)}") + +def kmem__kmalloc(sample: perf.sample_event) -> None: + """Handles kmem:kmalloc events.""" + print_header("kmem__kmalloc", sample) + print_uncommon(sample) + + print(f"call_site=3D{sample.call_site:d}, ptr=3D{sample.ptr:d}, " + f"bytes_req=3D{sample.bytes_req:d}, bytes_alloc=3D{sample.bytes_= alloc:d}, " + f"gfp_flags=3D{flag_str('kmem__kmalloc', 'gfp_flags', sample.gfp= _flags)}") + +def trace_unhandled(event_name: str) -> None: + """Tracks unhandled events.""" + unhandled[event_name] +=3D 1 + +def print_unhandled() -> None: + """Prints summary of unhandled events.""" + if not unhandled: + return + print("\nunhandled events:\n") + print(f"{'event':<40} {'count':>10}") + print("---------------------------------------- -----------") + for event_name, count in unhandled.items(): + print(f"{event_name:<40} {count:10}") + +def process_event(sample: perf.sample_event) -> None: + """Callback for processing events.""" + event_name =3D str(sample.evsel) + if event_name =3D=3D "evsel(irq:softirq_entry)": + irq__softirq_entry(sample) + elif "evsel(kmem:kmalloc)" in event_name: + kmem__kmalloc(sample) + else: + trace_unhandled(event_name) + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser() + ap.add_argument("-i", "--input", default=3D"perf.data", help=3D"Input = file name") + args =3D ap.parse_args() + + trace_begin() + session =3D perf.session(perf.data(args.input), sample=3Dprocess_event) + session.process_events() + trace_end() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AA5B43A63F9 for ; Sat, 25 Apr 2026 22:51:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157486; cv=none; b=IPspg0j598zc/1JTkkBY9rNyOyQhTB0U8gioL9KVVbaKGc3u4l9PpQ7M9tUSQ17iTOhnBUoaQKYHk6sLN3heW9GO0IXrxATzrMYxEYhYEihBVDDC2KOz8lRVIujjckyX4OUhv53L1anrGDibnlNZF3+RzbLjzgpWFRq6LgLLHdc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157486; c=relaxed/simple; bh=IJUCBgPg9wYDYawtxHW5XNHuYjUrl13nxcH5QuDBI54=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=tlFWwBVCqnoY7CilLwmMwzOlCpp5/JCO3/BbYjTqNiCLijFGQpeppHb9E8PscWbO48kg2sAPYdlcjLh5s5JvDlBEg7ATpt8pXHWXjVCZy1CXgh4Y38W/NSsrSu9jAuNY8ua2Kr3/YBSmvraKrBVtTpOZ6/GtE48J71+g017YnL4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=dT3kZ33J; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="dT3kZ33J" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2de07c12745so25489991eec.1 for ; Sat, 25 Apr 2026 15:51:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157484; x=1777762284; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=yfBwBqR2Z+Zk1dnV7MbJ1NLKT+aSgQqUxonuLHmwfTg=; b=dT3kZ33JdfTBZ7ANWiwDC78HI1btq5Z8e0PVtej/OQ/f1Iw6wUaqUYuadI/WKOmSDx /6CGkUg+Rytq7JPuIjhA1Y8ZOEJpNFkVJ3jNiWA5RsW6tA4B3KQRfmQlZiApfcLKdfK2 iE4QilDHGOFAdgU7HzvpgrZ582jc4irsVf1/do9aPPaYuAELhfenb7+jKFbkWpNkdn8q 6EUNKrkwKbMpL7xeOw1cPp8RwnzkHrRWyF/sEupF8oK8RPKN4c3u6Z/7I58HC04Jt695 hoGsYxG0cM8/7D6gIMAOFWMYjGKHunpBfLpeTuBU4iEDJhGjRnqJDUJ2Zi8qCeILusnb boUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157484; x=1777762284; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=yfBwBqR2Z+Zk1dnV7MbJ1NLKT+aSgQqUxonuLHmwfTg=; b=CpIgI1KZYBW+PZ1VYGya1ci72RGmq7RDBQq0E+RdYcWLWsZkoskMhWXSh406eGmyxI Fmk/mTkNH9C+Zxj5HQxXmunshoQXVDtpf9HSQYOQOrDfg5nn6hJ07MUWQekQKQjOeGMx sQCJ8MIMUE7vI84aNFdspGq4eW1kA8zOcx6ynIoEGFz8HkMoQtnUHPnBVTv/hvwOWawp YK8gr8/KkiL4kV3rG1j5ZypSCHhkteHunCOzA2uTp10S87NO7CLsNnu0k0yrjGTPfpzZ pSxW1LeKlZ+3zBhd8SSxOD+SHXq8s1Z02khDR0NWk/CmFS5VbgsIAJVwHxOOWAx4UVej 5EIg== X-Forwarded-Encrypted: i=1; AFNElJ/2nFC2AVDNAaGofbF8W1jPvp5xcd0SrFAtJb6PdcLLqPANdwcaEgL/BEFdJ9MjLOYOGfh1Sr1uKLYQVXA=@vger.kernel.org X-Gm-Message-State: AOJu0YwtZMJcKIAHgOmj3fNfhXzIiEfs7GqlM8vQb9owBrOxxJGRdbN3 jR+kOpS0ecNSLcVKBYAlytXi1aFgs1GLbOyuaCwWIbvPsdspWpp+3TUQGYMaLSB5Y9y1MuYLQft 4G6n6YCd14g== X-Received: from dybuz22.prod.google.com ([2002:a05:7301:5096:b0:2df:70c6:196b]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:ca4:b0:2d9:6373:ad10 with SMTP id 5a478bee46e88-2e473cb4f9bmr17775893eec.7.1777157483617; Sat, 25 Apr 2026 15:51:23 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:26 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-35-irogers@google.com> Subject: [PATCH v7 34/59] perf compaction-times: Port compaction-times to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a port of the compaction-times script that uses the perf python module directly. This approach is significantly faster than using perf script callbacks as it avoids creating intermediate dictionaries for all event fields. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: Fixed Closure Call: Changed cls.fobj.filter(pid, comm) to cls.fobj(pid, comm) . Since fobj is a function (closure) and not a class instance, calling .filter() on it would raise an AttributeError . --- tools/perf/python/compaction-times.py | 326 ++++++++++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100755 tools/perf/python/compaction-times.py diff --git a/tools/perf/python/compaction-times.py b/tools/perf/python/comp= action-times.py new file mode 100755 index 000000000000..7f17c251ded7 --- /dev/null +++ b/tools/perf/python/compaction-times.py @@ -0,0 +1,326 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +Report time spent in memory compaction. + +Memory compaction is a feature in the Linux kernel that defragments memory +by moving used pages to create larger contiguous blocks of free memory. Th= is +is particularly useful for allocating huge pages. + +This script processes trace events related to memory compaction and report= s: +- Total time spent in compaction (stall time). +- Statistics for page migration (moved vs. failed). +- Statistics for the free scanner (scanned vs. isolated pages). +- Statistics for the migration scanner (scanned vs. isolated pages). + +Definitions: +- **Compaction**: Defragmenting memory by moving allocated pages. +- **Migration**: Moving pages from their current location to free pages fo= und by the free scanner. +- **Free Scanner**: Scans memory (typically from the end of a zone) to fin= d free pages. +- **Migration Scanner**: Scans memory (typically from the beginning of a z= one) + to find pages to move. +- **Isolated Pages**: Pages that have been temporarily removed from the bu= ddy + system for migration or as migration targets. + +Ported from tools/perf/scripts/python/compaction-times.py to the modern pe= rf Python module. +""" + +import argparse +import enum +import re +import sys +from typing import Callable, Dict, List, Optional, Any +import perf + +class Popt(enum.IntEnum): + """Process display options.""" + DISP_DFL =3D 0 + DISP_PROC =3D 1 + DISP_PROC_VERBOSE =3D 2 + +class Topt(enum.IntFlag): + """Trace display options.""" + DISP_TIME =3D 0 + DISP_MIG =3D 1 + DISP_ISOLFREE =3D 2 + DISP_ISOLMIG =3D 4 + DISP_ALL =3D DISP_MIG | DISP_ISOLFREE | DISP_ISOLMIG + +# Globals to satisfy pylint when accessed in functions before assignment i= n main. +OPT_NS =3D True +opt_disp =3D Topt.DISP_ALL +opt_proc =3D Popt.DISP_DFL +session =3D None + +def get_comm_filter(regex: re.Pattern) -> Callable[[int, str], bool]: + """Returns a filter function based on command regex.""" + def filter_func(_pid: int, comm: str) -> bool: + regex_match =3D regex.search(comm) + return regex_match is None or regex_match.group() =3D=3D "" + return filter_func + +def get_pid_filter(low_str: str, high_str: str) -> Callable[[int, str], bo= ol]: + """Returns a filter function based on PID range.""" + low =3D 0 if low_str =3D=3D "" else int(low_str) + high =3D 0 if high_str =3D=3D "" else int(high_str) + + def filter_func(pid: int, _comm: str) -> bool: + return not (pid >=3D low and (high =3D=3D 0 or pid <=3D high)) + return filter_func + +def ns_to_time(ns: int) -> str: + """Format nanoseconds to string based on options.""" + return f"{ns}ns" if OPT_NS else f"{round(ns, -3) // 1000}us" + +class Pair: + """Represents a pair of related counters (e.g., scanned vs isolated, m= oved vs failed).""" + def __init__(self, aval: int, bval: int, + alabel: Optional[str] =3D None, blabel: Optional[str] =3D= None): + self.alabel =3D alabel + self.blabel =3D blabel + self.aval =3D aval + self.bval =3D bval + + def __add__(self, rhs: 'Pair') -> 'Pair': + self.aval +=3D rhs.aval + self.bval +=3D rhs.bval + return self + + def __str__(self) -> str: + return f"{self.alabel}=3D{self.aval} {self.blabel}=3D{self.bval}" + +class Cnode: + """Holds statistics for a single compaction event or an aggregated set= of events.""" + def __init__(self, ns: int): + self.ns =3D ns + self.migrated =3D Pair(0, 0, "moved", "failed") + self.fscan =3D Pair(0, 0, "scanned", "isolated") + self.mscan =3D Pair(0, 0, "scanned", "isolated") + + def __add__(self, rhs: 'Cnode') -> 'Cnode': + self.ns +=3D rhs.ns + self.migrated +=3D rhs.migrated + self.fscan +=3D rhs.fscan + self.mscan +=3D rhs.mscan + return self + + def __str__(self) -> str: + prev =3D False + s =3D f"{ns_to_time(self.ns)} " + if opt_disp & Topt.DISP_MIG: + s +=3D f"migration: {self.migrated}" + prev =3D True + if opt_disp & Topt.DISP_ISOLFREE: + s +=3D f"{' ' if prev else ''}free_scanner: {self.fscan}" + prev =3D True + if opt_disp & Topt.DISP_ISOLMIG: + s +=3D f"{' ' if prev else ''}migration_scanner: {self.mscan}" + return s + + def complete(self, secs: int, nsecs: int) -> None: + """Complete the node with duration.""" + self.ns =3D (secs * 1000000000 + nsecs) - self.ns + + def increment(self, migrated: Optional[Pair], fscan: Optional[Pair], + mscan: Optional[Pair]) -> None: + """Increment statistics.""" + if migrated is not None: + self.migrated +=3D migrated + if fscan is not None: + self.fscan +=3D fscan + if mscan is not None: + self.mscan +=3D mscan + +class Chead: + """Aggregates compaction statistics per process (PID) and maintains to= tal statistics.""" + heads: Dict[int, 'Chead'] =3D {} + val =3D Cnode(0) + fobj: Optional[Any] =3D None + + @classmethod + def add_filter(cls, fobj: Any) -> None: + """Add a filter object.""" + cls.fobj =3D fobj + + @classmethod + def create_pending(cls, pid: int, comm: str, start_secs: int, start_ns= ecs: int) -> None: + """Create a pending node for a process.""" + filtered =3D False + try: + head =3D cls.heads[pid] + filtered =3D head.is_filtered() + except KeyError: + if cls.fobj is not None: + filtered =3D cls.fobj(pid, comm) + head =3D cls.heads[pid] =3D Chead(comm, pid, filtered) + + if not filtered: + head.mark_pending(start_secs, start_nsecs) + + @classmethod + def increment_pending(cls, pid: int, migrated: Optional[Pair], + fscan: Optional[Pair], mscan: Optional[Pair]) ->= None: + """Increment pending stats for a process.""" + if pid not in cls.heads: + return + head =3D cls.heads[pid] + if not head.is_filtered(): + if head.is_pending(): + head.do_increment(migrated, fscan, mscan) + else: + sys.stderr.write(f"missing start compaction event for pid = {pid}\n") + + @classmethod + def complete_pending(cls, pid: int, secs: int, nsecs: int) -> None: + """Complete pending stats for a process.""" + if pid not in cls.heads: + return + head =3D cls.heads[pid] + if not head.is_filtered(): + if head.is_pending(): + head.make_complete(secs, nsecs) + else: + sys.stderr.write(f"missing start compaction event for pid = {pid}\n") + + @classmethod + def gen(cls): + """Generate heads for display.""" + if opt_proc !=3D Popt.DISP_DFL: + yield from cls.heads.values() + + @classmethod + def get_total(cls) -> Cnode: + """Get total statistics.""" + return cls.val + + def __init__(self, comm: str, pid: int, filtered: bool): + self.comm =3D comm + self.pid =3D pid + self.val =3D Cnode(0) + self.pending: Optional[Cnode] =3D None + self.filtered =3D filtered + self.list: List[Cnode] =3D [] + + def mark_pending(self, secs: int, nsecs: int) -> None: + """Mark node as pending.""" + self.pending =3D Cnode(secs * 1000000000 + nsecs) + + def do_increment(self, migrated: Optional[Pair], fscan: Optional[Pair], + mscan: Optional[Pair]) -> None: + """Increment pending stats.""" + if self.pending is not None: + self.pending.increment(migrated, fscan, mscan) + + def make_complete(self, secs: int, nsecs: int) -> None: + """Make pending stats complete.""" + if self.pending is not None: + self.pending.complete(secs, nsecs) + Chead.val +=3D self.pending + + if opt_proc !=3D Popt.DISP_DFL: + self.val +=3D self.pending + + if opt_proc =3D=3D Popt.DISP_PROC_VERBOSE: + self.list.append(self.pending) + self.pending =3D None + + def enumerate(self) -> None: + """Enumerate verbose stats.""" + if opt_proc =3D=3D Popt.DISP_PROC_VERBOSE and not self.is_filtered= (): + for i, pelem in enumerate(self.list): + sys.stdout.write(f"{self.pid}[{self.comm}].{i+1}: {pelem}\= n") + + def is_pending(self) -> bool: + """Check if node is pending.""" + return self.pending is not None + + def is_filtered(self) -> bool: + """Check if node is filtered.""" + return self.filtered + + def display(self) -> None: + """Display stats.""" + if not self.is_filtered(): + sys.stdout.write(f"{self.pid}[{self.comm}]: {self.val}\n") + +def trace_end() -> None: + """Called at the end of trace processing.""" + sys.stdout.write(f"total: {Chead.get_total()}\n") + for i in Chead.gen(): + i.display() + i.enumerate() + +def process_event(sample: perf.sample_event) -> None: + """Callback for processing events.""" + event_name =3D str(sample.evsel) + pid =3D sample.sample_pid + comm =3D session.find_thread(pid).comm() if session else "[unknown]" + secs =3D sample.sample_time // 1000000000 + nsecs =3D sample.sample_time % 1000000000 + + if "evsel(compaction:mm_compaction_begin)" in event_name: + Chead.create_pending(pid, comm, secs, nsecs) + elif "evsel(compaction:mm_compaction_end)" in event_name: + Chead.complete_pending(pid, secs, nsecs) + elif "evsel(compaction:mm_compaction_migratepages)" in event_name: + Chead.increment_pending(pid, Pair(sample.nr_migrated, sample.nr_fa= iled), None, None) + elif "evsel(compaction:mm_compaction_isolate_freepages)" in event_name: + Chead.increment_pending(pid, None, Pair(sample.nr_scanned, sample.= nr_taken), None) + elif "evsel(compaction:mm_compaction_isolate_migratepages)" in event_n= ame: + Chead.increment_pending(pid, None, None, Pair(sample.nr_scanned, s= ample.nr_taken)) + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser(description=3D"Report time spent in com= paction") + ap.add_argument("-p", action=3D"store_true", help=3D"display by proces= s") + ap.add_argument("-pv", action=3D"store_true", help=3D"display by proce= ss (verbose)") + ap.add_argument("-u", action=3D"store_true", help=3D"display results i= n microseconds") + ap.add_argument("-t", action=3D"store_true", help=3D"display stall tim= es only") + ap.add_argument("-m", action=3D"store_true", help=3D"display stats for= migration") + ap.add_argument("-fs", action=3D"store_true", help=3D"display stats fo= r free scanner") + ap.add_argument("-ms", action=3D"store_true", help=3D"display stats fo= r migration scanner") + ap.add_argument("filter", nargs=3D"?", help=3D"pid|pid-range|comm-rege= x") + ap.add_argument("-i", "--input", default=3D"perf.data", help=3D"Input = file name") + args =3D ap.parse_args() + + opt_proc =3D Popt.DISP_DFL + if args.pv: + opt_proc =3D Popt.DISP_PROC_VERBOSE + elif args.p: + opt_proc =3D Popt.DISP_PROC + + OPT_NS =3D not args.u + + opt_disp =3D Topt.DISP_ALL + if args.t or args.m or args.fs or args.ms: + opt_disp =3D Topt(0) + if args.t: + opt_disp |=3D Topt.DISP_TIME + if args.m: + opt_disp |=3D Topt.DISP_MIG + if args.fs: + opt_disp |=3D Topt.DISP_ISOLFREE + if args.ms: + opt_disp |=3D Topt.DISP_ISOLMIG + + if args.filter: + PID_PATTERN =3D r"^(\d*)-(\d*)$|^(\d*)$" + pid_re =3D re.compile(PID_PATTERN) + match =3D pid_re.search(args.filter) + filter_obj: Any =3D None + if match is not None and match.group() !=3D "": + if match.group(3) is not None: + filter_obj =3D get_pid_filter(match.group(3), match.group(= 3)) + else: + filter_obj =3D get_pid_filter(match.group(1), match.group(= 2)) + else: + try: + comm_re =3D re.compile(args.filter) + except re.error: + sys.stderr.write(f"invalid regex '{args.filter}'\n") + sys.exit(1) + filter_obj =3D get_comm_filter(comm_re) + Chead.add_filter(filter_obj) + + session =3D perf.session(perf.data(args.input), sample=3Dprocess_event) + session.process_events() + trace_end() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A6BEF3A6B84 for ; Sat, 25 Apr 2026 22:51:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157488; cv=none; b=oZJ29OACUbdoLFPnCZryQFZTJBU2wwVpChFkEQCxlg1afMiPuTt9TrUCtqeH0gVGMi173j5k6EKEG5J+qUgJisBcQGRHFk77k+Ka35fsh7nAtGYmi2BERpjjH9ixk/FQw8zTI41fWDFrZ/730p2vcKGun27zfKIUpUE4os+TpgQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157488; c=relaxed/simple; bh=eB9dzgD4yVRaLfiR1HqWvcMAHdYJ+qeRP6Hk8Skz4ek=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=RUupi8GOAVRV9PTkDCK5yuuAbwap3+nYk1GJ4GjU2SuJ3qfqM+6Vw3wRYeVx+FE11zJplNPVDfQ2tmortXdY++rRqNH2CN4KDnxhYwkBtJ3A3D+PdFzEegMgO1DCx5IDbmc3n7sZHyfMGc4b7tcW15bEs9EF+rYJuQWxBOIe/4E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=IGHiW8f4; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="IGHiW8f4" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2eaed3d96d7so860726eec.0 for ; Sat, 25 Apr 2026 15:51:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157486; x=1777762286; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=cF1Joyi8qfkVdlOn+yMtqrXIVsPBBbn9slQYje9uqkM=; b=IGHiW8f4u9g/46M5FU4cD2vYVhgFBzMMc/gtpZuoFyomceJ+Wi3b6zVLKng4y1nPDG cMCMKC9kI5EeujdHgK3PSVcF+G+52sHpfQLihG8RaZPjHBZEWanUkeFBVfL99ECmacic f9sH2gyddn13ZWajNNFHlGYKocl1A1NovduknWHeIjjb1rq4Enqu4tVm8hyQGrfc0v8Y BU3Vg7O+fjPqwgoGq+iGdoVy7yPy7nbMpC0DEfq6LoRsWAZ9vzmP2CxShrH5M+fFwfJS 8SofRltGuhSxQOdLQdPnpak9Bti4GvJUX75TjKEbueaNFxQzfSujIDlK8g8cSdRVkY15 VeSg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157486; x=1777762286; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=cF1Joyi8qfkVdlOn+yMtqrXIVsPBBbn9slQYje9uqkM=; b=Xoup4Hkx0E7LbH+YiEIw2SrXx321Znn4X+HFVhSUbfIOicN1xG3yQPtAAVtqX3mbRW JA49B5Cz4HV1nRK/p5j7SXuKo0Mn+2Gu30qKDZcvZWpIItOlc27U3R5pVi0NLu6uh4Y2 MsTgvwszT1j3ht4IEnfP7QdCdBjgK4IMxOT0BDpbl0CeguZtK3VH66VWEXlHviHuDt3p sO9PhEq7NAr6nyRH12Te2kKNxwgAMn6+75ClzAPd+t9inOYvLuOm19BhyQ9QwSsbaLzc 2IN8rEnuElys45wYFN5rWH4l0OtQaaxW9hhNxXXVP/tHpq5r+EVWtp+YrpMHpRLd0mS2 cq7g== X-Forwarded-Encrypted: i=1; AFNElJ9GiaTeNmUDFn6dzv5Fn+boO8ZaP5eFKtC7Q9fpO43zRTQ/2vWYVeeK4PTQVZ4DwRD0Wp88pj8C9zAApM0=@vger.kernel.org X-Gm-Message-State: AOJu0YyoKextUV2xFmStr96utV8PW2qLOeO9fMyOWDmVaaUMKM9XA9t7 D2K3+cO30vaXw4BsrnxL68rS+/IrW+dsPg6pCP5RM3m5qw6QC3MrSTVtZqqdPED/XY8LnhTcXv3 C0p9a6gpgLw== X-Received: from dlbvv17.prod.google.com ([2002:a05:7022:5f11:b0:12a:c3c1:4ace]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:ec07:b0:128:cea1:7e3b with SMTP id a92af1059eb24-12c73f9b8c7mr16951393c88.23.1777157485704; Sat, 25 Apr 2026 15:51:25 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:27 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-36-irogers@google.com> Subject: [PATCH v7 35/59] perf event_analyzing_sample: Port event_analyzing_sample to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a port of the event_analyzing_sample script that uses the perf python module directly. This approach is significantly faster than using perf script callbacks as it avoids creating intermediate dictionaries for all event fields. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Dynamic Database Path: Moved DB_PATH to a command-line argument ( -d / --database ) that defaults to "perf.db" . 2. Security: Avoided using /dev/shm by default to prevent symlink attacks, while retaining the performance suggestion in the help text. 3. Corrected Closure Call: Fixed the bug where it was trying to call .filter() on a closure. v6: - Fixed performance issue by removing autocommit mode in SQLite and batching commits. --- tools/perf/python/event_analyzing_sample.py | 297 ++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100755 tools/perf/python/event_analyzing_sample.py diff --git a/tools/perf/python/event_analyzing_sample.py b/tools/perf/pytho= n/event_analyzing_sample.py new file mode 100755 index 000000000000..2132db7f0e56 --- /dev/null +++ b/tools/perf/python/event_analyzing_sample.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +General event handler in Python, using SQLite to analyze events. + +The 2 database related functions in this script just show how to gather +the basic information, and users can modify and write their own functions +according to their specific requirement. + +The first function "show_general_events" just does a basic grouping for all +generic events with the help of sqlite, and the 2nd one "show_pebs_ll" is +for a x86 HW PMU event: PEBS with load latency data. + +Ported from tools/perf/scripts/python/event_analyzing_sample.py +""" + +import argparse +import math +import sqlite3 +import struct +from typing import Any +import perf + +# Event types, user could add more here +EVTYPE_GENERIC =3D 0 +EVTYPE_PEBS =3D 1 # Basic PEBS event +EVTYPE_PEBS_LL =3D 2 # PEBS event with load latency info +EVTYPE_IBS =3D 3 + +# +# Currently we don't have good way to tell the event type, but by +# the size of raw buffer, raw PEBS event with load latency data's +# size is 176 bytes, while the pure PEBS event's size is 144 bytes. +# +def create_event(name, comm, dso, symbol, raw_buf): + """Create an event object based on raw buffer size.""" + if len(raw_buf) =3D=3D 144: + event =3D PebsEvent(name, comm, dso, symbol, raw_buf) + elif len(raw_buf) =3D=3D 176: + event =3D PebsNHM(name, comm, dso, symbol, raw_buf) + else: + event =3D PerfEvent(name, comm, dso, symbol, raw_buf) + + return event + +class PerfEvent: + """Base class for all perf event samples.""" + event_num =3D 0 + def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=3DEVTYPE_= GENERIC): + self.name =3D name + self.comm =3D comm + self.dso =3D dso + self.symbol =3D symbol + self.raw_buf =3D raw_buf + self.ev_type =3D ev_type + PerfEvent.event_num +=3D 1 + + def show(self): + """Display PMU event info.""" + print(f"PMU event: name=3D{self.name:12s}, symbol=3D{self.symbol:2= 4s}, " + f"comm=3D{self.comm:8s}, dso=3D{self.dso:12s}") + +# +# Basic Intel PEBS (Precise Event-based Sampling) event, whose raw buffer +# contains the context info when that event happened: the EFLAGS and +# linear IP info, as well as all the registers. +# +class PebsEvent(PerfEvent): + """Intel PEBS event.""" + pebs_num =3D 0 + def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=3DEVTYPE_= PEBS): + tmp_buf =3D raw_buf[0:80] + flags, ip, ax, bx, cx, dx, si, di, bp, sp =3D struct.unpack('QQQQQ= QQQQQ', tmp_buf) + self.flags =3D flags + self.ip =3D ip + self.ax =3D ax + self.bx =3D bx + self.cx =3D cx + self.dx =3D dx + self.si =3D si + self.di =3D di + self.bp =3D bp + self.sp =3D sp + + super().__init__(name, comm, dso, symbol, raw_buf, ev_type) + PebsEvent.pebs_num +=3D 1 + del tmp_buf + +# +# Intel Nehalem and Westmere support PEBS plus Load Latency info which lie +# in the four 64 bit words write after the PEBS data: +# Status: records the IA32_PERF_GLOBAL_STATUS register value +# DLA: Data Linear Address (EIP) +# DSE: Data Source Encoding, where the latency happens, hit or mi= ss +# in L1/L2/L3 or IO operations +# LAT: the actual latency in cycles +# +class PebsNHM(PebsEvent): + """Intel Nehalem/Westmere PEBS event with load latency.""" + pebs_nhm_num =3D 0 + def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=3DEVTYPE_= PEBS_LL): + tmp_buf =3D raw_buf[144:176] + status, dla, dse, lat =3D struct.unpack('QQQQ', tmp_buf) + self.status =3D status + self.dla =3D dla + self.dse =3D dse + self.lat =3D lat + + super().__init__(name, comm, dso, symbol, raw_buf, ev_type) + PebsNHM.pebs_nhm_num +=3D 1 + del tmp_buf + +session: Any =3D None + +con =3D None + +def trace_begin(db_path: str) -> None: + """Initialize database tables.""" + print("In trace_begin:\n") + global con + con =3D sqlite3.connect(db_path) + assert con is not None + + # Will create several tables at the start, pebs_ll is for PEBS data wi= th + # load latency info, while gen_events is for general event. + con.execute(""" + create table if not exists gen_events ( + name text, + symbol text, + comm text, + dso text + );""") + con.execute(""" + create table if not exists pebs_ll ( + name text, + symbol text, + comm text, + dso text, + flags integer, + ip integer, + status integer, + dse integer, + dla integer, + lat integer + );""") + +def insert_db(event: Any) -> None: + """Insert event into database.""" + assert con is not None + if event.ev_type =3D=3D EVTYPE_GENERIC: + con.execute("insert into gen_events values(?, ?, ?, ?)", + (event.name, event.symbol, event.comm, event.dso)) + elif event.ev_type =3D=3D EVTYPE_PEBS_LL: + event.ip &=3D 0x7fffffffffffffff + event.dla &=3D 0x7fffffffffffffff + con.execute("insert into pebs_ll values (?, ?, ?, ?, ?, ?, ?, ?, ?= , ?)", + (event.name, event.symbol, event.comm, event.dso, even= t.flags, + event.ip, event.status, event.dse, event.dla, event.l= at)) + +def process_event(sample: perf.sample_event) -> None: + """Callback for processing events.""" + # Create and insert event object to a database so that user could + # do more analysis with simple database commands. + + # Resolve comm, symbol, dso + comm =3D "Unknown_comm" + try: + if session is not None: + # FIXME: session.find_thread() only takes one argument and use= s it as both + # PID and TID in C. This means it only resolves main threads c= orrectly. + # Sub-threads will get the main thread's comm. + proc =3D session.find_thread(sample.sample_pid) + if proc: + comm =3D proc.comm() + except TypeError: + pass + + # Symbol and dso info are not always resolved + dso =3D sample.dso if hasattr(sample, 'dso') and sample.dso else "Unkn= own_dso" + symbol =3D sample.symbol if hasattr(sample, 'symbol') and sample.symbo= l else "Unknown_symbol" + name =3D str(sample.evsel) + if name.startswith("evsel("): + name =3D name[6:-1] + + # Create the event object and insert it to the right table in database + try: + event =3D create_event(name, comm, dso, symbol, sample.raw_buf) + insert_db(event) + except (sqlite3.Error, ValueError, TypeError) as e: + print(f"Error creating/inserting event: {e}") + +def num2sym(num: int) -> str: + """Convert number to a histogram symbol (log2).""" + # As the event number may be very big, so we can't use linear way + # to show the histogram in real number, but use a log2 algorithm. + if num <=3D 0: + return "" + snum =3D '#' * (int(math.log(num, 2)) + 1) + return snum + +def show_general_events() -> None: + """Display statistics for general events.""" + assert con is not None + count =3D con.execute("select count(*) from gen_events") + for t in count: + print(f"There is {t[0]} records in gen_events table") + if t[0] =3D=3D 0: + return + + print("Statistics about the general events grouped by thread/symbol/ds= o: \n") + + # Group by thread + commq =3D con.execute(""" + select comm, count(comm) from gen_events + group by comm order by -count(comm) + """) + print(f"\n{ 'comm':>16} {'number':>8} {'histogram':>16}\n{'=3D'*42}") + for row in commq: + print(f"{row[0]:>16} {row[1]:>8} {num2sym(row[1])}") + + # Group by symbol + print(f"\n{'symbol':>32} {'number':>8} {'histogram':>16}\n{'=3D'*58}") + symbolq =3D con.execute(""" + select symbol, count(symbol) from gen_events + group by symbol order by -count(symbol) + """) + for row in symbolq: + print(f"{row[0]:>32} {row[1]:>8} {num2sym(row[1])}") + + # Group by dso + print(f"\n{'dso':>40} {'number':>8} {'histogram':>16}\n{'=3D'*74}") + dsoq =3D con.execute("select dso, count(dso) from gen_events group by = dso order by -count(dso)") + for row in dsoq: + print(f"{row[0]:>40} {row[1]:>8} {num2sym(row[1])}") + +def show_pebs_ll() -> None: + """Display statistics for PEBS load latency events.""" + assert con is not None + # This function just shows the basic info, and we could do more with t= he + # data in the tables, like checking the function parameters when some + # big latency events happen. + count =3D con.execute("select count(*) from pebs_ll") + for t in count: + print(f"There is {t[0]} records in pebs_ll table") + if t[0] =3D=3D 0: + return + + print("Statistics about the PEBS Load Latency events grouped by thread= /symbol/dse/latency: \n") + + # Group by thread + commq =3D con.execute("select comm, count(comm) from pebs_ll group by = comm order by -count(comm)") + print(f"\n{'comm':>16} {'number':>8} {'histogram':>16}\n{'=3D'*42}") + for row in commq: + print(f"{row[0]:>16} {row[1]:>8} {num2sym(row[1])}") + + # Group by symbol + print(f"\n{'symbol':>32} {'number':>8} {'histogram':>16}\n{'=3D'*58}") + symbolq =3D con.execute(""" + select symbol, count(symbol) from pebs_ll + group by symbol order by -count(symbol) + """) + for row in symbolq: + print(f"{row[0]:>32} {row[1]:>8} {num2sym(row[1])}") + + # Group by dse + dseq =3D con.execute("select dse, count(dse) from pebs_ll group by dse= order by -count(dse)") + print(f"\n{'dse':>32} {'number':>8} {'histogram':>16}\n{'=3D'*58}") + for row in dseq: + print(f"{row[0]:>32} {row[1]:>8} {num2sym(row[1])}") + + # Group by latency + latq =3D con.execute("select lat, count(lat) from pebs_ll group by lat= order by lat") + print(f"\n{'latency':>32} {'number':>8} {'histogram':>16}\n{'=3D'*58}") + for row in latq: + print(f"{str(row[0]):>32} {row[1]:>8} {num2sym(row[1])}") + +def trace_end() -> None: + """Called at the end of trace processing.""" + print("In trace_end:\n") + if con: + con.commit() + show_general_events() + show_pebs_ll() + if con: + con.close() + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser(description=3D"Analyze events with SQLi= te") + ap.add_argument("-i", "--input", default=3D"perf.data", help=3D"Input = file name") + ap.add_argument("-d", "--database", default=3D"perf.db", + help=3D"Database file name (tip: use /dev/shm/perf.db = for speedup)") + args =3D ap.parse_args() + + trace_begin(args.database) + session =3D perf.session(perf.data(args.input), sample=3Dprocess_event) + session.process_events() + trace_end() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3F0E03A75B7 for ; Sat, 25 Apr 2026 22:51:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157491; cv=none; b=tsgvwSnasX44w2XkL9vFfgcW/66hJ/MnnqzKjX3u+7fyPhgfmF2ohYAEiP6IDwDFK1NiDOv8Auq0klPNIlEbziY5jzz3bT3amx/Myl3jqVpSRLIbaHIQefK5ANrXzEqjVZfN4lds0OvGdQPmKXWHo+5ykyE7rIflVyqVXJsg/yk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157491; c=relaxed/simple; bh=Kxda9I+GvLoC1sIZ48qz+oZCWo4LY6Bc6jAdB7bnXUY=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=SVd3Upvpm1ZIpR0umMe24vUQtjbbxMBXxJkMqi/9C3U2SjNsrSaA5W5UOTLf3nLKrut7HkpmyPYv95VE8mOI+o4iAzqZYZudHoqwkv8x1/iIt5FXB0pJOzrBYI210hQrW53IWx5M4p5ZeSqygBTb32nKXIDmoEbQk62pE42xAIk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Dp8uwGvb; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Dp8uwGvb" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2dd6fb4c867so15665458eec.0 for ; Sat, 25 Apr 2026 15:51:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157488; x=1777762288; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=N4Nf17+iE4TpFXICTEQCjer2ymy7/ZPQXwIyAydjrk8=; b=Dp8uwGvb+IpwocMvUb+IlKgB+X3iAJTUZWEr9HeSMvpG80D6Fd31AG7QJZ6jjK+DE3 i74U720f7vEsWBdibssjCewRFHz8qRpMsvOMhUD3MsF0ld5/NndmZn5eGsRhMyeUXtmx +EZ5OTrEtytG1SZa27rCCd9R31Yd86+CS0ccOfikIDbosIql8k5y+vxw4cua+P22oAjV Vqw0LurhRQrFxN0rLBJFjyCQcgIU0T/7zUkJJBkLhAA5MpX6bzvyuqBTfQq2Ldj/IJI3 a5euUmvSSkJ2yXYvKgFRMzUWBl70ovnCkRXHeRzeG4qt0ORd8l1xa9oPGr3XJn7tDKeq 2RKw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157488; x=1777762288; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=N4Nf17+iE4TpFXICTEQCjer2ymy7/ZPQXwIyAydjrk8=; b=tKg47mr78wxp0CaZPQNYlfk2whwJTrzQ7yJ041dYeWwNmyaUmku+g7nBtqHeAFO+Hv 7LZ1Bxm63iupKcuKmkz4M/9fZuKYUKedVqQpG256pYCehkLNWsPtt34XIPJrKc9p+V4k 0PMvUJx0+E/0UMjyHntIFDGtbeTnMAab3I6UUWOcmQmUTtJaohgC0xGk+yonyuctRsRU TG+Cp+CM6XGpBs368zX1equBg66BB1FkWJNss0e73JVHgmfvyKf2ekTbAkL7z4E2j3vx KRXMek/5+m7gBDzjHXrU3Uvoe+yVCI58OAAo7kGjkFDSP5fKvdTiRwLzBS67XVscqm3X N0hw== X-Forwarded-Encrypted: i=1; AFNElJ9zxO/3BgOyTON7386+AzA8mLPzoA6nYw2Wp4jQIcVHFs09e30w2bc6C/e3YykrNr46PRA5JCeSgp3mZrs=@vger.kernel.org X-Gm-Message-State: AOJu0YyujLxK/X6oKHpoVG8xrQEqgyZAI7xJvf4XvchtE8JVTuCqaYDh qRGCV/pILjD9WWEpUGpl1mDaXBkP81MJ9j70trdhkLBGfe54He0gmPzZzVd6wDywwb4iL/2wAI4 YkVV8Dyv2wA== X-Received: from dlak2.prod.google.com ([2002:a05:701b:2902:b0:12d:b319:ccc4]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:6ba1:b0:12c:5e48:4c01 with SMTP id a92af1059eb24-12c73f6c501mr21511343c88.1.1777157488085; Sat, 25 Apr 2026 15:51:28 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:28 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-37-irogers@google.com> Subject: [PATCH v7 36/59] perf export-to-sqlite: Port export-to-sqlite to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This commit ports the export-to-sqlite.py script to use the modern perf Python module and the standard library sqlite3 module. It drops the dependency on PySide2.QtSql. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v5: 1. Fix Callchain Resolution: Corrected attribute lookups on callchain nodes. The `dso` and `symbol` properties already return strings, so attempting to get a `.name` attribute from them failed and caused fallback to "Unknown_...". --- tools/perf/python/export-to-sqlite.py | 372 ++++++++++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100755 tools/perf/python/export-to-sqlite.py diff --git a/tools/perf/python/export-to-sqlite.py b/tools/perf/python/expo= rt-to-sqlite.py new file mode 100755 index 000000000000..736b56ff8d59 --- /dev/null +++ b/tools/perf/python/export-to-sqlite.py @@ -0,0 +1,372 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +Export perf data to a sqlite3 database. + +This script has been ported to use the modern perf Python module and the +standard library sqlite3 module. It no longer requires PySide2 or QtSql +for exporting. + +Examples of using this script with Intel PT: + + $ perf record -e intel_pt//u ls + $ python tools/perf/python/export-to-sqlite.py -i perf.data -o pt_example + +To browse the database, sqlite3 can be used e.g. + + $ sqlite3 pt_example + sqlite> .header on + sqlite> select * from samples_view where id < 10; + sqlite> .mode column + sqlite> select * from samples_view where id < 10; + sqlite> .tables + sqlite> .schema samples_view + sqlite> .quit + +An example of using the database is provided by the script +exported-sql-viewer.py. Refer to that script for details. + +Ported from tools/perf/scripts/python/export-to-sqlite.py +""" + +import argparse +import os +import sqlite3 +import sys +from typing import Dict, Optional +import perf + + +class DatabaseExporter: + """Handles database connection and exporting of perf events.""" + + def __init__(self, db_path: str): + self.con =3D sqlite3.connect(db_path) + self.session: Optional[perf.session] =3D None + self.sample_count =3D 0 + + # Caches and counters grouped to reduce instance attributes + self.caches: Dict[str, dict] =3D { + 'threads': {}, + 'comms': {}, + 'dsos': {}, + 'symbols': {}, + 'events': {}, + 'branch_types': {}, + 'call_paths': {} + } + + self.next_id =3D { + 'thread': 1, + 'comm': 1, + 'dso': 1, + 'symbol': 1, + 'event': 1, + 'branch_type': 1, + 'call_path': 1 + } + + self.create_tables() + + def create_tables(self) -> None: + """Create database tables.""" + self.con.execute(""" + CREATE TABLE IF NOT EXISTS selected_events ( + id INTEGER NOT NULL PRIMARY KEY, + name VARCHAR(80)) + """) + self.con.execute(""" + CREATE TABLE IF NOT EXISTS machines ( + id INTEGER NOT NULL PRIMARY KEY, + pid INTEGER, + root_dir VARCHAR(4096)) + """) + self.con.execute(""" + CREATE TABLE IF NOT EXISTS threads ( + id INTEGER NOT NULL PRIMARY KEY, + machine_id BIGINT, + process_id BIGINT, + pid INTEGER, + tid INTEGER) + """) + self.con.execute(""" + CREATE TABLE IF NOT EXISTS comms ( + id INTEGER NOT NULL PRIMARY KEY, + comm VARCHAR(16), + c_thread_id BIGINT, + c_time BIGINT, + exec_flag BOOLEAN) + """) + self.con.execute(""" + CREATE TABLE IF NOT EXISTS comm_threads ( + id INTEGER NOT NULL PRIMARY KEY, + comm_id BIGINT, + thread_id BIGINT) + """) + self.con.execute(""" + CREATE TABLE IF NOT EXISTS dsos ( + id INTEGER NOT NULL PRIMARY KEY, + machine_id BIGINT, + short_name VARCHAR(256), + long_name VARCHAR(4096), + build_id VARCHAR(64)) + """) + self.con.execute(""" + CREATE TABLE IF NOT EXISTS symbols ( + id INTEGER NOT NULL PRIMARY KEY, + dso_id BIGINT, + sym_start BIGINT, + sym_end BIGINT, + binding INTEGER, + name VARCHAR(2048)) + """) + self.con.execute(""" + CREATE TABLE IF NOT EXISTS branch_types ( + id INTEGER NOT NULL PRIMARY KEY, + name VARCHAR(80)) + """) + self.con.execute(""" + CREATE TABLE IF NOT EXISTS samples ( + id INTEGER NOT NULL PRIMAR= Y KEY, + evsel_id BIGINT, + machine_id BIGINT, + thread_id BIGINT, + comm_id BIGINT, + dso_id BIGINT, + symbol_id BIGINT, + sym_offset BIGINT, + ip BIGINT, + time BIGINT, + cpu INTEGER, + to_dso_id BIGINT, + to_symbol_id BIGINT, + to_sym_offset BIGINT, + to_ip BIGINT, + period BIGINT, + weight BIGINT, + transaction_ BIGINT, + data_src BIGINT, + branch_type INTEGER, + in_tx BOOLEAN, + call_path_id BIGINT, + insn_count BIGINT, + cyc_count BIGINT, + flags INTEGER) + """) + self.con.execute(""" + CREATE TABLE IF NOT EXISTS call_paths ( + id INTEGER NOT NULL PRIMAR= Y KEY, + parent_id BIGINT, + symbol_id BIGINT, + ip BIGINT) + """) + self.con.execute(""" + CREATE VIEW IF NOT EXISTS samples_view AS + SELECT s.id, e.name as event, t.pid, t.tid, c.comm, + d.short_name as dso, sym.name as symbol, s.sym_offset, + s.ip, s.time, s.cpu + FROM samples s + JOIN selected_events e ON s.evsel_id =3D e.id + JOIN threads t ON s.thread_id =3D t.id + JOIN comms c ON s.comm_id =3D c.id + JOIN dsos d ON s.dso_id =3D d.id + JOIN symbols sym ON s.symbol_id =3D sym.id; + """) + + # id =3D=3D 0 means unknown. It is easier to create records for th= em than + # replace the zeroes with NULLs + self.con.execute("INSERT OR IGNORE INTO selected_events VALUES (0,= 'unknown')") + self.con.execute("INSERT OR IGNORE INTO machines VALUES (0, 0, 'un= known')") + self.con.execute("INSERT OR IGNORE INTO threads VALUES (0, 0, 0, -= 1, -1)") + self.con.execute("INSERT OR IGNORE INTO comms VALUES (0, 'unknown'= , 0, 0, 0)") + self.con.execute("INSERT OR IGNORE INTO dsos VALUES (0, 0, 'unknow= n', 'unknown', '')") + self.con.execute("INSERT OR IGNORE INTO symbols VALUES (0, 0, 0, 0= , 0, 'unknown')") + + def get_event_id(self, name: str) -> int: + """Get or create event ID.""" + if name in self.caches['events']: + return self.caches['events'][name] + event_id =3D self.next_id['event'] + self.con.execute("INSERT INTO selected_events VALUES (?, ?)", + (event_id, name)) + self.caches['events'][name] =3D event_id + self.next_id['event'] +=3D 1 + return event_id + + def get_thread_id(self, pid: int, tid: int) -> int: + """Get or create thread ID.""" + key =3D (pid, tid) + if key in self.caches['threads']: + return self.caches['threads'][key] + thread_id =3D self.next_id['thread'] + self.con.execute("INSERT INTO threads VALUES (?, ?, ?, ?, ?)", + (thread_id, 0, pid, pid, tid)) + self.caches['threads'][key] =3D thread_id + self.next_id['thread'] +=3D 1 + return thread_id + + def get_comm_id(self, comm: str, thread_id: int) -> int: + """Get or create comm ID.""" + if comm in self.caches['comms']: + return self.caches['comms'][comm] + comm_id =3D self.next_id['comm'] + self.con.execute("INSERT INTO comms VALUES (?, ?, ?, ?, ?)", + (comm_id, comm, thread_id, 0, 0)) + self.con.execute("INSERT INTO comm_threads VALUES (?, ?, ?)", + (comm_id, comm_id, thread_id)) + self.caches['comms'][comm] =3D comm_id + self.next_id['comm'] +=3D 1 + return comm_id + + def get_dso_id(self, short_name: str, long_name: str, + build_id: str) -> int: + """Get or create DSO ID.""" + if short_name in self.caches['dsos']: + return self.caches['dsos'][short_name] + dso_id =3D self.next_id['dso'] + self.con.execute("INSERT INTO dsos VALUES (?, ?, ?, ?, ?)", + (dso_id, 0, short_name, long_name, build_id)) + self.caches['dsos'][short_name] =3D dso_id + self.next_id['dso'] +=3D 1 + return dso_id + + def get_symbol_id(self, dso_id: int, name: str, start: int, + end: int) -> int: + """Get or create symbol ID.""" + key =3D (dso_id, name) + if key in self.caches['symbols']: + return self.caches['symbols'][key] + symbol_id =3D self.next_id['symbol'] + self.con.execute("INSERT INTO symbols VALUES (?, ?, ?, ?, ?, ?)", + (symbol_id, dso_id, start, end, 0, name)) + self.caches['symbols'][key] =3D symbol_id + self.next_id['symbol'] +=3D 1 + return symbol_id + + def get_call_path_id(self, parent_id: int, symbol_id: int, + ip: int) -> int: + """Get or create call path ID.""" + key =3D (parent_id, symbol_id, ip) + if key in self.caches['call_paths']: + return self.caches['call_paths'][key] + call_path_id =3D self.next_id['call_path'] + self.con.execute("INSERT INTO call_paths VALUES (?, ?, ?, ?)", + (call_path_id, parent_id, symbol_id, ip)) + self.caches['call_paths'][key] =3D call_path_id + self.next_id['call_path'] +=3D 1 + return call_path_id + + def process_event(self, sample: perf.sample_event) -> None: + """Callback for processing events.""" + thread_id =3D self.get_thread_id(sample.sample_pid, sample.sample_= tid) + + comm =3D "Unknown_comm" + try: + if self.session is not None: + proc =3D self.session.find_thread(sample.sample_pid) + if proc: + comm =3D proc.comm() + except TypeError: + pass + comm_id =3D self.get_comm_id(comm, thread_id) + + dso_id =3D self.get_dso_id( + getattr(sample, 'dso', "Unknown_dso") or "Unknown_dso", + getattr(sample, 'dso_long_name', "Unknown_dso_long") or "Unkno= wn_dso_long", + getattr(sample, 'dso_bid', "") or "" + ) + + symbol_id =3D self.get_symbol_id( + dso_id, + getattr(sample, 'symbol', "Unknown_symbol") or "Unknown_symbol= ", + getattr(sample, 'sym_start', 0) or 0, + getattr(sample, 'sym_end', 0) or 0 + ) + + # Handle callchain + call_path_id =3D 0 + if hasattr(sample, 'callchain') and sample.callchain: + parent_id =3D 0 + for node in sample.callchain: + dso_name =3D getattr(node, 'dso', "Unknown_dso") or "Unkno= wn_dso" + symbol_name =3D getattr(node, 'symbol', "Unknown_symbol") = or "Unknown_symbol" + + node_dso_id =3D self.get_dso_id(dso_name, dso_name, "") + node_symbol_id =3D self.get_symbol_id(node_dso_id, symbol_= name, 0, 0) + + parent_id =3D self.get_call_path_id(parent_id, node_symbol= _id, node.ip) + call_path_id =3D parent_id + else: + call_path_id =3D 0 + + # Insert sample + self.con.execute(""" + INSERT INTO samples VALUES ( + NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?= , ?, ?, ?, ?, ?, ? + ) + """, ( + self.get_event_id(getattr(sample.evsel, 'name', str(sample.evs= el))), + 0, thread_id, comm_id, + dso_id, symbol_id, getattr(sample, 'sym_offset', 0), + sample.sample_ip, sample.sample_time, sample.sample_cpu, + 0, 0, 0, 0, # to_dso, to_symbol, to_sym_offset, to_ip + getattr(sample, 'sample_period', 0) or 0, + getattr(sample, 'sample_weight', 0) or 0, + getattr(sample, 'transaction_', 0), + getattr(sample, 'data_src', 0), + 0, # branch_type + getattr(sample, 'in_tx', 0), + call_path_id, + getattr(sample, 'insn_count', 0), + getattr(sample, 'cyc_count', 0), + getattr(sample, 'flags', 0) + )) + + self.sample_count +=3D 1 + if self.sample_count % 10000 =3D=3D 0: + self.commit() + + def commit(self) -> None: + """Commit transaction.""" + self.con.commit() + + def close(self) -> None: + """Close connection.""" + self.con.close() + + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser( + description=3D"Export perf data to a sqlite3 database") + ap.add_argument("-i", "--input", default=3D"perf.data", + help=3D"Input file name") + ap.add_argument("-o", "--output", default=3D"perf.db", + help=3D"Output database name") + args =3D ap.parse_args() + + try: + fd =3D os.open(args.output, os.O_CREAT | os.O_EXCL | os.O_WRONLY) + os.close(fd) + except FileExistsError: + print(f"Error: {args.output} already exists") + sys.exit(1) + + exporter =3D DatabaseExporter(args.output) + + session =3D None + error_occurred =3D False + try: + session =3D perf.session(perf.data(args.input), + sample=3Dexporter.process_event) + exporter.session =3D session + session.process_events() + exporter.commit() + print(f"Successfully exported to {args.output}") + except Exception as e: + print(f"Error processing events: {e}") + error_occurred =3D True + finally: + exporter.close() + if error_occurred: + if os.path.exists(args.output): + os.remove(args.output) --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D0D0E3A785D for ; Sat, 25 Apr 2026 22:51:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157493; cv=none; b=PIDn3fuDbHNyjE6GNhQutSC8An80821KKKvdirR+WHQUzVHNsPiaLAiDH06h54gE5ilVzre6omJDBqCgjcLtJnzD6pW63njTBEBPgLVM3N+sMS8/0eZSHdSfabSB4Nid6a9uB7Gkbz99kS7Il6ikNEXEb9VNYEGCVqqC/mhFF6U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157493; c=relaxed/simple; bh=A7O/Xe3Jzwu6XX6+ARG1kxp2mxEIfU1PnuglMtIKhmA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=JVtDS6e9ekC2kVe488fg/f6PHWUc7AZ2jmeLme0tKkH4FaoCZ2KZLZfko+Oz394ePYxhKzmqh7+4e8wNNHmuHW4T2/EtHaQOEcMS+Mcdvw1DMFr0kdC4OKQZwaNN0+MuJiunD9og2B/HkcKWe0DS8feqspfJ30vUY+F9cUDZQ/M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=tkCSQEuI; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="tkCSQEuI" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2da19227bc1so20923805eec.1 for ; Sat, 25 Apr 2026 15:51:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157490; x=1777762290; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=eSIqwKcS0eTw/iJ71nuF6LH/l45/M4zma3yK4ndbcR0=; b=tkCSQEuI3zxcTXIZ0vWpsM8cLaslYttsXsk/RkUnMe/OPjCtpTu/dBCGUSmnnV0uQR uIYMylaNuYNoIEscCAg7wu6jbH0vAFfJtNA/VhOoz2QDUn3wwP9V8IvMlOS48jgP4xdy Cay5Je2lKVuF5LnUoD/F7ucbWDUB3nXXgEUXNq7DDuxZZPc2Wm8nVKOk6Gn9nZP0lf5Y Olovxndpl7XgekhM6PSjVDAX8oFZClrqOr+FFTCMkd8Qv4ClvingJG0rCTBnT879M/Gt A+Y/YYGa2mgNodfvBkPB6hRKHz5WgaSWBLJxkXjq2qhjFNuXezvRxaMkM4mfDmTRMNWB YMnw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157490; x=1777762290; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=eSIqwKcS0eTw/iJ71nuF6LH/l45/M4zma3yK4ndbcR0=; b=KBYI4tVA1UMqWGCI6bYROuoTVLSE0UBKWOpyNRqviODDT+bPYPhWmiyz4P4N0AfvhH DIrBPkhjKWFgLkFyq3+XPeWlM9m0L0cy5JoEVAtuBMLfVclygCW6RgDleLhxMCCQSS+7 iqb9rcCPDE9WVZLF++ooYJT/ELbRwPpPZiLS0CYHBDvNTgIf0AKPiWb0I1zhcq0rizg2 KCKc4UN2UXII9gJzRRBY64MVWWNl1Xx6cRkm5FueprCSG5CPzjQcyB4Fy6u/zZU5y/vN Nk+xUV8V3E6+C4Z5cudc0rBxhtOamrPVpPsJr4FaeUtHPjaw+dZn2m+Q6D8v1jiQyjnb mrFQ== X-Forwarded-Encrypted: i=1; AFNElJ96tGXQZFfttPZTwU/HI6iq9PsAKIfCv8O9QqEWTtoSBZqEJ4c9lEeqU9DZzEmWo3xS27E0n9WdAAcmrmw=@vger.kernel.org X-Gm-Message-State: AOJu0YwqOqSaPMtVHPEXov5TWIYpD5AfikgEjt72mO1lBTqxN3QBQqhR nw2DTK5xPyYz7VIw9HJoEIiSJKyYmnNSqZ9f7AfV40Xplb6jza5YA4+/fiItuVbvgo1Fj4DW2hI xcj9psh0SSg== X-Received: from dyckg11.prod.google.com ([2002:a05:7301:d18b:b0:2e8:5e04:ef25]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:fa10:b0:2e6:e504:5435 with SMTP id 5a478bee46e88-2e6e5045e56mr11466870eec.12.1777157489866; Sat, 25 Apr 2026 15:51:29 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:29 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-38-irogers@google.com> Subject: [PATCH v7 37/59] perf export-to-postgresql: Port export-to-postgresql to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Ported from tools/perf/scripts/python/export-to-postgresql.py to use the perf Python module API. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v5: 1. Fix Data Integrity: Added `comm_thread` ID sequence to prevent duplicate primary keys in `comm_threads` table. 2. Fix COPY failure: Ensured file trailer is written and files are closed before being copied to PostgreSQL, preventing data rejection. --- tools/perf/python/export-to-postgresql.py | 701 ++++++++++++++++++++++ 1 file changed, 701 insertions(+) create mode 100755 tools/perf/python/export-to-postgresql.py diff --git a/tools/perf/python/export-to-postgresql.py b/tools/perf/python/= export-to-postgresql.py new file mode 100755 index 000000000000..0118dc348b1e --- /dev/null +++ b/tools/perf/python/export-to-postgresql.py @@ -0,0 +1,701 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +r""" +Export perf data to a postgresql database. + +This script has been ported to use the modern perf Python module and +libpq via ctypes. It no longer requires PySide2 or QtSql for exporting. + +The script assumes postgresql is running on the local machine and that the +user has postgresql permissions to create databases. + +An example of using this script with Intel PT: + + $ perf record -e intel_pt//u ls + $ python tools/perf/python/export-to-postgresql.py -i perf.data -o pt_exa= mple + +To browse the database, psql can be used e.g. + + $ psql pt_example + pt_example=3D# select * from samples_view where id < 100; + pt_example=3D# \d+ + pt_example=3D# \d+ samples_view + pt_example=3D# \q + +An example of using the database is provided by the script +exported-sql-viewer.py. Refer to that script for details. + +Tables: + + The tables largely correspond to perf tools' data structures. They are + largely self-explanatory. + + samples + 'samples' is the main table. It represents what instruction was + executing at a point in time when something (a selected event) + happened. The memory address is the instruction pointer or 'ip'. + + branch_types + 'branch_types' provides descriptions for each type of branch. + + comm_threads + 'comm_threads' shows how 'comms' relates to 'threads'. + + comms + 'comms' contains a record for each 'comm' - the name given to the + executable that is running. + + dsos + 'dsos' contains a record for each executable file or library. + + machines + 'machines' can be used to distinguish virtual machines if + virtualization is supported. + + selected_events + 'selected_events' contains a record for each kind of event that + has been sampled. + + symbols + 'symbols' contains a record for each symbol. Only symbols that + have samples are present. + + threads + 'threads' contains a record for each thread. + +Views: + + Most of the tables have views for more friendly display. The views are: + + comm_threads_view + dsos_view + machines_view + samples_view + symbols_view + threads_view + +Ported from tools/perf/scripts/python/export-to-postgresql.py +""" + +import argparse +from ctypes import CDLL, c_char_p, c_int, c_void_p, c_ubyte +import ctypes.util +import os +import shutil +import struct +import sys +from typing import Any, Dict, Optional +import perf + +# Need to access PostgreSQL C library directly to use COPY FROM STDIN +libpq_name =3D ctypes.util.find_library("pq") +if not libpq_name: + libpq_name =3D "libpq.so.5" + +try: + libpq =3D CDLL(libpq_name) +except OSError as e: + print(f"Error loading {libpq_name}: {e}") + print("Please ensure PostgreSQL client library is installed.") + sys.exit(1) + +PQconnectdb =3D libpq.PQconnectdb +PQconnectdb.restype =3D c_void_p +PQconnectdb.argtypes =3D [c_char_p] +PQfinish =3D libpq.PQfinish +PQfinish.argtypes =3D [c_void_p] +PQstatus =3D libpq.PQstatus +PQstatus.restype =3D c_int +PQstatus.argtypes =3D [c_void_p] +PQexec =3D libpq.PQexec +PQexec.restype =3D c_void_p +PQexec.argtypes =3D [c_void_p, c_char_p] +PQresultStatus =3D libpq.PQresultStatus +PQresultStatus.restype =3D c_int +PQresultStatus.argtypes =3D [c_void_p] +PQputCopyData =3D libpq.PQputCopyData +PQputCopyData.restype =3D c_int +PQputCopyData.argtypes =3D [c_void_p, c_void_p, c_int] +PQputCopyEnd =3D libpq.PQputCopyEnd +PQputCopyEnd.restype =3D c_int +PQputCopyEnd.argtypes =3D [c_void_p, c_void_p] +PQgetResult =3D libpq.PQgetResult +PQgetResult.restype =3D c_void_p +PQgetResult.argtypes =3D [c_void_p] +PQclear =3D libpq.PQclear +PQclear.argtypes =3D [c_void_p] + + +def toserverstr(s: str) -> bytes: + """Convert string to server encoding (UTF-8).""" + return bytes(s, "UTF_8") + + +def toclientstr(s: str) -> bytes: + """Convert string to client encoding (UTF-8).""" + return bytes(s, "UTF_8") + + + +class PostgresExporter: + """Handles PostgreSQL connection and exporting of perf events.""" + + def __init__(self, dbname: str): + self.dbname =3D dbname + self.conn =3D None + self.session: Optional[perf.session] =3D None + self.output_dir_name =3D os.getcwd() + "/" + dbname + "-perf-data" + + self.file_header =3D struct.pack("!11sii", b"PGCOPY\n\377\r\n\0", = 0, 0) + self.file_trailer =3D b"\377\377" + + # Caches and counters grouped to reduce instance attributes + self.caches: Dict[str, dict] =3D { + 'threads': {}, + 'comms': {}, + 'dsos': {}, + 'symbols': {}, + 'events': {}, + 'branch_types': {}, + 'call_paths': {} + } + + self.next_id =3D { + 'thread': 1, + 'comm': 1, + 'dso': 1, + 'symbol': 1, + 'event': 1, + 'branch_type': 1, + 'comm_thread': 1, + 'call_path': 1 + } + + self.files: Dict[str, Any] =3D {} + self.unhandled_count =3D 0 + + def connect(self, db_to_use: str) -> None: + """Connect to database.""" + conn_str =3D toclientstr(f"dbname =3D {db_to_use}") + self.conn =3D PQconnectdb(conn_str) + if PQstatus(self.conn) !=3D 0: + raise RuntimeError(f"PQconnectdb failed for {db_to_use}") + + def disconnect(self) -> None: + """Disconnect from database.""" + if self.conn: + PQfinish(self.conn) + self.conn =3D None + + def do_query(self, sql: str) -> None: + """Execute a query and check status.""" + res =3D PQexec(self.conn, toserverstr(sql)) + status =3D PQresultStatus(res) + PQclear(res) + if status not in (1, 2): # PGRES_COMMAND_OK, PGRES_TUPLES_OK + raise RuntimeError(f"Query failed: {sql}") + + + def open_output_file(self, file_name: str): + """Open intermediate binary file.""" + path_name =3D self.output_dir_name + "/" + file_name + f =3D open(path_name, "wb+") + f.write(self.file_header) + return f + + def close_output_file(self, f): + """Close intermediate binary file.""" + f.write(self.file_trailer) + f.close() + + def copy_output_file(self, path_name: str, table_name: str): + """Copy intermediate file to database.""" + sql =3D f"COPY {table_name} FROM STDIN (FORMAT 'binary')" + res =3D PQexec(self.conn, toserverstr(sql)) + if PQresultStatus(res) !=3D 4: # PGRES_COPY_IN + PQclear(res) + raise RuntimeError(f"COPY FROM STDIN PQexec failed for {table_= name}") + PQclear(res) + + with open(path_name, "rb") as f: + data =3D f.read(65536) + while len(data) > 0: + c_data =3D (c_ubyte * len(data)).from_buffer_copy(data) + ret =3D PQputCopyData(self.conn, c_data, len(data)) + if ret !=3D 1: + raise RuntimeError(f"PQputCopyData failed for {table_n= ame}") + data =3D f.read(65536) + + ret =3D PQputCopyEnd(self.conn, None) + if ret !=3D 1: + raise RuntimeError(f"PQputCopyEnd failed for {table_name}") + + res =3D PQgetResult(self.conn) + while res: + PQclear(res) + res =3D PQgetResult(self.conn) + + + + def setup_db(self) -> None: + """Create database and tables. MUST be called after init.""" + os.mkdir(self.output_dir_name) + + self.connect('postgres') + try: + self.do_query(f'CREATE DATABASE "{self.dbname}"') + except Exception as e: + os.rmdir(self.output_dir_name) + raise e + self.disconnect() + + self.connect(self.dbname) + self.do_query("SET client_min_messages TO WARNING") + + self.do_query(""" + CREATE TABLE selected_events ( + id bigint NOT NULL, + name varchar(80)) + """) + self.do_query(""" + CREATE TABLE machines ( + id bigint NOT NULL, + pid integer, + root_dir varchar(4096)) + """) + self.do_query(""" + CREATE TABLE threads ( + id bigint NOT NULL, + machine_id bigint, + process_id bigint, + pid integer, + tid integer) + """) + self.do_query(""" + CREATE TABLE comms ( + id bigint NOT NULL, + comm varchar(16), + c_thread_id bigint, + c_time bigint, + exec_flag boolean) + """) + self.do_query(""" + CREATE TABLE comm_threads ( + id bigint NOT NULL, + comm_id bigint, + thread_id bigint) + """) + self.do_query(""" + CREATE TABLE dsos ( + id bigint NOT NULL, + machine_id bigint, + short_name varchar(256), + long_name varchar(4096), + build_id varchar(64)) + """) + self.do_query(""" + CREATE TABLE symbols ( + id bigint NOT NULL, + dso_id bigint, + sym_start bigint, + sym_end bigint, + binding integer, + name varchar(2048)) + """) + self.do_query(""" + CREATE TABLE branch_types ( + id integer NOT NULL, + name varchar(80)) + """) + self.do_query(""" + CREATE TABLE samples ( + id bigint NOT NULL, + evsel_id bigint, + machine_id bigint, + thread_id bigint, + comm_id bigint, + dso_id bigint, + symbol_id bigint, + sym_offset bigint, + ip bigint, + time bigint, + cpu integer, + to_dso_id bigint, + to_symbol_id bigint, + to_sym_offset bigint, + to_ip bigint, + period bigint, + weight bigint, + transaction_ bigint, + data_src bigint, + branch_type integer, + in_tx boolean, + call_path_id bigint, + insn_count bigint, + cyc_count bigint, + flags integer) + """) + self.do_query(""" + CREATE TABLE call_paths ( + id bigint NOT NULL, + parent_id bigint, + symbol_id bigint, + ip bigint) + """) + + self.files['evsel'] =3D self.open_output_file("evsel_table.bin") + self.files['machine'] =3D self.open_output_file("machine_table.bin= ") + self.files['thread'] =3D self.open_output_file("thread_table.bin") + self.files['comm'] =3D self.open_output_file("comm_table.bin") + self.files['comm_thread'] =3D self.open_output_file("comm_thread_t= able.bin") + self.files['dso'] =3D self.open_output_file("dso_table.bin") + self.files['symbol'] =3D self.open_output_file("symbol_table.bin") + self.files['branch_type'] =3D self.open_output_file("branch_type_t= able.bin") + self.files['sample'] =3D self.open_output_file("sample_table.bin") + self.files['call_path'] =3D self.open_output_file("call_path_table= .bin") + + self.write_evsel(0, "unknown") + self.write_machine(0, 0, "unknown") + self.write_thread(0, 0, 0, -1, -1) + self.write_comm(0, "unknown", 0, 0, 0) + self.write_dso(0, 0, "unknown", "unknown", "") + self.write_symbol(0, 0, 0, 0, 0, "unknown") + self.write_call_path(0, 0, 0, 0) + + def write_evsel(self, evsel_id: int, name: str) -> None: + """Write event to binary file.""" + name_bytes =3D toserverstr(name) + n =3D len(name_bytes) + fmt =3D "!hiqi" + str(n) + "s" + value =3D struct.pack(fmt, 2, 8, evsel_id, n, name_bytes) + self.files['evsel'].write(value) + + def write_machine(self, machine_id: int, pid: int, root_dir: str) -> N= one: + """Write machine to binary file.""" + rd_bytes =3D toserverstr(root_dir) + n =3D len(rd_bytes) + fmt =3D "!hiqiii" + str(n) + "s" + value =3D struct.pack(fmt, 3, 8, machine_id, 4, pid, n, rd_bytes) + self.files['machine'].write(value) + + + def write_thread(self, thread_id: int, machine_id: int, process_id: in= t, + pid: int, tid: int) -> None: + """Write thread to binary file.""" + value =3D struct.pack("!hiqiqiqiiii", 5, 8, thread_id, 8, machine_= id, + 8, process_id, 4, pid, 4, tid) + self.files['thread'].write(value) + + + def write_comm(self, comm_id: int, comm_str: str, thread_id: int, + time: int, exec_flag: int) -> None: + """Write comm to binary file.""" + comm_bytes =3D toserverstr(comm_str) + n =3D len(comm_bytes) + fmt =3D "!hiqi" + str(n) + "s" + "iqiqiB" + value =3D struct.pack(fmt, 5, 8, comm_id, n, comm_bytes, 8, + thread_id, 8, time, 1, exec_flag) + self.files['comm'].write(value) + + def write_comm_thread(self, comm_thread_id: int, comm_id: int, + thread_id: int) -> None: + """Write comm_thread to binary file.""" + fmt =3D "!hiqiqiq" + value =3D struct.pack(fmt, 3, 8, comm_thread_id, 8, comm_id, 8, th= read_id) + self.files['comm_thread'].write(value) + + + def write_dso(self, dso_id: int, machine_id: int, short_name: str, + long_name: str, build_id: str) -> None: + """Write DSO to binary file.""" + sn_bytes =3D toserverstr(short_name) + ln_bytes =3D toserverstr(long_name) + bi_bytes =3D toserverstr(build_id) + n1, n2, n3 =3D len(sn_bytes), len(ln_bytes), len(bi_bytes) + fmt =3D "!hiqiqi" + str(n1) + "si" + str(n2) + "si" + str(n3) + "s" + value =3D struct.pack(fmt, 5, 8, dso_id, 8, machine_id, n1, + sn_bytes, n2, ln_bytes, n3, bi_bytes) + self.files['dso'].write(value) + + + def write_symbol(self, symbol_id: int, dso_id: int, sym_start: int, + sym_end: int, binding: int, symbol_name: str) -> Non= e: + """Write symbol to binary file.""" + name_bytes =3D toserverstr(symbol_name) + n =3D len(name_bytes) + fmt =3D "!hiqiqiqiqiii" + str(n) + "s" + value =3D struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, + sym_start, 8, sym_end, 4, binding, n, name_byt= es) + self.files['symbol'].write(value) + + def write_call_path(self, cp_id: int, parent_id: int, symbol_id: int, + ip: int) -> None: + """Write call path to binary file.""" + fmt =3D "!hiqiqiqiq" + value =3D struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id= , 8, ip) + self.files['call_path'].write(value) + + + def write_sample(self, sample_id: int, evsel_id: int, thread_id: int, + comm_id: int, dso_id: int, symbol_id: int, + sample: perf.sample_event, call_path_id: int) -> Non= e: + """Write sample to binary file.""" + value =3D struct.pack( + "!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiBiqiqiqii", + 25, 8, sample_id, 8, evsel_id, 8, 0, 8, thread_id, 8, comm_id, + 8, dso_id, 8, symbol_id, 8, getattr(sample, 'sym_offset', 0), + 8, sample.sample_ip, 8, sample.sample_time, 4, sample.sample_c= pu, + 8, 0, 8, 0, 8, 0, 8, 0, + 8, getattr(sample, 'sample_period', 0) or 0, + 8, getattr(sample, 'sample_weight', 0) or 0, + 8, getattr(sample, 'transaction_', 0) or 0, + 8, getattr(sample, 'data_src', 0) or 0, + 4, 0, + 1, getattr(sample, 'in_tx', 0) or 0, + 8, call_path_id, + 8, getattr(sample, 'insn_count', 0) or 0, + 8, getattr(sample, 'cyc_count', 0) or 0, + 4, getattr(sample, 'flags', 0) or 0 + ) + self.files['sample'].write(value) + + def get_event_id(self, name: str) -> int: + """Get or create event ID.""" + if name in self.caches['events']: + return self.caches['events'][name] + event_id =3D self.next_id['event'] + self.write_evsel(event_id, name) + self.caches['events'][name] =3D event_id + self.next_id['event'] +=3D 1 + return event_id + + def get_thread_id(self, pid: int, tid: int) -> int: + """Get or create thread ID.""" + key =3D (pid, tid) + if key in self.caches['threads']: + return self.caches['threads'][key] + thread_id =3D self.next_id['thread'] + self.write_thread(thread_id, 0, pid, pid, tid) + self.caches['threads'][key] =3D thread_id + self.next_id['thread'] +=3D 1 + return thread_id + + def get_comm_id(self, comm: str, thread_id: int) -> int: + """Get or create comm ID.""" + if comm in self.caches['comms']: + comm_id =3D self.caches['comms'][comm] + else: + comm_id =3D self.next_id['comm'] + self.write_comm(comm_id, comm, thread_id, 0, 0) + self.caches['comms'][comm] =3D comm_id + self.next_id['comm'] +=3D 1 + + key =3D (comm_id, thread_id) + if 'comm_threads' not in self.caches: + self.caches['comm_threads'] =3D {} + if key not in self.caches['comm_threads']: + comm_thread_id =3D self.next_id['comm_thread'] + self.write_comm_thread(comm_thread_id, comm_id, thread_id) + self.caches['comm_threads'][key] =3D True + self.next_id['comm_thread'] +=3D 1 + + return comm_id + + def get_dso_id(self, short_name: str, long_name: str, + build_id: str) -> int: + """Get or create DSO ID.""" + if short_name in self.caches['dsos']: + return self.caches['dsos'][short_name] + dso_id =3D self.next_id['dso'] + self.write_dso(dso_id, 0, short_name, long_name, build_id) + self.caches['dsos'][short_name] =3D dso_id + self.next_id['dso'] +=3D 1 + return dso_id + + def get_symbol_id(self, dso_id: int, name: str, start: int, + end: int) -> int: + """Get or create symbol ID.""" + key =3D (dso_id, name) + if key in self.caches['symbols']: + return self.caches['symbols'][key] + symbol_id =3D self.next_id['symbol'] + self.write_symbol(symbol_id, dso_id, start, end, 0, name) + self.caches['symbols'][key] =3D symbol_id + self.next_id['symbol'] +=3D 1 + return symbol_id + + def get_call_path_id(self, parent_id: int, symbol_id: int, + ip: int) -> int: + """Get or create call path ID.""" + key =3D (parent_id, symbol_id, ip) + if key in self.caches['call_paths']: + return self.caches['call_paths'][key] + call_path_id =3D self.next_id['call_path'] + self.write_call_path(call_path_id, parent_id, symbol_id, ip) + self.caches['call_paths'][key] =3D call_path_id + self.next_id['call_path'] +=3D 1 + return call_path_id + + def process_event(self, sample: perf.sample_event) -> None: + """Callback for processing events.""" + thread_id =3D self.get_thread_id(sample.sample_pid, sample.sample_= tid) + + comm =3D "Unknown_comm" + try: + if self.session is not None: + proc =3D self.session.find_thread(sample.sample_pid) + if proc: + comm =3D proc.comm() + except TypeError: + pass + comm_id =3D self.get_comm_id(comm, thread_id) + + dso_id =3D self.get_dso_id( + getattr(sample, 'dso', "Unknown_dso") or "Unknown_dso", + getattr(sample, 'dso_long_name', "Unknown_dso_long") or "Unkno= wn_dso_long", + getattr(sample, 'dso_bid', "") or "" + ) + + symbol_id =3D self.get_symbol_id( + dso_id, + getattr(sample, 'symbol', "Unknown_symbol") or "Unknown_symbol= ", + getattr(sample, 'sym_start', 0) or 0, + getattr(sample, 'sym_end', 0) or 0 + ) + + call_path_id =3D 0 + if hasattr(sample, 'callchain') and sample.callchain: + parent_id =3D 0 + for node in sample.callchain: + node_dso =3D getattr(node, 'dso', None) or getattr(node, '= map', None) + node_symbol =3D getattr(node, 'symbol', None) or getattr(n= ode, 'sym', None) + + dso_name =3D "Unknown_dso" + if node_dso: + dso_name =3D getattr(node_dso, 'name', "Unknown_dso") = or "Unknown_dso" + + symbol_name =3D "Unknown_symbol" + if node_symbol: + symbol_name =3D getattr(node_symbol, 'name', "Unknown_= symbol") or "Unknown_symbol" + + node_dso_id =3D self.get_dso_id(dso_name, dso_name, "") + node_symbol_id =3D self.get_symbol_id(node_dso_id, symbol_= name, 0, 0) + + parent_id =3D self.get_call_path_id(parent_id, node_symbol= _id, node.ip) + call_path_id =3D parent_id + + sample_id =3D self.next_id['event'] + self.write_sample(sample_id, + self.get_event_id(getattr(sample.evsel, 'name', = str(sample.evsel))), + thread_id, comm_id, dso_id, symbol_id, sample, + call_path_id) + self.next_id['event'] +=3D 1 + + def finalize(self) -> None: + """Copy files to database and add keys/views.""" + print("Copying to database...") + for name, f in self.files.items(): + self.close_output_file(f) + + for name, f in self.files.items(): + table_name =3D name + "s" if name !=3D "call_path" else "call_= paths" + if name =3D=3D "evsel": + table_name =3D "selected_events" + self.copy_output_file(f.name, table_name) + + print("Removing intermediate files...") + for name, f in self.files.items(): + os.unlink(f.name) + os.rmdir(self.output_dir_name) + + print("Adding primary keys") + self.do_query("ALTER TABLE selected_events ADD PRIMARY KEY (id)") + self.do_query("ALTER TABLE machines ADD PRIMARY KEY (id)") + self.do_query("ALTER TABLE threads ADD PRIMARY KEY (id)") + self.do_query("ALTER TABLE comms ADD PRIMARY KEY (id)") + self.do_query("ALTER TABLE comm_threads ADD PRIMARY KEY (id)") + self.do_query("ALTER TABLE dsos ADD PRIMARY KEY (id)") + self.do_query("ALTER TABLE symbols ADD PRIMARY KEY (id)") + self.do_query("ALTER TABLE branch_types ADD PRIMARY KEY (id)") + self.do_query("ALTER TABLE samples ADD PRIMARY KEY (id)") + self.do_query("ALTER TABLE call_paths ADD PRIMARY KEY (id)") + + print("Creating views...") + self.do_query(""" + CREATE VIEW machines_view AS + SELECT id, pid, root_dir, + CASE WHEN id=3D0 THEN 'unknown' WHEN pid=3D-1 THEN 'host' ELSE= 'guest' END AS host_or_guest + FROM machines + """) + self.do_query(""" + CREATE VIEW dsos_view AS + SELECT id, machine_id, + (SELECT host_or_guest FROM machines_view WHERE id =3D machine_= id) AS host_or_guest, + short_name, long_name, build_id + FROM dsos + """) + self.do_query(""" + CREATE VIEW symbols_view AS + SELECT id, name, + (SELECT short_name FROM dsos WHERE id=3Ddso_id) AS dso, + dso_id, sym_start, sym_end, + CASE WHEN binding=3D0 THEN 'local' WHEN binding=3D1 THEN 'glob= al' ELSE 'weak' END AS binding + FROM symbols + """) + self.do_query(""" + CREATE VIEW threads_view AS + SELECT id, machine_id, + (SELECT host_or_guest FROM machines_view WHERE id =3D machine_= id) AS host_or_guest, + process_id, pid, tid + FROM threads + """) + self.do_query(""" + CREATE VIEW samples_view AS + SELECT id, time, cpu, + (SELECT pid FROM threads WHERE id =3D thread_id) AS pid, + (SELECT tid FROM threads WHERE id =3D thread_id) AS tid, + (SELECT comm FROM comms WHERE id =3D comm_id) AS command, + (SELECT name FROM selected_events WHERE id =3D evsel_id) AS ev= ent, + to_hex(ip) AS ip_hex, + (SELECT name FROM symbols WHERE id =3D symbol_id) AS symbol, + sym_offset, + (SELECT short_name FROM dsos WHERE id =3D dso_id) AS dso_short= _name, + to_hex(to_ip) AS to_ip_hex, + (SELECT name FROM symbols WHERE id =3D to_symbol_id) AS to_sym= bol, + to_sym_offset, + (SELECT short_name FROM dsos WHERE id =3D to_dso_id) AS to_dso= _short_name, + (SELECT name FROM branch_types WHERE id =3D branch_type) AS br= anch_type_name, + in_tx, insn_count, cyc_count, flags + FROM samples + """) + + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser( + description=3D"Export perf data to a postgresql database") + ap.add_argument("-i", "--input", default=3D"perf.data", + help=3D"Input file name") + ap.add_argument("-o", "--output", required=3DTrue, + help=3D"Output database name") + args =3D ap.parse_args() + + exporter =3D PostgresExporter(args.output) + exporter.setup_db() + + session =3D None + error_occurred =3D False + try: + session =3D perf.session(perf.data(args.input), + sample=3Dexporter.process_event) + exporter.session =3D session + session.process_events() + exporter.finalize() + print(f"Successfully exported to {args.output}") + except Exception as e: + print(f"Error processing events: {e}") + error_occurred =3D True + finally: + exporter.disconnect() + if error_occurred: + if os.path.exists(exporter.output_dir_name): + shutil.rmtree(exporter.output_dir_name) --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F2DCD3A9014 for ; Sat, 25 Apr 2026 22:51:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157494; cv=none; b=TA9d5IJHzGeIzqqiLcLOJv/WmJCjaR/P3qvgnsA44m3Mhz9o0i3ufcyHgbrX6PBRTkac8cbbTJMNmOd3DxJEIhj6lr1oqm3ba0V7VITHJLmIZLhGbPmQOc+4EbaIobkZ6rBSXnYsAAxlEEvst6b0Lu3oOqsWHcT01b9/xxjgNTg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157494; c=relaxed/simple; bh=cskMJqUjMN8z5wW286c546Yg+5Pr06NDcX4mICkIag4=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=G1OgEjXPzepF64LWtxzMNKDzLBZV8AujY5IyIkQkJfbS47vECsvOzTq6m/NiMG6CladClVIXFqVDDtvKZJUAlLhqi4pHCSuS+y4qPYHvSvQDxyoEkDHO1w3Ze50Aj5ki3xSDkR4ZWyL4qc/8CH0gMCM6KwI+nOyMA4Y42g268zA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Lh+cZ3q0; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Lh+cZ3q0" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-12c87ba0890so30474600c88.0 for ; Sat, 25 Apr 2026 15:51:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157492; x=1777762292; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=+1e9d0GFF8QSKEFlq8avcbJMHsTR7P2tC3S887pI/aQ=; b=Lh+cZ3q0nrVMYZA2yeVxhhzeA/3FQdNgZJs1aOV8RWSbKYoIQm7fuYcDiCSp2tLznJ G8U8b0elILOAWIGfPBDYpEXGc+Kd1ft6nZ37nd43n7pdV5Wy2zXD3YbNGCXEpqIGcNar E3yWmQxk2wOCr+Wv/E5VdzfyY8nw8JxOffhATG2CH2Zv6GcpYTH1VaZfsqzckk+20xTd qDprlrxaBgeRAcoHR85tAGYJwbjCEF85a0idhMuavAaQpPD3Or+eDHuWVIyLzEX5HW/r 3+ALoNKWcn19WL8Gs7lMtMrDMntOqkO3ExsjtdxI0fxEqz9jxWXITd+u6IuiVqCbhLjl CNhQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157492; x=1777762292; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=+1e9d0GFF8QSKEFlq8avcbJMHsTR7P2tC3S887pI/aQ=; b=RiwEMrPo0h40XRfu37TG9yWtTCfuC9BywwbUFRDevFnbxZlH4nHepeocluHk9eQiHJ y0h9EDs25zLdKSj6qtnng6jFlh+Q8YOAxkpf1Wjspmta3Z7HE3VSwEhXdpTLuiw9wK+s M1xYt8V5n1ac1PEbb1pdFybw8UmD+UISwZK58KXGfN/SDp2iiIeyfJNoJSEVpOng23+7 1iGHMINypiLUp+mGlOrQNHENdvjarakO7KGiZVkWHU0e9KGHskK7ifOWcU1pDwo67gMf +kBhvvNp5SLouvJOJVoU14VD+Fv9WlgnJUfefmbwnkxOnE4H+wUWKnx3xChwIF4VDU4X CWYA== X-Forwarded-Encrypted: i=1; AFNElJ/7NC9KK8Gja7BeICDIJr36AahXFXxHzr4JMYLkf3JoIVdYhllAG3WSCCLEM2ol5+29Eht1jO1VF26Zm8U=@vger.kernel.org X-Gm-Message-State: AOJu0Yy934n5sab329MYykogxMuxQ1ZFs00VQeiL6EowBowb78+S6/56 MPOwuMStojzuLAhgia/Qyi8JBBDj7GywKD+OgS3tzSddTbNZoETicp4fgJMd4lzOmxXDL2Ho2o9 oDpBuIp+U6g== X-Received: from dlbpv4.prod.google.com ([2002:a05:7023:904:b0:12d:b3cc:a807]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:ead1:b0:128:c9fa:4c32 with SMTP id a92af1059eb24-12c73fa7c10mr21126322c88.28.1777157492022; Sat, 25 Apr 2026 15:51:32 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:30 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-39-irogers@google.com> Subject: [PATCH v7 38/59] perf failed-syscalls-by-pid: Port failed-syscalls-by-pid to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Ported from tools/perf/scripts/python/failed-syscalls-by-pid.py to use the perf Python module API. Key changes: - Used perf.syscall_name() to resolve syscall names instead of legacy Util library. - Used standard collections.defaultdict for nested statistics aggregation. - Used errno.errorcode for resolving error strings. - Supported optional filtering by COMM or PID via command line arguments. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: Fixed Syscall Name Fallback: Handled the case where perf.syscall_name() returns None , falling back to the string representation of the syscall ID to avoid TypeError during string formatting. v7: - Removed dead code (unused self.unhandled dictionary). --- tools/perf/python/failed-syscalls-by-pid.py | 116 ++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100755 tools/perf/python/failed-syscalls-by-pid.py diff --git a/tools/perf/python/failed-syscalls-by-pid.py b/tools/perf/pytho= n/failed-syscalls-by-pid.py new file mode 100755 index 000000000000..eecd553cbf8f --- /dev/null +++ b/tools/perf/python/failed-syscalls-by-pid.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +Displays system-wide failed system call totals, broken down by pid. +If a [comm] or [pid] arg is specified, only syscalls called by it are disp= layed. + +Ported from tools/perf/scripts/python/failed-syscalls-by-pid.py +""" + +import argparse +from collections import defaultdict +import errno +from typing import Optional +import perf + + +def strerror(nr: int) -> str: + """Return error string for a given errno.""" + try: + return errno.errorcode[abs(nr)] + except KeyError: + return f"Unknown {nr} errno" + + +class SyscallAnalyzer: + """Analyzes failed syscalls and aggregates counts.""" + + def __init__(self, for_comm: Optional[str] =3D None, for_pid: Optional= [int] =3D None): + self.for_comm =3D for_comm + self.for_pid =3D for_pid + self.session: Optional[perf.session] =3D None + self.syscalls: dict[tuple[str, int, int, int], int] =3D defaultdic= t(int) + + def process_event(self, sample: perf.sample_event) -> None: + """Process a single sample event.""" + event_name =3D str(sample.evsel) + if "sys_exit" not in event_name: + return + + pid =3D sample.sample_pid + if hasattr(self, 'session') and self.session: + comm =3D self.session.find_thread(pid).comm() + else: + comm =3D "Unknown" + + if self.for_comm and comm !=3D self.for_comm: + return + if self.for_pid and pid !=3D self.for_pid: + return + + ret =3D getattr(sample, "ret", 0) + if ret < 0: + syscall_id =3D getattr(sample, "id", -1) + if syscall_id =3D=3D -1: + syscall_id =3D getattr(sample, "sys_id", -1) + + if syscall_id !=3D -1: + self.syscalls[(comm, pid, syscall_id, ret)] +=3D 1 + + def print_summary(self) -> None: + """Print aggregated statistics.""" + if self.for_comm is not None: + print(f"\nsyscall errors for {self.for_comm}:\n") + elif self.for_pid is not None: + print(f"\nsyscall errors for PID {self.for_pid}:\n") + else: + print("\nsyscall errors:\n") + + print(f"{'comm [pid]':<30} {'count':>10}") + print(f"{'-' * 30:<30} {'-' * 10:>10}") + + sorted_keys =3D sorted(self.syscalls.keys(), key=3Dlambda k: (k[0]= , k[1], k[2])) + current_comm_pid =3D None + for comm, pid, syscall_id, ret in sorted_keys: + if current_comm_pid !=3D (comm, pid): + print(f"\n{comm} [{pid}]") + current_comm_pid =3D (comm, pid) + try: + name =3D perf.syscall_name(syscall_id) or str(syscall_id) + except AttributeError: + name =3D str(syscall_id) + print(f" syscall: {name:<16}") + err_str =3D strerror(ret) + count =3D self.syscalls[(comm, pid, syscall_id, ret)] + print(f" err =3D {err_str:<20} {count:10d}") + + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser( + description=3D"Displays system-wide failed system call totals, bro= ken down by pid.") + ap.add_argument("-i", "--input", default=3D"perf.data", + help=3D"Input file name") + ap.add_argument("filter", nargs=3D"?", help=3D"COMM or PID to filter b= y") + args =3D ap.parse_args() + + F_COMM =3D None + F_PID =3D None + + if args.filter: + try: + F_PID =3D int(args.filter) + except ValueError: + F_COMM =3D args.filter + + analyzer =3D SyscallAnalyzer(F_COMM, F_PID) + + try: + print("Press control+C to stop and show the summary") + session =3D perf.session(perf.data(args.input), sample=3Danalyzer.= process_event) + analyzer.session =3D session + session.process_events() + analyzer.print_summary() + except KeyboardInterrupt: + analyzer.print_summary() + except Exception as e: + print(f"Error processing events: {e}") --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 559EB38F630 for ; Sat, 25 Apr 2026 22:51:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157497; cv=none; b=F3N+GkHW/TubqFRvJ5Ur6H3gHSlK6v4/GMiXqVCSo6nRjpAkVNG4nIq4CWq9bHDvqtx0hSd7yh1JPl9psSSOTCnGF3L56Bg+DdXixROWZYUP3LDj7Fj5ZOmCBrj36jnmrkvUlK4ziipEnlg0rv1NT7WNCtfoXCxubML7W623rLY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157497; c=relaxed/simple; bh=GzRR6Gnov3fqrvFJle/yaXwL70MgO0uq6ThA3T+Rmlo=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ShZjmEuPoUm98re7/xnzLzc6dv3cPMAoGutPCohiN9STXlvfaNt43Fq6WweF0mjiwgH22Sr5bltWXn2baX4jAIclXjaWBasSS95bctXlKtxEE4akSqItyHhW/TCwZHhqGNoLRlgN+Wp8a29x/h5jwq4JCO846bG7pGRMtctTCNc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=HqsdJiu2; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="HqsdJiu2" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-126e8ee6227so9133165c88.0 for ; Sat, 25 Apr 2026 15:51:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157495; x=1777762295; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=d6/DbS8+a0Bbu++hm0mIANNgnhHVu7X5dNuwNyZ3JIg=; b=HqsdJiu2O1R0w0nc3xLZD9Phv6QhFEURQp0Iuej1SjxM0dszV8a1qPR6cEi8gb0lDM wycMzDArw80nsDLk0A/DrsB885x0+cFO5W9RIJsXogsuvHdUcht8GahiYVXZdD5qfa9s +qpVrOxeJFSxE7yjJlK8eW/+bxTI64/adBzzFf/1DwZJrp4lrNAg70vd9rSJJlLhCdQo x7IY3T7yeLeyMwFGuxg97uwJD2X8fJaDYUWWfjlaxDCHxJF+qw6t9luDOxGXchZPfNxw 9LgGRRFJrn+sq1n0e4LrXgRDWIp82rC05ehiKwvNxBa92cjBwTslr0mM6DVCfYO/ObwX P9dA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157495; x=1777762295; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=d6/DbS8+a0Bbu++hm0mIANNgnhHVu7X5dNuwNyZ3JIg=; b=Sg8Tj04Z/yRqW9Y/n2nwACirPitCddmGgheSyI9d30BDU9IDPLzw4Aphm6Oefc8tWV jlQE8hNxRRhWB0HVE2wUu3iEPn/YeCxCLKxSCJFYyJPybc2nmWuBVwzEZ+Nghfsy9/ih RCDdpuN/mWG9ywKmE+xVqiuDWAVPv9e8z6tckTjKp94fpvK5V2VAAg3hbnFPqhQ0k5mo b5GpADXif8cDjzGykgBB9ZGL58gamAIXyTEyHMG26gFkthc/pUGo/VDIYtSY6wk9QkFw LCFcAttG6Fq5iJ+xSWbfJT1TlgCIGWciF+mOKwfQ1q+QD49zb5vJ7/uIeIhwqMmDV/fo EG0Q== X-Forwarded-Encrypted: i=1; AFNElJ9EFfKiviPuSQrqKylzBmRtfaLLXyUJKCNmU2IGLVuEGd6dXBfPk6FexELiKuJtl6pVgjU3XTmCFmByg7g=@vger.kernel.org X-Gm-Message-State: AOJu0YxtE9IFBoK1ocS9kOweKp+isB1hyMPmfzWXQmucCn/doWgVlj+f Tk0TV4d0YCIbTu43P/NDgyRLQ1i+9QJk5wfuArxU+Ckb/fQsW3XfVUczqwg4EmJPo5C3Yzg3Yav dVKhR6CL/BQ== X-Received: from dlbrl2.prod.google.com ([2002:a05:7022:f502:b0:12d:c3dd:4fba]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:6b8d:b0:12a:6a64:81d9 with SMTP id a92af1059eb24-12c73f723aamr19527958c88.13.1777157494450; Sat, 25 Apr 2026 15:51:34 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:31 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-40-irogers@google.com> Subject: [PATCH v7 39/59] perf intel-pt-events: Port intel-pt-events/libxed to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Ported from tools/perf/scripts/python/. - Refactored intel-pt-events.py to use a class structure to eliminate global state and improve maintainability. - Removed Python 2 compatibility checks. - Renamed methods in libxed.py to snake_case (Instruction -> instruction, SetMode -> set_mode, DisassembleOne -> disassemble_one) to comply with pylint. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Robustness in print_cbr : Added checks to ensure raw_buf is at least 12 bytes long and that the frequency divisor is not zero, avoiding struct.error and ZeroDivisionError . 2. Robustness in print_evt : Added buffer length checks in the loop to prevent struct.error if event data count exceeds available buffer. 3. Buffer Handling in disassem : Used ctypes.create_string_buffer(insn, 64) to properly initialize the buffer with raw bytes, preventing truncation on \x00 bytes. 4. Corrected Field Names: Reverted short names to sample_ip , sample_time , and sample_cpu across multiple methods. 5. Comm Resolution: Used session.process(sample.sample_pid).comm() to get the thread name, rather than failing back to "Unknown" . 6. Event Name Cleanup: Stripped evsel( and ) from event names. 7. Fixed Broken Pipe Handling: Prevented sys.stdout from being closed before exiting in the handler. 8. Eliminated Hardcoded Offset in libxed.py : Added xed_decoded_inst_get_length from the official LibXED API rather than relying on the hardcoded byte offset 166 . --- tools/perf/python/intel-pt-events.py | 435 +++++++++++++++++++++++++++ tools/perf/python/libxed.py | 122 ++++++++ 2 files changed, 557 insertions(+) create mode 100755 tools/perf/python/intel-pt-events.py create mode 100755 tools/perf/python/libxed.py diff --git a/tools/perf/python/intel-pt-events.py b/tools/perf/python/intel= -pt-events.py new file mode 100755 index 000000000000..19a0faec8f5f --- /dev/null +++ b/tools/perf/python/intel-pt-events.py @@ -0,0 +1,435 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +Print Intel PT Events including Power Events and PTWRITE. +Ported from tools/perf/scripts/python/intel-pt-events.py +""" + +import argparse +import contextlib +from ctypes import addressof, create_string_buffer +import io +import os +import struct +import sys +from typing import Any, Optional +import perf + +# Try to import LibXED from legacy directory if available in PYTHONPATH +try: + from libxed import LibXED # type: ignore +except ImportError: + LibXED =3D None # type: ignore + + +class IntelPTAnalyzer: + """Analyzes Intel PT events and prints details.""" + + def __init__(self, cfg: argparse.Namespace): + self.args =3D cfg + self.session: Optional[perf.session] =3D None + self.insn =3D False + self.src =3D False + self.source_file_name: Optional[str] =3D None + self.line_number: int =3D 0 + self.dso: Optional[str] =3D None + self.stash_dict: dict[int, list[str]] =3D {} + self.output: Any =3D None + self.output_pos: int =3D 0 + self.cpu: int =3D -1 + self.time: int =3D 0 + self.switch_str: dict[int, str] =3D {} + + if cfg.insn_trace: + print("Intel PT Instruction Trace") + self.insn =3D True + elif cfg.src_trace: + print("Intel PT Source Trace") + self.insn =3D True + self.src =3D True + else: + print("Intel PT Branch Trace, Power Events, Event Trace and PT= WRITE") + + self.disassembler: Any =3D None + if self.insn and LibXED is not None: + try: + self.disassembler =3D LibXED() + except Exception as e: + print(f"Failed to initialize LibXED: {e}") + self.disassembler =3D None + + def print_ptwrite(self, raw_buf: bytes) -> None: + """Print PTWRITE data.""" + data =3D struct.unpack_from(" None: + """Print CBR data.""" + if len(raw_buf) < 12: + return + data =3D struct.unpack_from(" None: + """Print MWAIT data.""" + data =3D struct.unpack_from("> 32) & 0x3 + print(f"hints: {hints:#x} extensions: {extensions:#x}", end=3D' ') + + def print_pwre(self, raw_buf: bytes) -> None: + """Print PWRE data.""" + data =3D struct.unpack_from("> 7) & 1 + cstate =3D (payload >> 12) & 0xf + subcstate =3D (payload >> 8) & 0xf + print(f"hw: {hw} cstate: {cstate} sub-cstate: {subcstate}", end=3D= ' ') + + def print_exstop(self, raw_buf: bytes) -> None: + """Print EXSTOP data.""" + data =3D struct.unpack_from(" None: + """Print PWRX data.""" + data =3D struct.unpack_from("> 4) & 0xf + wake_reason =3D (payload >> 8) & 0xf + print(f"deepest cstate: {deepest_cstate} last cstate: {last_cstate= } " + f"wake reason: {wake_reason:#x}", end=3D' ') + + def print_psb(self, raw_buf: bytes) -> None: + """Print PSB data.""" + data =3D struct.unpack_from(" None: + """Print EVT data.""" + glb_cfe =3D ["", "INTR", "IRET", "SMI", "RSM", "SIPI", "INIT", "VM= ENTRY", "VMEXIT", + "VMEXIT_INTR", "SHUTDOWN", "", "UINT", "UIRET"] + [""] = * 18 + glb_evd =3D ["", "PFA", "VMXQ", "VMXR"] + [""] * 60 + + data =3D struct.unpack_from("> 7 + vector =3D data[1] + evd_cnt =3D data[2] + s =3D glb_cfe[typ] + if s: + print(f" cfe: {s} IP: {ip_flag} vector: {vector}", end=3D' ') + else: + print(f" cfe: {typ} IP: {ip_flag} vector: {vector}", end=3D' ') + pos =3D 4 + for _ in range(evd_cnt): + if len(raw_buf) < pos + 16: + break + data =3D struct.unpack_from(" None: + """Print IFLAG data.""" + data =3D struct.unpack_from("{iflag} {s} branch", end=3D' ') + + def common_start_str(self, comm: str, sample: perf.sample_event) -> st= r: + """Return common start string for display.""" + ts =3D sample.sample_time + cpu =3D sample.sample_cpu + pid =3D sample.sample_pid + tid =3D sample.tid + machine_pid =3D getattr(sample, "machine_pid", 0) + if machine_pid: + vcpu =3D getattr(sample, "vcpu", -1) + return (f"VM:{machine_pid:5d} VCPU:{vcpu:03d} {comm:>16s} {pid= :5u}/{tid:<5u} " + f"[{cpu:03u}] {ts // 1000000000:9u}.{ts % 1000000000:0= 9u} ") + return (f"{comm:>16s} {pid:5u}/{tid:<5u} [{cpu:03u}] " + f"{ts // 1000000000:9u}.{ts % 1000000000:09u} ") + + def print_common_start(self, comm: str, sample: perf.sample_event, nam= e: str) -> None: + """Print common start info.""" + flags_disp =3D getattr(sample, "flags_disp", "") + print(self.common_start_str(comm, sample) + f"{name:>8s} {flags_d= isp:>21s}", end=3D' ') + + def print_instructions_start(self, comm: str, sample: perf.sample_even= t) -> None: + """Print instructions start info.""" + flags =3D getattr(sample, "flags_disp", "") + if "x" in flags: + print(self.common_start_str(comm, sample) + "x", end=3D' ') + else: + print(self.common_start_str(comm, sample), end=3D' ') + + def disassem(self, insn: bytes, ip: int) -> tuple[int, str]: + """Disassemble instruction using LibXED.""" + inst =3D self.disassembler.instruction() + self.disassembler.set_mode(inst, 0) # Assume 64-bit + buf =3D create_string_buffer(insn, 64) + return self.disassembler.disassemble_one(inst, addressof(buf), len= (insn), ip) + + def print_common_ip(self, sample: perf.sample_event, symbol: str, dso:= str) -> None: + """Print IP and symbol info.""" + ip =3D sample.sample_ip + offs =3D f"+{sample.symoff:#x}" if hasattr(sample, "symoff") else = "" + cyc_cnt =3D getattr(sample, "cyc_cnt", 0) + if cyc_cnt: + insn_cnt =3D getattr(sample, "insn_cnt", 0) + ipc_str =3D f" IPC: {insn_cnt / cyc_cnt:#.2f} ({insn_cnt}/{cy= c_cnt})" + else: + ipc_str =3D "" + + if self.insn and self.disassembler is not None: + try: + insn =3D sample.insn() + except AttributeError: + insn =3D None + if insn: + cnt, text =3D self.disassem(insn, ip) + byte_str =3D (f"{ip:x}").rjust(16) + for k in range(cnt): + byte_str +=3D f" {insn[k]:02x}" + print(f"{byte_str:-40s} {text:-30s}", end=3D' ') + print(f"{symbol}{offs} ({dso})", end=3D' ') + else: + print(f"{ip:16x} {symbol}{offs} ({dso})", end=3D' ') + + addr_correlates_sym =3D getattr(sample, "addr_correlates_sym", Fal= se) + if addr_correlates_sym: + addr =3D sample.addr + addr_dso =3D getattr(sample, "addr_dso", "[unknown]") + addr_symbol =3D getattr(sample, "addr_symbol", "[unknown]") + addr_offs =3D f"+{sample.addr_symoff:#x}" if hasattr(sample, "= addr_symoff") else "" + print(f"=3D> {addr:x} {addr_symbol}{addr_offs} ({addr_dso}){ip= c_str}") + else: + print(ipc_str) + + def print_srccode(self, comm: str, sample: perf.sample_event, + symbol: str, dso: str, with_insn: bool) -> None: + """Print source code info.""" + ip =3D sample.sample_ip + if symbol =3D=3D "[unknown]": + start_str =3D self.common_start_str(comm, sample) + (f"{ip:x}"= ).rjust(16).ljust(40) + else: + offs =3D f"+{sample.symoff:#x}" if hasattr(sample, "symoff") e= lse "" + start_str =3D self.common_start_str(comm, sample) + (symbol + = offs).ljust(40) + + if with_insn and self.insn and self.disassembler is not None: + try: + insn =3D sample.insn() + except AttributeError: + insn =3D None + if insn: + _, text =3D self.disassem(insn, ip) + start_str +=3D text.ljust(30) + + try: + source_file_name, line_number, source_line =3D sample.srccode() + except (AttributeError, ValueError): + source_file_name, line_number, source_line =3D None, 0, None + + if source_file_name: + if self.line_number =3D=3D line_number and self.source_file_na= me =3D=3D source_file_name: + src_str =3D "" + else: + if len(source_file_name) > 40: + src_file =3D ("..." + source_file_name[-37:]) + " " + else: + src_file =3D source_file_name.ljust(41) + if source_line is None: + src_str =3D src_file + str(line_number).rjust(4) + " <= source not found>" + else: + src_str =3D src_file + str(line_number).rjust(4) + " "= + source_line + self.dso =3D None + elif dso =3D=3D self.dso: + src_str =3D "" + else: + src_str =3D dso + self.dso =3D dso + + self.line_number =3D line_number + self.source_file_name =3D source_file_name + print(start_str, src_str) + + def do_process_event(self, sample: perf.sample_event) -> None: + """Process event and print info.""" + comm =3D "Unknown" + if hasattr(self, 'session') and self.session: + try: + comm =3D self.session.find_thread(sample.sample_pid).comm() + except Exception: + pass + name =3D getattr(sample.evsel, 'name', str(sample.evsel)) + if name.startswith("evsel("): + name =3D name[6:-1] + dso =3D getattr(sample, "dso", "[unknown]") + symbol =3D getattr(sample, "symbol", "[unknown]") + + cpu =3D sample.sample_cpu + if cpu in self.switch_str: + print(self.switch_str[cpu]) + del self.switch_str[cpu] + + try: + raw_buf =3D sample.raw_buf + except AttributeError: + raw_buf =3D b"" + + if name.startswith("instructions"): + if self.src: + self.print_srccode(comm, sample, symbol, dso, True) + else: + self.print_instructions_start(comm, sample) + self.print_common_ip(sample, symbol, dso) + elif name.startswith("branches"): + if self.src: + self.print_srccode(comm, sample, symbol, dso, False) + else: + self.print_common_start(comm, sample, name) + self.print_common_ip(sample, symbol, dso) + elif name =3D=3D "ptwrite": + self.print_common_start(comm, sample, name) + self.print_ptwrite(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name =3D=3D "cbr": + self.print_common_start(comm, sample, name) + self.print_cbr(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name =3D=3D "mwait": + self.print_common_start(comm, sample, name) + self.print_mwait(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name =3D=3D "pwre": + self.print_common_start(comm, sample, name) + self.print_pwre(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name =3D=3D "exstop": + self.print_common_start(comm, sample, name) + self.print_exstop(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name =3D=3D "pwrx": + self.print_common_start(comm, sample, name) + self.print_pwrx(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name =3D=3D "psb": + self.print_common_start(comm, sample, name) + self.print_psb(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name =3D=3D "evt": + self.print_common_start(comm, sample, name) + self.print_evt(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name =3D=3D "iflag": + self.print_common_start(comm, sample, name) + self.print_iflag(raw_buf) + self.print_common_ip(sample, symbol, dso) + else: + self.print_common_start(comm, sample, name) + self.print_common_ip(sample, symbol, dso) + + def interleave_events(self, sample: perf.sample_event) -> None: + """Interleave output to avoid garbled lines from different CPUs.""" + self.cpu =3D sample.sample_cpu + ts =3D sample.sample_time + + if self.time !=3D ts: + self.time =3D ts + self.flush_stashed_output() + + self.output_pos =3D 0 + with contextlib.redirect_stdout(io.StringIO()) as self.output: + self.do_process_event(sample) + + self.stash_output() + + def stash_output(self) -> None: + """Stash output for later flushing.""" + output_str =3D self.output.getvalue()[self.output_pos:] + n =3D len(output_str) + if n: + self.output_pos +=3D n + if self.cpu not in self.stash_dict: + self.stash_dict[self.cpu] =3D [] + self.stash_dict[self.cpu].append(output_str) + if len(self.stash_dict[self.cpu]) > 1000: + self.flush_stashed_output() + + def flush_stashed_output(self) -> None: + """Flush stashed output.""" + while self.stash_dict: + cpus =3D list(self.stash_dict.keys()) + for cpu in cpus: + items =3D self.stash_dict[cpu] + countdown =3D self.args.interleave + while len(items) and countdown: + sys.stdout.write(items[0]) + del items[0] + countdown -=3D 1 + if not items: + del self.stash_dict[cpu] + + def process_event(self, sample: perf.sample_event) -> None: + """Wrapper to handle interleaving and exceptions.""" + try: + if self.args.interleave: + self.interleave_events(sample) + else: + self.do_process_event(sample) + except BrokenPipeError: + # Stop python printing broken pipe errors and traceback + sys.stdout =3D open(os.devnull, 'w', encoding=3D'utf-8') + sys.exit(1) + + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser() + ap.add_argument("-i", "--input", default=3D"perf.data", help=3D"Input = file name") + ap.add_argument("--insn-trace", action=3D'store_true') + ap.add_argument("--src-trace", action=3D'store_true') + ap.add_argument("--all-switch-events", action=3D'store_true') + ap.add_argument("--interleave", type=3Dint, nargs=3D'?', const=3D4, de= fault=3D0) + args =3D ap.parse_args() + + analyzer =3D IntelPTAnalyzer(args) + + try: + session =3D perf.session(perf.data(args.input), sample=3Danalyzer.= process_event) + analyzer.session =3D session + session.process_events() + if args.interleave: + analyzer.flush_stashed_output() + print("End") + except KeyboardInterrupt: + if args.interleave: + analyzer.flush_stashed_output() + print("End") + except Exception as e: + print(f"Error processing events: {e}") diff --git a/tools/perf/python/libxed.py b/tools/perf/python/libxed.py new file mode 100755 index 000000000000..0e622e6959c2 --- /dev/null +++ b/tools/perf/python/libxed.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +Python wrapper for libxed.so +Ported from tools/perf/scripts/python/libxed.py +""" + +from ctypes import CDLL, Structure, create_string_buffer, addressof, sizeo= f, \ + c_void_p, c_byte, c_int, c_uint, c_ulonglong + +# To use Intel XED, libxed.so must be present. To build and install +# libxed.so: +# git clone https://github.com/intelxed/mbuild.git mbuild +# git clone https://github.com/intelxed/xed +# cd xed +# ./mfile.py --share +# sudo ./mfile.py --prefix=3D/usr/local install +# sudo ldconfig +# + + +class XedStateT(Structure): + """xed_state_t structure.""" + _fields_ =3D [ + ("mode", c_int), + ("width", c_int) + ] + + +class XEDInstruction(): + """Represents a decoded instruction.""" + + def __init__(self, libxed): + # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to al= low for future expansion + xedd_t =3D c_byte * 512 + self.xedd =3D xedd_t() + self.xedp =3D addressof(self.xedd) + libxed.xed_decoded_inst_zero(self.xedp) + self.state =3D XedStateT() + self.statep =3D addressof(self.state) + # Buffer for disassembled instruction text + self.buffer =3D create_string_buffer(256) + self.bufferp =3D addressof(self.buffer) + + +class LibXED(): + """Wrapper for libxed.so.""" + + def __init__(self): + try: + self.libxed =3D CDLL("libxed.so") + except OSError: + self.libxed =3D None + if not self.libxed: + try: + self.libxed =3D CDLL("/usr/local/lib/libxed.so") + except OSError: + self.libxed =3D None + + if not self.libxed: + raise ImportError("libxed.so not found. Please install Intel X= ED.") + + self.xed_tables_init =3D self.libxed.xed_tables_init + self.xed_tables_init.restype =3D None + self.xed_tables_init.argtypes =3D [] + + self.xed_decoded_inst_zero =3D self.libxed.xed_decoded_inst_zero + self.xed_decoded_inst_zero.restype =3D None + self.xed_decoded_inst_zero.argtypes =3D [c_void_p] + + self.xed_operand_values_set_mode =3D self.libxed.xed_operand_value= s_set_mode + self.xed_operand_values_set_mode.restype =3D None + self.xed_operand_values_set_mode.argtypes =3D [c_void_p, c_void_p] + + self.xed_decoded_inst_zero_keep_mode =3D self.libxed.xed_decoded_i= nst_zero_keep_mode + self.xed_decoded_inst_zero_keep_mode.restype =3D None + self.xed_decoded_inst_zero_keep_mode.argtypes =3D [c_void_p] + + self.xed_decode =3D self.libxed.xed_decode + self.xed_decode.restype =3D c_int + self.xed_decode.argtypes =3D [c_void_p, c_void_p, c_uint] + + self.xed_format_context =3D self.libxed.xed_format_context + self.xed_format_context.restype =3D c_uint + self.xed_format_context.argtypes =3D [ + c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_voi= d_p + ] + + self.xed_decoded_inst_get_length =3D self.libxed.xed_decoded_inst_= get_length + self.xed_decoded_inst_get_length.restype =3D c_uint + self.xed_decoded_inst_get_length.argtypes =3D [c_void_p] + + self.xed_tables_init() + + def instruction(self): + """Create a new XEDInstruction.""" + return XEDInstruction(self) + + def set_mode(self, inst, mode): + """Set 32-bit or 64-bit mode.""" + if mode: + inst.state.mode =3D 4 # 32-bit + inst.state.width =3D 4 # 4 bytes + else: + inst.state.mode =3D 1 # 64-bit + inst.state.width =3D 8 # 8 bytes + self.xed_operand_values_set_mode(inst.xedp, inst.statep) + + def disassemble_one(self, inst, bytes_ptr, bytes_cnt, ip): + """Disassemble one instruction.""" + self.xed_decoded_inst_zero_keep_mode(inst.xedp) + err =3D self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) + if err: + return 0, "" + # Use AT&T mode (2), alternative is Intel (3) + ok =3D self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(= inst.buffer), ip, 0, 0) + if not ok: + return 0, "" + + result =3D inst.buffer.value.decode('utf-8') + # Return instruction length and the disassembled instruction text + return self.xed_decoded_inst_get_length(inst.xedp), result --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8E88A3AB26B for ; Sat, 25 Apr 2026 22:51:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157499; cv=none; b=PucQsaItdl6NH9MZ1XF+N5OEKHCZmmdRdF5eyDUiuMDftZQf6df1p6bZqQqx6WPp+N7aCWVhyqGdG0f52eHGzY8l+zMJ/CsF9wqjZlJX2Thay1IaxULc259prcDZl8P3IWVyem3Jb9gZo/uirUbUWqeKZWxEsjwa6Of94RsPe5Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157499; c=relaxed/simple; bh=L2Q2Gv1SqkcOuddrjKJDgvkqmuTF9MQuGmwjw7vhz/I=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=kNMSov/SypHXuhFSiI4u0KaC6MX6vBBty4G12nQc6d1FaQ65/WWOld5TS15LXfeo0KOveAb9CDaQU9ewaoPzmrezCDcBdAI/bydPPixLEFR/plkMUKv5+Mzh+Wa5PBwn/CLf0tlMhmMZNbDD25s11paYwCh7zfdscH2QclM2lBo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=NlmMz/nU; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="NlmMz/nU" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2bda35eab74so7739011eec.0 for ; Sat, 25 Apr 2026 15:51:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157497; x=1777762297; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=P0hpHM6Q0qnz4rILJ4wjp6r0pWm8+LVx9WiZ78eUWKU=; b=NlmMz/nUtWKF/FKJeB3mCN/nBx28g9GL9Aqrhq5ZPDM5icgLW4n/7Gde9M/z9GaYSt LIWD6SrTn5S/GIXEsd5j2Aq8htfAl4pquy7mONHl26hiteWz+fHV8ELSQThqU0lF0F45 dEPgb/zsPSQwecGB9GXFPNVyQ9L9D7NhMdU6hYxy8Yzo2UFLMwWXIf1cM7Rf3vc0amSa KENnuMoN4T/w52qn/EDNLKJtmQxS0nt9DgCMI+brKRlkKobL+22i5N7ZEhoWRvV2WL5z jjR2Z+olw+nEopdKXwpB+TQlMh9ODULy159Gfd+GK7J3Ge9Ix53UChJinD8F0s9LkZmw a5gw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157497; x=1777762297; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=P0hpHM6Q0qnz4rILJ4wjp6r0pWm8+LVx9WiZ78eUWKU=; b=hVLqsqJP8/xhpgkLu+MbRz7bSbV1VBbGw6DyjfrYT+vRtX1UAxRf564C3N8CvRsT00 Z5uSZOMzb5odV9fuTbEpTibtFF791dV9uDGd3Yzcs5dQyzWU34h5g4z+vyuhIyJ6TM2Z 1r6a6BvlKAgNTjeGP6R9jnqEgKOFKv9Toh4JHxAXj0uhCOaMtq0vWBb8PJrzBFNQlTC3 ZbncC+mbs8LUMndl6KV4DcXxqAR0pEjl/pb3LYgX+QpWUyMjlvoldNI3tsd33VP1szVr k3tgXF/5NBlBthXJpy/6Qidsl3PZHsC+ZOBrkqLVmgxKPSMCB6MG6sx+x9XseqUaOiii LwkA== X-Forwarded-Encrypted: i=1; AFNElJ+c6M/ua6IzrmIxOP9DVMaykW23MJv4g4FsQPzHDJM5csx48c7yC/FXuy8645Mqae71y/R4oC9A371pdgM=@vger.kernel.org X-Gm-Message-State: AOJu0YzOITHVBFsXhXMV0qfh+ezi2pd/cO+JXWFK/bLT5qgOMYexpdNt UEs9rn8EMG+8SAHRUWuYLH4+UQPzhNKMIbycrVLwdB+k8sdfE/3RV6SYHcrTebIzRjlvBbUs65Z 33bvyjOlOHQ== X-Received: from dybrt7.prod.google.com ([2002:a05:7301:4707:b0:2d8:e988:4a97]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:693c:3011:b0:2df:7882:1cf3 with SMTP id 5a478bee46e88-2e42c15cf81mr16379113eec.2.1777157496418; Sat, 25 Apr 2026 15:51:36 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:32 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-41-irogers@google.com> Subject: [PATCH v7 40/59] perf net_dropmonitor: Port net_dropmonitor to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Ported from tools/perf/scripts/python/. - Refactored the script to use a class structure (DropMonitor) to encapsulate state. - Used perf.session for event processing instead of legacy global handlers. - Maintained the manual /proc/kallsyms reading and binary search for symbol resolution as in the original script. - Cleaned up Python 2 compatibility artifacts. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Fixed Sorting of Locations: Kept location as an integer in the drop_log dictionary keys so that they are sorted numerically rather than lexicographically when generating the report. 2. Fixed Interrupt Handling: Moved the call to get_kallsyms_table() and print_drop_table() outside the try-except block. This ensures that the reporting phase happens exactly once, even if the user interrupts the trace with Ctrl-C. --- tools/perf/python/net_dropmonitor.py | 58 ++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100755 tools/perf/python/net_dropmonitor.py diff --git a/tools/perf/python/net_dropmonitor.py b/tools/perf/python/net_d= ropmonitor.py new file mode 100755 index 000000000000..25ea2a66ed3c --- /dev/null +++ b/tools/perf/python/net_dropmonitor.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +Monitor the system for dropped packets and produce a report of drop locati= ons and counts. +Ported from tools/perf/scripts/python/net_dropmonitor.py +""" + +import argparse +from collections import defaultdict +import sys +import perf + + +class DropMonitor: + """Monitors dropped packets and aggregates counts by location.""" + + def __init__(self): + self.drop_log: dict[tuple[str, int], int] =3D defaultdict(int) + self.unhandled: dict[str, int] =3D defaultdict(int) + + def print_drop_table(self) -> None: + """Print aggregated results.""" + print(f"{'LOCATION':>25} {'OFFSET':>25} {'COUNT':>25}") + for (sym, off) in sorted(self.drop_log.keys()): + print(f"{sym:>25} {off:>25d} {self.drop_log[(sym, off)]:>25d}") + + def process_event(self, sample: perf.sample_event) -> None: + """Process a single sample event.""" + if str(sample.evsel) !=3D "evsel(skb:kfree_skb)": + return + + try: + symbol =3D getattr(sample, "symbol", "[unknown]") + symoff =3D getattr(sample, "symoff", 0) + self.drop_log[(symbol, symoff)] +=3D 1 + except AttributeError: + self.unhandled[str(sample.evsel)] +=3D 1 + + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser( + description=3D"Monitor the system for dropped packets and produce = a " + "report of drop locations and counts.") + ap.add_argument("-i", "--input", default=3D"perf.data", help=3D"Input = file name") + args =3D ap.parse_args() + + monitor =3D DropMonitor() + + try: + session =3D perf.session(perf.data(args.input), sample=3Dmonitor.p= rocess_event) + session.process_events() + except KeyboardInterrupt: + print("\nStopping trace...") + except Exception as e: + print(f"Error processing events: {e}") + sys.exit(1) + + monitor.print_drop_table() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 91CD93AC0D0 for ; Sat, 25 Apr 2026 22:51:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157501; cv=none; b=ed79M9kz+sBg9bo+Cpxj2Qv+aiLNbpq2K4F3mu4FpFTGRobyLFdh696FQaQI+2ORhuDfaKuhcrrcjoC9HFlholLo2PgBZ0RRtvWvGuh5zvDPIi/BM0hH61Tqe6+m69uhq/6RrTivJFTeOUrj621FG0VLbk6T2D4UbGrXj3r6HAQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157501; c=relaxed/simple; bh=tLESZX2sDjjUwLqs9zO5ThuefSH3p+de6YiZLYWnduM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=r3QEwK3mTFh9uCnaODAN7yuEioPFP7bcY1HL1out+IoR7lSP/+9S0giAtg/l62H99CS9haoENDfnwY62A7HFgczwDOitpolnVfYTatHLT6+rshMpI7NGKxqG9LT/p1Ow3cx5wTOAvmFdji5TvEYHo1RgXKQGOt2IvFvtOz/xazE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=UD+9Dq1k; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="UD+9Dq1k" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2c16233ee11so12493185eec.1 for ; Sat, 25 Apr 2026 15:51:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157499; x=1777762299; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=ph7R9L13qRaJL3QC1gV+rLNWu/VKavX3EHLSwKF2rZc=; b=UD+9Dq1k7g/8arHuSUJvoJEE5Abg+nLtMP9rkYl7k/zccGySz8hkeuQXzTXkOo7POy vziiagHJRrqiUFrVLUH+Aj0a2FIzeXGo8KIOTBWqrFnjQu0z62rQ7bMKLNfHFQmMzS5/ uE02oyuQyTjiaTIlj4lto3HGGV+qiItBcqEkt/24GqqnTqby2dr1V0l5QLusIhj88uqD RfhDt06tw7T8WvLpWkY+4IR5AsNP5/q2Ih7pODFP3yrYeKFAI6fn3o7NcdtDiCshI8lL JgYjgCjTAVtWhoIydYqTHztonB23H3rtXWocF3lFktkGdl3p66SMwlmW1XLQsuGtOBig kgVQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157499; x=1777762299; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=ph7R9L13qRaJL3QC1gV+rLNWu/VKavX3EHLSwKF2rZc=; b=E8SlAGdRkigv/WiAM4SJtWQh1Mx8bSjkEbAp17U69gBkIUYQVRBGCK4nRnlXhiNb98 5dc+mMOOYoFkxLJueWQJD26FUWm4L138i+N16Z2JcM9AeVXei0B1dpYjbnPhB7lCh3nx l6ODQQ1cCG6P0NbbjylUQUUCXBO0IoaEolaxIm3gA1Zx3OLpnqZgH9DtAbN7AmAb4NE3 clnFbdnBbIdAikQOxtLncRlw+CaCedtEvh2YUNtUmdrw6K3yb9OUwJjsADIdozTYz9gC miKxUChU2/HJdBpAwNxp7u7ycSZohtfINhmW9eVJPYOVZC9twH+OGPqUOpn8nAx+1iIt t8xg== X-Forwarded-Encrypted: i=1; AFNElJ/MZscFS82raa2BPORlqACk/tlXsNjponujcnG9P1n2flzjNDKfu34wK+T1x18hBv2oFZl9u019fJhAimI=@vger.kernel.org X-Gm-Message-State: AOJu0YzfhGy/GWo4aOMG4fxsU4n0claei9Gz/xQYwzvbfxAcFbYytYpo WOZ6YxpkCgwZYL7Pzd/zy4x6MPATvXcDyUJ6lTBxvgJNCEJKfHsIADBIU0rq3JZcYK7b66L+emq p48wC6hd35w== X-Received: from dybor13.prod.google.com ([2002:a05:7301:1f0d:b0:2d8:4599:1d20]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:693c:40c9:b0:2e6:ff79:e344 with SMTP id 5a478bee46e88-2e6ff7a04bemr13956737eec.9.1777157498644; Sat, 25 Apr 2026 15:51:38 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:33 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-42-irogers@google.com> Subject: [PATCH v7 41/59] perf netdev-times: Port netdev-times to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Ported from tools/perf/scripts/python/. - Refactored the script to use a class structure (NetDevTimesAnalyzer) to encapsulate state. - Used perf.session for event collection and processed them in time order at the end to match legacy behavior. - Extracted tracepoint fields directly from sample attributes. - Moved format string constants to module level. - Cleaned up Python 2 compatibility artifacts (like cmp_to_key). Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: Corrected Field Names: Fixed getattr calls for skblen and dev_name to use "len" and "name" respectively, as exposed by the actual tracepoints. --- tools/perf/python/netdev-times.py | 472 ++++++++++++++++++++++++++++++ 1 file changed, 472 insertions(+) create mode 100755 tools/perf/python/netdev-times.py diff --git a/tools/perf/python/netdev-times.py b/tools/perf/python/netdev-t= imes.py new file mode 100755 index 000000000000..3fe46b4e7f21 --- /dev/null +++ b/tools/perf/python/netdev-times.py @@ -0,0 +1,472 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +Display a process of packets and processed time. +It helps us to investigate networking or network device. + +Ported from tools/perf/scripts/python/netdev-times.py +""" + +import argparse +from collections import defaultdict +import sys +from typing import Optional +import perf + +# Format for displaying rx packet processing +PF_IRQ_ENTRY =3D " irq_entry(+%.3fmsec irq=3D%d:%s)" +PF_SOFT_ENTRY =3D " softirq_entry(+%.3fmsec)" +PF_NAPI_POLL =3D " napi_poll_exit(+%.3fmsec %s)" +PF_JOINT =3D " |" +PF_WJOINT =3D " | |" +PF_NET_RECV =3D " |---netif_receive_skb(+%.3fmsec skb=3D%x len=3D%= d)" +PF_NET_RX =3D " |---netif_rx(+%.3fmsec skb=3D%x)" +PF_CPY_DGRAM =3D " | skb_copy_datagram_iovec(+%.3fmsec %d:%s)" +PF_KFREE_SKB =3D " | kfree_skb(+%.3fmsec location=3D%x)" +PF_CONS_SKB =3D " | consume_skb(+%.3fmsec)" + + +class NetDevTimesAnalyzer: + """Analyzes network device events and prints charts.""" + + def __init__(self, cfg: argparse.Namespace): + self.args =3D cfg + self.session: Optional[perf.session] =3D None + self.show_tx =3D cfg.tx or (not cfg.tx and not cfg.rx) + self.show_rx =3D cfg.rx or (not cfg.tx and not cfg.rx) + self.dev =3D cfg.dev + self.debug =3D cfg.debug + self.buffer_budget =3D 65536 + self.irq_dic: dict[int, list[dict]] =3D defaultdict(list) + self.net_rx_dic: dict[int, dict] =3D {} + self.receive_hunk_list: list[dict] =3D [] + self.rx_skb_list: list[dict] =3D [] + self.tx_queue_list: list[dict] =3D [] + self.tx_xmit_list: list[dict] =3D [] + self.tx_free_list: list[dict] =3D [] + + self.buffer_budget =3D 65536 + self.of_count_rx_skb_list =3D 0 + self.of_count_tx_queue_list =3D 0 + self.of_count_tx_xmit_list =3D 0 + + def diff_msec(self, src: int, dst: int) -> float: + """Calculate a time interval(msec) from src(nsec) to dst(nsec).""" + return (dst - src) / 1000000.0 + + def print_transmit(self, hunk: dict) -> None: + """Display a process of transmitting a packet.""" + if self.dev and hunk['dev'].find(self.dev) < 0: + return + queue_t_sec =3D hunk['queue_t'] // 1000000000 + queue_t_usec =3D hunk['queue_t'] % 1000000000 // 1000 + print(f"{hunk['dev']:7s} {hunk['len']:5d} " + f"{queue_t_sec:6d}.{queue_t_usec:06d}sec " + f"{self.diff_msec(hunk['queue_t'], hunk['xmit_t']):12.3f}mse= c " + f"{self.diff_msec(hunk['xmit_t'], hunk['free_t']):12.3f}msec= ") + + def print_receive(self, hunk: dict) -> None: + """Display a process of received packets and interrupts.""" + show_hunk =3D False + irq_list =3D hunk['irq_list'] + if not irq_list: + return + cpu =3D irq_list[0]['cpu'] + base_t =3D irq_list[0]['irq_ent_t'] + + if self.dev: + for irq in irq_list: + if irq['name'].find(self.dev) >=3D 0: + show_hunk =3D True + break + else: + show_hunk =3D True + + if not show_hunk: + return + + base_t_sec =3D base_t // 1000000000 + base_t_usec =3D base_t % 1000000000 // 1000 + print(f"{base_t_sec}.{base_t_usec:06d}sec cpu=3D{cpu}") + for irq in irq_list: + print(PF_IRQ_ENTRY % + (self.diff_msec(base_t, irq['irq_ent_t']), + irq['irq'], irq['name'])) + print(PF_JOINT) + irq_event_list =3D irq['event_list'] + for irq_event in irq_event_list: + if irq_event['event'] =3D=3D 'netif_rx': + print(PF_NET_RX % + (self.diff_msec(base_t, irq_event['time']), + irq_event['skbaddr'])) + print(PF_JOINT) + + print(PF_SOFT_ENTRY % self.diff_msec(base_t, hunk['sirq_ent_t'])) + print(PF_JOINT) + event_list =3D hunk['event_list'] + for i, event in enumerate(event_list): + if event['event_name'] =3D=3D 'napi_poll': + print(PF_NAPI_POLL % + (self.diff_msec(base_t, event['event_t']), + event['dev'])) + if i =3D=3D len(event_list) - 1: + print("") + else: + print(PF_JOINT) + else: + print(PF_NET_RECV % + (self.diff_msec(base_t, event['event_t']), + event['skbaddr'], + event['len'])) + if 'comm' in event: + print(PF_WJOINT) + print(PF_CPY_DGRAM % + (self.diff_msec(base_t, event['comm_t']), + event['pid'], event['comm'])) + elif 'handle' in event: + print(PF_WJOINT) + if event['handle'] =3D=3D "kfree_skb": + print(PF_KFREE_SKB % + (self.diff_msec(base_t, event['comm_t']), + event['location'])) + elif event['handle'] =3D=3D "consume_skb": + print(PF_CONS_SKB % + self.diff_msec(base_t, event['comm_t'])) + print(PF_JOINT) + + def handle_irq_handler_entry(self, event: dict) -> None: + """Handle irq:irq_handler_entry event.""" + time =3D event['time'] + cpu =3D event['cpu'] + irq =3D event['irq'] + irq_name =3D event['irq_name'] + irq_record =3D {'irq': irq, 'name': irq_name, 'cpu': cpu, + 'irq_ent_t': time, 'event_list': []} + self.irq_dic[cpu].append(irq_record) + + def handle_irq_handler_exit(self, event: dict) -> None: + """Handle irq:irq_handler_exit event.""" + time =3D event['time'] + cpu =3D event['cpu'] + irq =3D event['irq'] + if cpu not in self.irq_dic or not self.irq_dic[cpu]: + return + irq_record =3D self.irq_dic[cpu].pop() + if irq !=3D irq_record['irq']: + return + irq_record['irq_ext_t'] =3D time + # if an irq doesn't include NET_RX softirq, drop. + if irq_record['event_list']: + self.irq_dic[cpu].append(irq_record) + + def handle_irq_softirq_raise(self, event: dict) -> None: + """Handle irq:softirq_raise event.""" + time =3D event['time'] + cpu =3D event['cpu'] + if cpu not in self.irq_dic or not self.irq_dic[cpu]: + return + irq_record =3D self.irq_dic[cpu].pop() + irq_record['event_list'].append({'time': time, 'event': 'sirq_rais= e'}) + self.irq_dic[cpu].append(irq_record) + + def handle_irq_softirq_entry(self, event: dict) -> None: + """Handle irq:softirq_entry event.""" + time =3D event['time'] + cpu =3D event['cpu'] + self.net_rx_dic[cpu] =3D {'sirq_ent_t': time, 'event_list': []} + + def handle_irq_softirq_exit(self, event: dict) -> None: + """Handle irq:softirq_exit event.""" + time =3D event['time'] + cpu =3D event['cpu'] + irq_list =3D [] + event_list =3D [] + sirq_ent_t =3D None + + if cpu in self.irq_dic: + irq_list =3D self.irq_dic[cpu] + del self.irq_dic[cpu] + if cpu in self.net_rx_dic: + sirq_ent_t =3D self.net_rx_dic[cpu]['sirq_ent_t'] + event_list =3D self.net_rx_dic[cpu]['event_list'] + del self.net_rx_dic[cpu] + if not irq_list or not event_list or sirq_ent_t is None: + return + rec_data =3D {'sirq_ent_t': sirq_ent_t, 'sirq_ext_t': time, + 'irq_list': irq_list, 'event_list': event_list} + self.receive_hunk_list.append(rec_data) + + def handle_napi_poll(self, event: dict) -> None: + """Handle napi:napi_poll event.""" + time =3D event['time'] + cpu =3D event['cpu'] + dev_name =3D event['dev_name'] + work =3D event['work'] + budget =3D event['budget'] + if cpu in self.net_rx_dic: + event_list =3D self.net_rx_dic[cpu]['event_list'] + rec_data =3D {'event_name': 'napi_poll', + 'dev': dev_name, 'event_t': time, + 'work': work, 'budget': budget} + event_list.append(rec_data) + + def handle_netif_rx(self, event: dict) -> None: + """Handle net:netif_rx event.""" + time =3D event['time'] + cpu =3D event['cpu'] + skbaddr =3D event['skbaddr'] + skblen =3D event['skblen'] + dev_name =3D event['dev_name'] + if cpu not in self.irq_dic or not self.irq_dic[cpu]: + return + irq_record =3D self.irq_dic[cpu].pop() + irq_record['event_list'].append({'time': time, 'event': 'netif_rx', + 'skbaddr': skbaddr, 'skblen': skb= len, + 'dev_name': dev_name}) + self.irq_dic[cpu].append(irq_record) + + def handle_netif_receive_skb(self, event: dict) -> None: + """Handle net:netif_receive_skb event.""" + time =3D event['time'] + cpu =3D event['cpu'] + skbaddr =3D event['skbaddr'] + skblen =3D event['skblen'] + if cpu in self.net_rx_dic: + rec_data =3D {'event_name': 'netif_receive_skb', + 'event_t': time, 'skbaddr': skbaddr, 'len': skblen} + event_list =3D self.net_rx_dic[cpu]['event_list'] + event_list.append(rec_data) + self.rx_skb_list.insert(0, rec_data) + if len(self.rx_skb_list) > self.buffer_budget: + self.rx_skb_list.pop() + self.of_count_rx_skb_list +=3D 1 + + def handle_net_dev_queue(self, event: dict) -> None: + """Handle net:net_dev_queue event.""" + time =3D event['time'] + skbaddr =3D event['skbaddr'] + skblen =3D event['skblen'] + dev_name =3D event['dev_name'] + skb =3D {'dev': dev_name, 'skbaddr': skbaddr, 'len': skblen, 'queu= e_t': time} + self.tx_queue_list.insert(0, skb) + if len(self.tx_queue_list) > self.buffer_budget: + self.tx_queue_list.pop() + self.of_count_tx_queue_list +=3D 1 + + def handle_net_dev_xmit(self, event: dict) -> None: + """Handle net:net_dev_xmit event.""" + time =3D event['time'] + skbaddr =3D event['skbaddr'] + rc =3D event['rc'] + if rc =3D=3D 0: # NETDEV_TX_OK + for i, skb in enumerate(self.tx_queue_list): + if skb['skbaddr'] =3D=3D skbaddr: + skb['xmit_t'] =3D time + self.tx_xmit_list.insert(0, skb) + del self.tx_queue_list[i] + if len(self.tx_xmit_list) > self.buffer_budget: + self.tx_xmit_list.pop() + self.of_count_tx_xmit_list +=3D 1 + return + + def handle_kfree_skb(self, event: dict) -> None: + """Handle skb:kfree_skb event.""" + time =3D event['time'] + skbaddr =3D event['skbaddr'] + comm =3D event['comm'] + pid =3D event['pid'] + location =3D event['location'] + for i, skb in enumerate(self.tx_queue_list): + if skb['skbaddr'] =3D=3D skbaddr: + del self.tx_queue_list[i] + return + for i, skb in enumerate(self.tx_xmit_list): + if skb['skbaddr'] =3D=3D skbaddr: + skb['free_t'] =3D time + self.tx_free_list.append(skb) + del self.tx_xmit_list[i] + return + for i, rec_data in enumerate(self.rx_skb_list): + if rec_data['skbaddr'] =3D=3D skbaddr: + rec_data.update({'handle': "kfree_skb", + 'comm': comm, 'pid': pid, 'comm_t': time,= 'location': location}) + del self.rx_skb_list[i] + return + + def handle_consume_skb(self, event: dict) -> None: + """Handle skb:consume_skb event.""" + time =3D event['time'] + skbaddr =3D event['skbaddr'] + for i, skb in enumerate(self.tx_xmit_list): + if skb['skbaddr'] =3D=3D skbaddr: + skb['free_t'] =3D time + self.tx_free_list.append(skb) + del self.tx_xmit_list[i] + return + + def handle_skb_copy_datagram_iovec(self, event: dict) -> None: + """Handle skb:skb_copy_datagram_iovec event.""" + time =3D event['time'] + skbaddr =3D event['skbaddr'] + comm =3D event['comm'] + pid =3D event['pid'] + for i, rec_data in enumerate(self.rx_skb_list): + if skbaddr =3D=3D rec_data['skbaddr']: + rec_data.update({'handle': "skb_copy_datagram_iovec", + 'comm': comm, 'pid': pid, 'comm_t': time}) + del self.rx_skb_list[i] + return + + + + def print_summary(self) -> None: + """Print charts.""" + + # display receive hunks + if self.show_rx: + for hunk in self.receive_hunk_list: + self.print_receive(hunk) + + # display transmit hunks + if self.show_tx: + print(" dev len Qdisc " + " netdevice free") + for hunk in self.tx_free_list: + self.print_transmit(hunk) + + if self.debug: + print("debug buffer status") + print("----------------------------") + print(f"xmit Qdisc:remain:{len(self.tx_queue_list)} " + f"overflow:{self.of_count_tx_queue_list}") + print(f"xmit netdevice:remain:{len(self.tx_xmit_list)} " + f"overflow:{self.of_count_tx_xmit_list}") + print(f"receive:remain:{len(self.rx_skb_list)} " + f"overflow:{self.of_count_rx_skb_list}") + + def handle_single_event(self, event: dict) -> None: + """Handle a single processed event.""" + name =3D event['name'] + if name =3D=3D 'irq:softirq_exit': + self.handle_irq_softirq_exit(event) + elif name =3D=3D 'irq:softirq_entry': + self.handle_irq_softirq_entry(event) + elif name =3D=3D 'irq:softirq_raise': + self.handle_irq_softirq_raise(event) + elif name =3D=3D 'irq:irq_handler_entry': + self.handle_irq_handler_entry(event) + elif name =3D=3D 'irq:irq_handler_exit': + self.handle_irq_handler_exit(event) + elif name =3D=3D 'napi:napi_poll': + self.handle_napi_poll(event) + elif name =3D=3D 'net:netif_receive_skb': + self.handle_netif_receive_skb(event) + elif name =3D=3D 'net:netif_rx': + self.handle_netif_rx(event) + elif name =3D=3D 'skb:skb_copy_datagram_iovec': + self.handle_skb_copy_datagram_iovec(event) + elif name =3D=3D 'net:net_dev_queue': + self.handle_net_dev_queue(event) + elif name =3D=3D 'net:net_dev_xmit': + self.handle_net_dev_xmit(event) + elif name =3D=3D 'skb:kfree_skb': + self.handle_kfree_skb(event) + elif name =3D=3D 'skb:consume_skb': + self.handle_consume_skb(event) + + def process_event(self, sample: perf.sample_event) -> None: + """Process events directly on-the-fly.""" + name =3D str(sample.evsel) + pid =3D sample.sample_pid + if hasattr(self, 'session') and self.session: + comm =3D self.session.find_thread(pid).comm() + else: + comm =3D "Unknown" + event_data =3D { + 'name': name[6:-1] if name.startswith("evsel(") else name, + 'time': sample.sample_time, + 'cpu': sample.sample_cpu, + 'pid': pid, + 'comm': comm, + } + + # Extract specific fields based on event type + if name.startswith("evsel(irq:softirq_"): + event_data['vec'] =3D getattr(sample, "vec", 0) + # Filter for NET_RX + try: + if perf.symbol_str("irq:softirq_entry", "vec", # type: ig= nore + event_data['vec']) !=3D "NET_RX": + return + except AttributeError: + # Fallback if symbol_str not available or fails + if event_data['vec'] !=3D 3: # NET_RX_SOFTIRQ is usually 3 + return + elif name =3D=3D "evsel(irq:irq_handler_entry)": + event_data['irq'] =3D getattr(sample, "irq", -1) + event_data['irq_name'] =3D getattr(sample, "name", "[unknown]") + elif name =3D=3D "evsel(irq:irq_handler_exit)": + event_data['irq'] =3D getattr(sample, "irq", -1) + event_data['ret'] =3D getattr(sample, "ret", 0) + elif name =3D=3D "evsel(napi:napi_poll)": + event_data['napi'] =3D getattr(sample, "napi", 0) + event_data['dev_name'] =3D getattr(sample, "dev_name", "[unkno= wn]") + event_data['work'] =3D getattr(sample, "work", 0) + event_data['budget'] =3D getattr(sample, "budget", 0) + elif name in ("evsel(net:netif_receive_skb)", "evsel(net:netif_rx)= ", + "evsel(net:net_dev_queue)"): + event_data['skbaddr'] =3D getattr(sample, "skbaddr", 0) + event_data['skblen'] =3D getattr(sample, "len", 0) + event_data['dev_name'] =3D getattr(sample, "name", "[unknown]") + elif name =3D=3D "evsel(net:net_dev_xmit)": + event_data['skbaddr'] =3D getattr(sample, "skbaddr", 0) + event_data['skblen'] =3D getattr(sample, "len", 0) + event_data['rc'] =3D getattr(sample, "rc", 0) + event_data['dev_name'] =3D getattr(sample, "name", "[unknown]") + elif name =3D=3D "evsel(skb:kfree_skb)": + event_data['skbaddr'] =3D getattr(sample, "skbaddr", 0) + event_data['location'] =3D getattr(sample, "location", 0) + event_data['protocol'] =3D getattr(sample, "protocol", 0) + event_data['reason'] =3D getattr(sample, "reason", 0) + elif name =3D=3D "evsel(skb:consume_skb)": + event_data['skbaddr'] =3D getattr(sample, "skbaddr", 0) + event_data['location'] =3D getattr(sample, "location", 0) + elif name =3D=3D "evsel(skb:skb_copy_datagram_iovec)": + event_data['skbaddr'] =3D getattr(sample, "skbaddr", 0) + event_data['skblen'] =3D getattr(sample, "skblen", 0) + + self.handle_single_event(event_data) + + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser(description=3D"Display a process of pac= kets and processed time.") + ap.add_argument("-i", "--input", default=3D"perf.data", help=3D"Input = file name") + ap.add_argument("tx", nargs=3D"?", help=3D"show only tx chart") + ap.add_argument("rx", nargs=3D"?", help=3D"show only rx chart") + ap.add_argument("dev", nargs=3D"?", help=3D"show only specified device= ") + ap.add_argument("debug", nargs=3D"?", help=3D"work with debug mode. It= shows buffer status.") + args =3D ap.parse_args() + + parsed_args =3D argparse.Namespace(tx=3DFalse, rx=3DFalse, dev=3DNone,= debug=3DFalse, input=3Dargs.input) + + for arg in sys.argv[1:]: + if arg =3D=3D 'tx': + parsed_args.tx =3D True + elif arg =3D=3D 'rx': + parsed_args.rx =3D True + elif arg.startswith('dev=3D'): + parsed_args.dev =3D arg[4:] + elif arg =3D=3D 'debug': + parsed_args.debug =3D True + + analyzer =3D NetDevTimesAnalyzer(parsed_args) + + try: + session =3D perf.session(perf.data(parsed_args.input), sample=3Dan= alyzer.process_event) + analyzer.session =3D session + session.process_events() + analyzer.print_summary() + except KeyboardInterrupt: + analyzer.print_summary() + except Exception as e: + print(f"Error processing events: {e}") --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f74.google.com (mail-dl1-f74.google.com [74.125.82.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EF3B53ACA51 for ; Sat, 25 Apr 2026 22:51:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157503; cv=none; b=DSRehqpIVR7yghjUdwF5Nc2WK/ujVUGm4OCSKI44NcWrkttnsi/S6l0qcMbROMxPainQjqtO/PNwzI0Ep5Ir3uo8kt5bw5H9QorFBU97IqkGI4y3X09W8Z9Uk6oiIVcW3lHR09BCnO5mMLXObYi2RLm7B1/iqYtjrP1FCg5RuD0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157503; c=relaxed/simple; bh=m4evdrAfFdloOFUinFFaS10MkOUODfOhUsZC1qEcLyU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=BnAJOGlDxbUk7bQJP9TqZCnTXtJokGFZfgj4dc+C3Ru40VhsOEtbbkgBeaXcuu/MLsq6hvvnYoEAjRlJRXrK4A70r4gGG0CR2P/X0MdEwQ47baPJvc5SgUlvVx+AiATE44aE7rrMt5LMJ1ZDcBmkLAcbgU39IPjAY/ED6SYKiA0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=fjfxVGXW; arc=none smtp.client-ip=74.125.82.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="fjfxVGXW" Received: by mail-dl1-f74.google.com with SMTP id a92af1059eb24-12c8de02a4dso15631086c88.1 for ; Sat, 25 Apr 2026 15:51:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157501; x=1777762301; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=tFkKJpx387LQyGkxFesF27g3u3l0azo1BknyR7NysHU=; b=fjfxVGXWHv4TvW2Hpazs34WsyvSJz2W6US8l41F1XvNL24bTsvMgT2ZD3pi+yoyC0I KjyuDEUe3u9SSDv8k6JSKDD2d53OZKkHzaJJnvVkgfm8cJ0pgRMSzxeF+LRvlkhUNphG FiWeUQKWAOBZ7bszUlAFwT8KlxXeUeZjuzxgUNsGybME9yRYicCDA8AqdtArnvEidv/b 8Jajx37gAvQ/5aUQzbZ850hwBYewvsIoUM28wz3nNSXOmfvxa2uS1yebjtDe5joI+6jy nIYZB8VseWidW+D/I+nRUi3dibtVO7K6jdnpro2DxaDLGfJqRUrSfN+ubaAdoD1pY6b8 KSkw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157501; x=1777762301; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=tFkKJpx387LQyGkxFesF27g3u3l0azo1BknyR7NysHU=; b=IPYuoWYYA7b4uQ+HwCJlx3zubBCCRgPUah3JRJM0atO0VfpFTvan9/JiMssfKzNF31 /q3IjoPQLi8d3CH/N8ibqa0o0Ty0lKe+/2sKnaGM/7SuMxT50PFJPjrowrPWJGnrgkDt 8rBEmex7cd7cE3h8FD5nk/BtHmeWSU2BsrJYn7quPVLNC4JIUJnx4a0duPa76NyaBWYn 4B6T2KhemMRenQZFoxSY0+rsgCwyd8yD1zTHLAPfBgco+5fufHa6b2sV5qp07n1TQBIM kDFproxDgr9KsLRzpT0j+7bgT6T2SIgEJhEA5l5RqAEZWONvmSQBizNJSntMM9HfXZfl yYag== X-Forwarded-Encrypted: i=1; AFNElJ8KAs8rMmi83MCWs6QPYkbKtlEzNPldEDemDhJ/yHATOJmeB9PfdHrT+8gMOgykEQdjGqUWKWGLoqT3KLI=@vger.kernel.org X-Gm-Message-State: AOJu0YxVf6TGt9nqsMA069sBTn7H8xXtj2GH+IJBFlWMPMA0KGdcULL2 afN94KBy7UyH9KoqzrOEr+VeBjkKUL+qj4YO+ppdij+OabUAupWOF5wQs55XNbZCQmcXa2mz8qr noE1wgkgeyA== X-Received: from dlbpu10.prod.google.com ([2002:a05:7022:e88a:b0:12b:fba9:5eb0]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:418e:b0:12c:87f1:f41a with SMTP id a92af1059eb24-12c87f1f772mr17623182c88.21.1777157500791; Sat, 25 Apr 2026 15:51:40 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:34 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-43-irogers@google.com> Subject: [PATCH v7 42/59] perf powerpc-hcalls: Port powerpc-hcalls to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Ported from tools/perf/scripts/python/. - Refactored the script to use a class structure (HCallAnalyzer) to encapsulate state. - Used perf.session for event processing. - Tracked hcall entry and exit to calculate duration and aggregate statistics. - Moved the large hcall_table to a module-level constant HCALL_TABLE. - Cleaned up Python 2 compatibility artifacts. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- tools/perf/python/powerpc-hcalls.py | 211 ++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100755 tools/perf/python/powerpc-hcalls.py diff --git a/tools/perf/python/powerpc-hcalls.py b/tools/perf/python/powerp= c-hcalls.py new file mode 100755 index 000000000000..c4fa539174c9 --- /dev/null +++ b/tools/perf/python/powerpc-hcalls.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0+ +""" +Hypervisor call statistics + +Copyright (C) 2018 Ravi Bangoria, IBM Corporation +Ported from tools/perf/scripts/python/powerpc-hcalls.py +""" + +import argparse +from collections import defaultdict +import perf + +# Hypervisor call table +HCALL_TABLE =3D { + 4: 'H_REMOVE', + 8: 'H_ENTER', + 12: 'H_READ', + 16: 'H_CLEAR_MOD', + 20: 'H_CLEAR_REF', + 24: 'H_PROTECT', + 28: 'H_GET_TCE', + 32: 'H_PUT_TCE', + 36: 'H_SET_SPRG0', + 40: 'H_SET_DABR', + 44: 'H_PAGE_INIT', + 48: 'H_SET_ASR', + 52: 'H_ASR_ON', + 56: 'H_ASR_OFF', + 60: 'H_LOGICAL_CI_LOAD', + 64: 'H_LOGICAL_CI_STORE', + 68: 'H_LOGICAL_CACHE_LOAD', + 72: 'H_LOGICAL_CACHE_STORE', + 76: 'H_LOGICAL_ICBI', + 80: 'H_LOGICAL_DCBF', + 84: 'H_GET_TERM_CHAR', + 88: 'H_PUT_TERM_CHAR', + 92: 'H_REAL_TO_LOGICAL', + 96: 'H_HYPERVISOR_DATA', + 100: 'H_EOI', + 104: 'H_CPPR', + 108: 'H_IPI', + 112: 'H_IPOLL', + 116: 'H_XIRR', + 120: 'H_MIGRATE_DMA', + 124: 'H_PERFMON', + 220: 'H_REGISTER_VPA', + 224: 'H_CEDE', + 228: 'H_CONFER', + 232: 'H_PROD', + 236: 'H_GET_PPP', + 240: 'H_SET_PPP', + 244: 'H_PURR', + 248: 'H_PIC', + 252: 'H_REG_CRQ', + 256: 'H_FREE_CRQ', + 260: 'H_VIO_SIGNAL', + 264: 'H_SEND_CRQ', + 272: 'H_COPY_RDMA', + 276: 'H_REGISTER_LOGICAL_LAN', + 280: 'H_FREE_LOGICAL_LAN', + 284: 'H_ADD_LOGICAL_LAN_BUFFER', + 288: 'H_SEND_LOGICAL_LAN', + 292: 'H_BULK_REMOVE', + 304: 'H_MULTICAST_CTRL', + 308: 'H_SET_XDABR', + 312: 'H_STUFF_TCE', + 316: 'H_PUT_TCE_INDIRECT', + 332: 'H_CHANGE_LOGICAL_LAN_MAC', + 336: 'H_VTERM_PARTNER_INFO', + 340: 'H_REGISTER_VTERM', + 344: 'H_FREE_VTERM', + 348: 'H_RESET_EVENTS', + 352: 'H_ALLOC_RESOURCE', + 356: 'H_FREE_RESOURCE', + 360: 'H_MODIFY_QP', + 364: 'H_QUERY_QP', + 368: 'H_REREGISTER_PMR', + 372: 'H_REGISTER_SMR', + 376: 'H_QUERY_MR', + 380: 'H_QUERY_MW', + 384: 'H_QUERY_HCA', + 388: 'H_QUERY_PORT', + 392: 'H_MODIFY_PORT', + 396: 'H_DEFINE_AQP1', + 400: 'H_GET_TRACE_BUFFER', + 404: 'H_DEFINE_AQP0', + 408: 'H_RESIZE_MR', + 412: 'H_ATTACH_MCQP', + 416: 'H_DETACH_MCQP', + 420: 'H_CREATE_RPT', + 424: 'H_REMOVE_RPT', + 428: 'H_REGISTER_RPAGES', + 432: 'H_DISABLE_AND_GETC', + 436: 'H_ERROR_DATA', + 440: 'H_GET_HCA_INFO', + 444: 'H_GET_PERF_COUNT', + 448: 'H_MANAGE_TRACE', + 468: 'H_FREE_LOGICAL_LAN_BUFFER', + 472: 'H_POLL_PENDING', + 484: 'H_QUERY_INT_STATE', + 580: 'H_ILLAN_ATTRIBUTES', + 592: 'H_MODIFY_HEA_QP', + 596: 'H_QUERY_HEA_QP', + 600: 'H_QUERY_HEA', + 604: 'H_QUERY_HEA_PORT', + 608: 'H_MODIFY_HEA_PORT', + 612: 'H_REG_BCMC', + 616: 'H_DEREG_BCMC', + 620: 'H_REGISTER_HEA_RPAGES', + 624: 'H_DISABLE_AND_GET_HEA', + 628: 'H_GET_HEA_INFO', + 632: 'H_ALLOC_HEA_RESOURCE', + 644: 'H_ADD_CONN', + 648: 'H_DEL_CONN', + 664: 'H_JOIN', + 676: 'H_VASI_STATE', + 688: 'H_ENABLE_CRQ', + 696: 'H_GET_EM_PARMS', + 720: 'H_SET_MPP', + 724: 'H_GET_MPP', + 748: 'H_HOME_NODE_ASSOCIATIVITY', + 756: 'H_BEST_ENERGY', + 764: 'H_XIRR_X', + 768: 'H_RANDOM', + 772: 'H_COP', + 788: 'H_GET_MPP_X', + 796: 'H_SET_MODE', + 61440: 'H_RTAS', +} + + +class HCallAnalyzer: + """Analyzes hypervisor calls and aggregates statistics.""" + + def __init__(self): + # output: {opcode: {'min': min, 'max': max, 'time': time, 'cnt': c= nt}} + self.output =3D defaultdict(lambda: {'time': 0, 'cnt': 0, 'min': f= loat('inf'), 'max': 0}) + # d_enter: {pid: (opcode, nsec)} + self.d_enter: dict[int, tuple[int, int]] =3D {} + self.print_ptrn =3D '%-28s%10s%10s%10s%10s' + + def hcall_table_lookup(self, opcode: int) -> str: + """Lookup hcall name by opcode.""" + return HCALL_TABLE.get(opcode, str(opcode)) + + def process_event(self, sample: perf.sample_event) -> None: + """Process a single sample event.""" + name =3D str(sample.evsel) + pid =3D sample.sample_pid + time =3D sample.time + opcode =3D getattr(sample, "opcode", -1) + + if opcode =3D=3D -1: + return + + if name =3D=3D "evsel(powerpc:hcall_entry)": + self.d_enter[pid] =3D (opcode, time) + elif name =3D=3D "evsel(powerpc:hcall_exit)": + if pid in self.d_enter: + opcode_entry, time_entry =3D self.d_enter[pid] + if opcode_entry =3D=3D opcode: + diff =3D time - time_entry + del self.d_enter[pid] + + stats =3D self.output[opcode] + stats['time'] +=3D diff + stats['cnt'] +=3D 1 + if diff < stats['min']: + stats['min'] =3D diff + if diff > stats['max']: + stats['max'] =3D diff + + def print_summary(self) -> None: + """Print aggregated statistics.""" + print(self.print_ptrn % ('hcall', 'count', 'min(ns)', 'max(ns)', '= avg(ns)')) + print('-' * 68) + for opcode in sorted(self.output.keys()): + h_name =3D self.hcall_table_lookup(opcode) + stats =3D self.output[opcode] + time =3D stats['time'] + cnt =3D stats['cnt'] + min_t =3D stats['min'] + max_t =3D stats['max'] + + # Avoid float representation for large integers if possible, + # or use formatted strings. Legacy used time//cnt. + avg_t =3D time // cnt if cnt > 0 else 0 + + # If min was not updated, it remains inf, but cnt should be > = 0 if in output + if min_t =3D=3D float('inf'): + min_t =3D 0 + + print(self.print_ptrn % (h_name, cnt, int(min_t), int(max_t), = avg_t)) + + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser(description=3D"Hypervisor call statisti= cs") + ap.add_argument("-i", "--input", default=3D"perf.data", help=3D"Input = file name") + args =3D ap.parse_args() + + analyzer =3D HCallAnalyzer() + + try: + session =3D perf.session(perf.data(args.input), sample=3Danalyzer.= process_event) + session.process_events() + analyzer.print_summary() + except KeyboardInterrupt: + analyzer.print_summary() + except Exception as e: + print(f"Error processing events: {e}") --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C51F53AC0FD for ; Sat, 25 Apr 2026 22:51:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157506; cv=none; b=aQAab4UoJAafOECTEiycdK3UWHUJeCKp6niw2j7KKK2mce2y4yH3iB8NTnCp9fFuZcGc+ywsLNI8VNMgSwJQy1qRj+FC81OOO066BOUPTOPm+CesS4zNplsbcFj7cIpRhXaiY68cyo+NBdobQHpifcbfVWmVAy39PwoM4WN5Z2w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157506; c=relaxed/simple; bh=wxQ7XBXwKkGoKBfzJmTM6BFvGs1xO0kKj7O3T2ZqZ4Y=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=CnxEvF46la7uc+cygQXQur4pUDr2jiCVTg/8mFIr0JJ/S3hZ4uZkUFUwMwzmzhCzU7yE3792891MIwxa23JbqqVGdJ2znuDh6wpRJgMf9tVM9gMxHVueTpGCb4ZPhllkcx9kI39CSh9yXnLiLx/GdU+90Nmy2UoSrL28nwVtLXw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=qidLG/iS; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="qidLG/iS" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2eaf70f3b5fso734600eec.0 for ; Sat, 25 Apr 2026 15:51:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157503; x=1777762303; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=SC9Z8TYU3FIlegbJKB+Rnykve8tItTwLd+p+lGKeOhc=; b=qidLG/iS90ZrO2BpcMvvGiruriK553ANVg1LDaIyvv6LIA+s2LbsixG9MgITvoO7yD iMglmHAr4SoyXh0DnolWy8v2PtBMlrSSX7YcaiJR+Ppey8prD5LQcqy9Yiri/M2ZQQom Y1NiyZocOEzvq6oHkbXkhRXp6MtFcLBWHdS3ITYWvki8pGm1Bv0es5BRkSZLFp0wSsD+ fSBrXfKanL6pNIBlKiszCSMBlqUT5GcaiRZzpiotxuk1hvV7yteTxle2Dzh655fckbiV RAeKh6uFN1usuoAI4eWi8ANo9OiFCsDc6Pzrvq89WoeVpA6mE28075sM35cgkfnz/Q6b uD9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157503; x=1777762303; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=SC9Z8TYU3FIlegbJKB+Rnykve8tItTwLd+p+lGKeOhc=; b=NFVj1CRP/fBqDAbnybkDt54MDpLrQ2tbffjo6uxg9/jSsnrlLsV0nPhgPxL358gE8f CM75A+ythsuhD12vAAkG9eie5N4DBbrmK2/9Z6hoc8/lJ3ejpcM8lSFIu1lAA3qQj87p BY88Doecg5dUB1kWmYG9eOVw+vkduOqZIof5W8T3OefPAAJ1pAX4PN7SArKvSL+hpqUs yr+/Gi4Jmanp62jy9ig8H36l3Vfam8N+0fIL8POpNzcDq6QghaaxXFpNpe9KI2Y9BidN sDLm3gMC7HS0hrpL+X7ObFJ4Yc5yL+VcQjox4Xni5RA//XIPgS7rMXOrJ/Jrj1m/7qO8 JgvA== X-Forwarded-Encrypted: i=1; AFNElJ+FJa+w/YtzwbmdmxCOZhLKV6sOC+rM+658GVaMeu7XcrR1vs8IHxm+OteiFZkWKC56cb1+ge4Y0Ag3dMk=@vger.kernel.org X-Gm-Message-State: AOJu0Yzz1SStaZN9YXEzMxzQrLLQM2dgPCk+KHoGphKy9wXHsr0wgU6Z LnWrugMcPi8P0ItwRTJs8QIjO9q9m00S19XbkHGDM6aTy7yHGFRUri+xVIPzg9fNQQCgZRqKCMb DkuM3J6CYWw== X-Received: from dycf28.prod.google.com ([2002:a05:7300:50dc:b0:2d9:6c52:c24a]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:641a:b0:2dd:6937:79d6 with SMTP id 5a478bee46e88-2e46c48a9cfmr15637752eec.7.1777157502527; Sat, 25 Apr 2026 15:51:42 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:35 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-44-irogers@google.com> Subject: [PATCH v7 43/59] perf sched-migration: Port sched-migration/SchedGui to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Ported from tools/perf/scripts/python/ and its Util lib. - Refactored sched-migration.py to use a class structure (SchedMigrationAnalyzer) to encapsulate state. - Used perf.session for event processing. - Ported SchedGui.py to the same directory to keep it as a local dependency. - Made wxPython dependency optional in sched-migration.py, printing a message if it's missing instead of failing with ImportError. - Cleaned up Python 2 compatibility artifacts. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: tools/perf/python/SchedGui.py: - Python 3 Compatibility: Fixed TypeError issues caused by float division in Python 3 when wxPython expected integers. Used integer division ( // ) and explicit int() casts for scrollbar and size calculations. - wxPython Phoenix API Updates: - Replaced deprecated SetDimensions() with SetSize() . - Replaced removed GetPositionTuple() with GetPosition() in on_mouse_down. - Fixed wx.PaintDC creation in on_paint to use event.GetEventObject() to ensure valid DC creation regardless of which window triggered the event. - Layout and Rendering Fixes: - Replaced static layout with a wx.SplitterWindow to physically separate the drawing area from the text area, preventing them from overlapping and restoring scrollbar functionality. - Adjusted the initial sash position to give 3/4 of the height to the drawing area. - Replaced wx.StaticText with a multiline wx.TextCtrl for the summary area to allow text selection and simpler value updates. - Added CPU labels ("CPU ") drawn at the left edge of the visible area in on_paint . - Added background clearing ( dc.Clear() ) in on_paint to avoid "ghosting" of old text and rectangles when scrolling. tools/perf/python/sched-migration.py: - Fixed a bug where sharing a snapshot in find_time_slice caused data mutation across calls. - Added safety checks to handle empty data cases (e.g., when intervals have no events). - Fixed fallbacks in fill_zone when search conditions fail to find a matching time slice. --- tools/perf/python/SchedGui.py | 219 +++++++++++++ tools/perf/python/sched-migration.py | 469 +++++++++++++++++++++++++++ 2 files changed, 688 insertions(+) create mode 100755 tools/perf/python/SchedGui.py create mode 100755 tools/perf/python/sched-migration.py diff --git a/tools/perf/python/SchedGui.py b/tools/perf/python/SchedGui.py new file mode 100755 index 000000000000..6111f3e5f552 --- /dev/null +++ b/tools/perf/python/SchedGui.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# SchedGui.py - Python extension for perf script, basic GUI code for +# traces drawing and overview. +# +# Copyright (C) 2010 by Frederic Weisbecker +# +# Ported to modern directory structure. + +try: + import wx # type: ignore +except ImportError: + raise ImportError("You need to install the wxpython lib for this scrip= t") + + +class RootFrame(wx.Frame): + Y_OFFSET =3D 100 + RECT_HEIGHT =3D 100 + RECT_SPACE =3D 50 + EVENT_MARKING_WIDTH =3D 5 + + def __init__(self, sched_tracer, title, parent=3DNone, id=3D-1): + wx.Frame.__init__(self, parent, id, title) + + (self.screen_width, self.screen_height) =3D wx.GetDisplaySize() + self.screen_width -=3D 10 + self.screen_height -=3D 10 + self.zoom =3D 0.5 + self.scroll_scale =3D 20 + self.sched_tracer =3D sched_tracer + self.sched_tracer.set_root_win(self) + (self.ts_start, self.ts_end) =3D sched_tracer.interval() + self.update_width_virtual() + self.nr_rects =3D sched_tracer.nr_rectangles() + 1 + self.height_virtual =3D RootFrame.Y_OFFSET + \ + (self.nr_rects * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE= )) + + # whole window panel + self.panel =3D wx.Panel(self, size=3D(self.screen_width, self.scre= en_height)) + + # scrollable container + # Create SplitterWindow + self.splitter =3D wx.SplitterWindow(self.panel, style=3Dwx.SP_3D) + + # scrollable container (Top) + self.scroll =3D wx.ScrolledWindow(self.splitter) + self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, + int(self.width_virtual // self.scroll_sc= ale), + int(self.height_virtual // self.scroll_s= cale)) + self.scroll.EnableScrolling(True, True) + self.scroll.SetFocus() + + # scrollable drawing area + self.scroll_panel =3D wx.Panel(self.scroll, + size=3D(self.screen_width - 15, self.= screen_height // 2)) + self.scroll_panel.Bind(wx.EVT_PAINT, self.on_paint) + self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press) + self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) + self.scroll.Bind(wx.EVT_KEY_DOWN, self.on_key_press) + self.scroll.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) + + self.scroll_panel.SetSize(int(self.width_virtual), int(self.height= _virtual)) + + # Create a separate panel for text (Bottom) + self.text_panel =3D wx.Panel(self.splitter) + self.text_sizer =3D wx.BoxSizer(wx.VERTICAL) + self.txt =3D wx.TextCtrl(self.text_panel, -1, "Click a bar to see = details", + style=3Dwx.TE_MULTILINE) + self.text_sizer.Add(self.txt, 1, wx.EXPAND | wx.ALL, 5) + self.text_panel.SetSizer(self.text_sizer) + + # Split the window + self.splitter.SplitHorizontally(self.scroll, self.text_panel, (sel= f.screen_height * 3) // 4) + + # Main sizer to layout splitter + self.main_sizer =3D wx.BoxSizer(wx.VERTICAL) + self.main_sizer.Add(self.splitter, 1, wx.EXPAND) + self.panel.SetSizer(self.main_sizer) + + self.scroll.Fit() + self.Fit() + + self.Show(True) + + def us_to_px(self, val): + return val / (10 ** 3) * self.zoom + + def px_to_us(self, val): + return (val / self.zoom) * (10 ** 3) + + def scroll_start(self): + (x, y) =3D self.scroll.GetViewStart() + return (x * self.scroll_scale, y * self.scroll_scale) + + def scroll_start_us(self): + (x, y) =3D self.scroll_start() + return self.px_to_us(x) + + def paint_rectangle_zone(self, nr, color, top_color, start, end): + offset_px =3D self.us_to_px(start - self.ts_start) + width_px =3D self.us_to_px(end - start) + + offset_py =3D RootFrame.Y_OFFSET + (nr * (RootFrame.RECT_HEIGHT + = RootFrame.RECT_SPACE)) + width_py =3D RootFrame.RECT_HEIGHT + + dc =3D self.dc + + if top_color is not None: + (r, g, b) =3D top_color + top_color =3D wx.Colour(r, g, b) + brush =3D wx.Brush(top_color, wx.SOLID) + dc.SetBrush(brush) + dc.DrawRectangle(int(offset_px), int(offset_py), + int(width_px), RootFrame.EVENT_MARKING_WIDTH) + width_py -=3D RootFrame.EVENT_MARKING_WIDTH + offset_py +=3D RootFrame.EVENT_MARKING_WIDTH + + (r, g, b) =3D color + color =3D wx.Colour(r, g, b) + brush =3D wx.Brush(color, wx.SOLID) + dc.SetBrush(brush) + dc.DrawRectangle(int(offset_px), int(offset_py), int(width_px), in= t(width_py)) + + def update_rectangles(self, dc, start, end): + start +=3D self.ts_start + end +=3D self.ts_start + self.sched_tracer.fill_zone(start, end) + + def on_paint(self, event): + window =3D event.GetEventObject() + dc =3D wx.PaintDC(window) + + # Clear background to avoid ghosting + dc.SetBackground(wx.Brush(window.GetBackgroundColour())) + dc.Clear() + + self.dc =3D dc + + width =3D min(self.width_virtual, self.screen_width) + (x, y) =3D self.scroll_start() + start =3D self.px_to_us(x) + end =3D self.px_to_us(x + width) + self.update_rectangles(dc, start, end) + + # Draw CPU labels at the left edge of the visible area + (x_scroll, _) =3D self.scroll_start() + for nr in range(self.nr_rects): + offset_py =3D RootFrame.Y_OFFSET + (nr * (RootFrame.RECT_HEIGH= T + RootFrame.RECT_SPACE)) + dc.DrawText(f"CPU {nr}", x_scroll + 10, offset_py + 10) + + def rect_from_ypixel(self, y): + y -=3D RootFrame.Y_OFFSET + rect =3D y // (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) + height =3D y % (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) + + if rect < 0 or rect > self.nr_rects - 1 or height > RootFrame.RECT= _HEIGHT: + return -1 + + return rect + + def update_summary(self, txt): + self.txt.SetValue(txt) + self.text_panel.Layout() + self.splitter.Layout() + self.text_panel.Refresh() + + def on_mouse_down(self, event): + pos =3D event.GetPosition() + x, y =3D pos.x, pos.y + rect =3D self.rect_from_ypixel(y) + if rect =3D=3D -1: + return + + t =3D self.px_to_us(x) + self.ts_start + + self.sched_tracer.mouse_down(rect, t) + + def update_width_virtual(self): + self.width_virtual =3D self.us_to_px(self.ts_end - self.ts_start) + + def __zoom(self, x): + self.update_width_virtual() + (xpos, ypos) =3D self.scroll.GetViewStart() + xpos =3D int(self.us_to_px(x) // self.scroll_scale) + self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, + int(self.width_virtual // self.scroll_sc= ale), + int(self.height_virtual // self.scroll_s= cale), + xpos, ypos) + self.Refresh() + + def zoom_in(self): + x =3D self.scroll_start_us() + self.zoom *=3D 2 + self.__zoom(x) + + def zoom_out(self): + x =3D self.scroll_start_us() + self.zoom /=3D 2 + self.__zoom(x) + + def on_key_press(self, event): + key =3D event.GetRawKeyCode() + if key =3D=3D ord("+"): + self.zoom_in() + return + if key =3D=3D ord("-"): + self.zoom_out() + return + + key =3D event.GetKeyCode() + (x, y) =3D self.scroll.GetViewStart() + if key =3D=3D wx.WXK_RIGHT: + self.scroll.Scroll(x + 1, y) + elif key =3D=3D wx.WXK_LEFT: + self.scroll.Scroll(x - 1, y) + elif key =3D=3D wx.WXK_DOWN: + self.scroll.Scroll(x, y + 1) + elif key =3D=3D wx.WXK_UP: + self.scroll.Scroll(x, y - 1) diff --git a/tools/perf/python/sched-migration.py b/tools/perf/python/sched= -migration.py new file mode 100755 index 000000000000..331278958763 --- /dev/null +++ b/tools/perf/python/sched-migration.py @@ -0,0 +1,469 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +Cpu task migration overview toy + +Copyright (C) 2010 Frederic Weisbecker +Ported to modern directory structure and refactored to use class. +""" + +import argparse +from collections import defaultdict, UserList +import perf + +# SchedGui might not be available if wxPython is missing +try: + from SchedGui import RootFrame + import wx # type: ignore + WX_AVAILABLE =3D True +except ImportError: + WX_AVAILABLE =3D False + +# Global threads dictionary +threads =3D defaultdict(lambda: "unknown") +threads[0] =3D "idle" + + +def thread_name(pid: int) -> str: + """Return thread name formatted with pid.""" + return f"{threads[pid]}:{pid}" + + +def task_state(state: int) -> str: + """Map task state integer to string.""" + states =3D { + 0: "R", + 1: "S", + 2: "D", + 64: "DEAD" + } + return states.get(state, "Unknown") + + +class RunqueueEventUnknown: + """Unknown runqueue event.""" + @staticmethod + def color(): + """Return color for event.""" + return None + + def __repr__(self): + return "unknown" + + +class RunqueueEventSleep: + """Sleep runqueue event.""" + @staticmethod + def color(): + """Return color for event.""" + return 0, 0, 0xff + + def __init__(self, sleeper: int): + self.sleeper =3D sleeper + + def __repr__(self): + return f"{thread_name(self.sleeper)} gone to sleep" + + +class RunqueueEventWakeup: + """Wakeup runqueue event.""" + @staticmethod + def color(): + """Return color for event.""" + return 0xff, 0xff, 0 + + def __init__(self, wakee: int): + self.wakee =3D wakee + + def __repr__(self): + return f"{thread_name(self.wakee)} woke up" + + +class RunqueueEventFork: + """Fork runqueue event.""" + @staticmethod + def color(): + """Return color for event.""" + return 0, 0xff, 0 + + def __init__(self, child: int): + self.child =3D child + + def __repr__(self): + return f"new forked task {thread_name(self.child)}" + + +class RunqueueMigrateIn: + """Migrate in runqueue event.""" + @staticmethod + def color(): + """Return color for event.""" + return 0, 0xf0, 0xff + + def __init__(self, new: int): + self.new =3D new + + def __repr__(self): + return f"task migrated in {thread_name(self.new)}" + + +class RunqueueMigrateOut: + """Migrate out runqueue event.""" + @staticmethod + def color(): + """Return color for event.""" + return 0xff, 0, 0xff + + def __init__(self, old: int): + self.old =3D old + + def __repr__(self): + return f"task migrated out {thread_name(self.old)}" + + +class RunqueueSnapshot: + """Snapshot of runqueue state.""" + + def __init__(self, tasks=3DNone, event=3DNone): + if tasks is None: + tasks =3D (0,) + if event is None: + event =3D RunqueueEventUnknown() + self.tasks =3D tuple(tasks) + self.event =3D event + + def sched_switch(self, prev: int, prev_state: int, next_pid: int): + """Handle sched switch in snapshot.""" + if task_state(prev_state) =3D=3D "R" and next_pid in self.tasks \ + and prev in self.tasks: + return self + + event =3D RunqueueEventUnknown() + if task_state(prev_state) !=3D "R": + event =3D RunqueueEventSleep(prev) # type: ignore + + next_tasks =3D list(self.tasks[:]) + if prev in self.tasks: + if task_state(prev_state) !=3D "R": + next_tasks.remove(prev) + elif task_state(prev_state) =3D=3D "R": + next_tasks.append(prev) + + if next_pid not in next_tasks: + next_tasks.append(next_pid) + + return RunqueueSnapshot(next_tasks, event) + + def migrate_out(self, old: int): + """Handle task migrate out in snapshot.""" + if old not in self.tasks: + return self + next_tasks =3D [task for task in self.tasks if task !=3D old] + + return RunqueueSnapshot(next_tasks, RunqueueMigrateOut(old)) + + def __migrate_in(self, new: int, event): + if new in self.tasks: + return RunqueueSnapshot(self.tasks, event) + next_tasks =3D self.tasks + tuple([new]) + + return RunqueueSnapshot(next_tasks, event) + + def migrate_in(self, new: int): + """Handle task migrate in in snapshot.""" + return self.__migrate_in(new, RunqueueMigrateIn(new)) + + def wake_up(self, new: int): + """Handle task wakeup in snapshot.""" + return self.__migrate_in(new, RunqueueEventWakeup(new)) + + def wake_up_new(self, new: int): + """Handle task fork in snapshot.""" + return self.__migrate_in(new, RunqueueEventFork(new)) + + def load(self) -> int: + """Provide the number of tasks on the runqueue. Don't count idle""" + return len(self.tasks) - 1 + + def __repr__(self): + return self.tasks.__repr__() + + +class TimeSlice: + """Represents a time slice of execution.""" + + def __init__(self, start: int, prev): + self.start =3D start + self.prev =3D prev + self.end =3D start + # cpus that triggered the event + self.event_cpus: list[int] =3D [] + if prev is not None: + self.total_load =3D prev.total_load + self.rqs =3D prev.rqs.copy() + else: + self.rqs =3D defaultdict(RunqueueSnapshot) + self.total_load =3D 0 + + def __update_total_load(self, old_rq: RunqueueSnapshot, new_rq: Runque= ueSnapshot): + diff =3D new_rq.load() - old_rq.load() + self.total_load +=3D diff + + def sched_switch(self, ts_list, prev: int, prev_state: int, next_pid: = int, cpu: int): + """Process sched_switch in time slice.""" + old_rq =3D self.prev.rqs[cpu] + new_rq =3D old_rq.sched_switch(prev, prev_state, next_pid) + + if old_rq is new_rq: + return + + self.rqs[cpu] =3D new_rq + self.__update_total_load(old_rq, new_rq) + ts_list.append(self) + self.event_cpus =3D [cpu] + + def migrate(self, ts_list, new: int, old_cpu: int, new_cpu: int): + """Process task migration in time slice.""" + if old_cpu =3D=3D new_cpu: + return + old_rq =3D self.prev.rqs[old_cpu] + out_rq =3D old_rq.migrate_out(new) + self.rqs[old_cpu] =3D out_rq + self.__update_total_load(old_rq, out_rq) + + new_rq =3D self.prev.rqs[new_cpu] + in_rq =3D new_rq.migrate_in(new) + self.rqs[new_cpu] =3D in_rq + self.__update_total_load(new_rq, in_rq) + + ts_list.append(self) + + if old_rq is not out_rq: + self.event_cpus.append(old_cpu) + self.event_cpus.append(new_cpu) + + def wake_up(self, ts_list, pid: int, cpu: int, fork: bool): + """Process wakeup in time slice.""" + old_rq =3D self.prev.rqs[cpu] + if fork: + new_rq =3D old_rq.wake_up_new(pid) + else: + new_rq =3D old_rq.wake_up(pid) + + if new_rq is old_rq: + return + self.rqs[cpu] =3D new_rq + self.__update_total_load(old_rq, new_rq) + ts_list.append(self) + self.event_cpus =3D [cpu] + + def next(self, t: int): + """Create next time slice.""" + self.end =3D t + return TimeSlice(t, self) + + +class TimeSliceList(UserList): + """List of time slices with search capabilities.""" + + def __init__(self, arg=3DNone): + super().__init__(arg if arg is not None else []) + self.root_win =3D None + + def get_time_slice(self, ts: int) -> TimeSlice: + """Get or create time slice for timestamp.""" + if len(self.data) =3D=3D 0: + ts_slice =3D TimeSlice(ts, TimeSlice(-1, None)) + else: + ts_slice =3D self.data[-1].next(ts) + return ts_slice + + def find_time_slice(self, ts: int) -> int: + """Binary search for time slice containing timestamp.""" + if not self.data: + return -1 + start =3D 0 + end =3D len(self.data) + found =3D -1 + searching =3D True + while searching: + if start in (end, end - 1): + searching =3D False + + i =3D (end + start) // 2 + if self.data[i].start <=3D ts <=3D self.data[i].end: + found =3D i + break + + if self.data[i].end < ts: + start =3D i + elif self.data[i].start > ts: + end =3D i + + return found + + def set_root_win(self, win): + """Set root window for GUI.""" + self.root_win =3D win + + def mouse_down(self, cpu: int, t: int): + """Handle mouse down event from GUI.""" + idx =3D self.find_time_slice(t) + if idx =3D=3D -1: + return + + ts =3D self[idx] + rq =3D ts.rqs[cpu] + raw =3D f"CPU: {cpu}\n" + raw +=3D f"Last event : {repr(rq.event)}\n" + raw +=3D f"Timestamp : {ts.start // (10 ** 9)}.{ts.start % (10 ** = 9) // 1000:06d}\n" + raw +=3D f"Duration : {(ts.end - ts.start) // (10 ** 6):6d} us\n" + raw +=3D f"Load =3D {rq.load()}\n" + for task in rq.tasks: + raw +=3D f"{thread_name(task)} \n" + + if self.root_win: + self.root_win.update_summary(raw) + + def update_rectangle_cpu(self, slice_obj: TimeSlice, cpu: int): + """Update rectangle for CPU in GUI.""" + rq =3D slice_obj.rqs[cpu] + + if slice_obj.total_load !=3D 0: + load_rate =3D rq.load() / float(slice_obj.total_load) + else: + load_rate =3D 0 + + red_power =3D int(0xff - (0xff * load_rate)) + color =3D (0xff, red_power, red_power) + + top_color =3D None + if cpu in slice_obj.event_cpus: + top_color =3D rq.event.color() + + if self.root_win: + self.root_win.paint_rectangle_zone(cpu, color, top_color, + slice_obj.start, slice_obj.= end) + + def fill_zone(self, start: int, end: int): + """Fill zone in GUI.""" + i =3D self.find_time_slice(start) + if i =3D=3D -1: + i =3D 0 + + for idx in range(i, len(self.data)): + timeslice =3D self.data[idx] + if timeslice.start > end: + return + + for cpu in timeslice.rqs: + self.update_rectangle_cpu(timeslice, cpu) + + def interval(self) -> tuple[int, int]: + """Return start and end timestamps.""" + if len(self.data) =3D=3D 0: + return 0, 0 + return self.data[0].start, self.data[-1].end + + def nr_rectangles(self) -> int: + """Return maximum CPU number.""" + if not self.data: + return 0 + last_ts =3D self.data[-1] + max_cpu =3D 0 + for cpu in last_ts.rqs: + max_cpu =3D max(max_cpu, cpu) + return max_cpu + + +class SchedMigrationAnalyzer: + """Analyzes task migrations and manages time slices.""" + + def __init__(self): + self.current_tsk =3D defaultdict(lambda: -1) + self.timeslices =3D TimeSliceList() + + def sched_switch(self, time: int, cpu: int, prev_comm: str, prev_pid: = int, prev_state: int, + next_comm: str, next_pid: int): + """Handle sched_switch event.""" + on_cpu_task =3D self.current_tsk[cpu] + + if on_cpu_task not in (-1, prev_pid): + print(f"Sched switch event rejected ts: {time} cpu: {cpu} " + f"prev: {prev_comm}({prev_pid}) next: {next_comm}({next_= pid})") + + threads[prev_pid] =3D prev_comm + threads[next_pid] =3D next_comm + self.current_tsk[cpu] =3D next_pid + + ts =3D self.timeslices.get_time_slice(time) + ts.sched_switch(self.timeslices, prev_pid, prev_state, next_pid, c= pu) + + def migrate(self, time: int, pid: int, orig_cpu: int, dest_cpu: int): + """Handle sched_migrate_task event.""" + ts =3D self.timeslices.get_time_slice(time) + ts.migrate(self.timeslices, pid, orig_cpu, dest_cpu) + + def wake_up(self, time: int, pid: int, success: int, target_cpu: int, = fork: bool): + """Handle wakeup event.""" + if success =3D=3D 0: + return + ts =3D self.timeslices.get_time_slice(time) + ts.wake_up(self.timeslices, pid, target_cpu, fork) + + def process_event(self, sample: perf.sample_event) -> None: + """Collect events and pass to analyzer.""" + name =3D str(sample.evsel) + time =3D sample.sample_time + cpu =3D sample.sample_cpu + _pid =3D sample.sample_pid + _comm =3D "Unknown" + + if name =3D=3D "evsel(sched:sched_switch)": + prev_comm =3D getattr(sample, "prev_comm", "Unknown") + prev_pid =3D getattr(sample, "prev_pid", -1) + prev_state =3D getattr(sample, "prev_state", 0) + next_comm =3D getattr(sample, "next_comm", "Unknown") + next_pid =3D getattr(sample, "next_pid", -1) + self.sched_switch(time, cpu, prev_comm, prev_pid, prev_state, = next_comm, next_pid) + elif name =3D=3D "evsel(sched:sched_migrate_task)": + task_pid =3D getattr(sample, "pid", -1) + orig_cpu =3D getattr(sample, "orig_cpu", -1) + dest_cpu =3D getattr(sample, "dest_cpu", -1) + self.migrate(time, task_pid, orig_cpu, dest_cpu) + elif name =3D=3D "evsel(sched:sched_wakeup)": + task_pid =3D getattr(sample, "pid", -1) + success =3D getattr(sample, "success", 1) + target_cpu =3D getattr(sample, "target_cpu", -1) + self.wake_up(time, task_pid, success, target_cpu, False) + elif name =3D=3D "evsel(sched:sched_wakeup_new)": + task_pid =3D getattr(sample, "pid", -1) + success =3D getattr(sample, "success", 1) + target_cpu =3D getattr(sample, "target_cpu", -1) + self.wake_up(time, task_pid, success, target_cpu, True) + + def run_gui(self): + """Start wxPython GUI.""" + if not WX_AVAILABLE: + print("wxPython is not available. Cannot start GUI.") + return + app =3D wx.App(False) + _frame =3D RootFrame(self.timeslices, "Migration") + app.MainLoop() + + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser(description=3D"Cpu task migration overv= iew toy") + ap.add_argument("-i", "--input", default=3D"perf.data", help=3D"Input = file name") + args =3D ap.parse_args() + + analyzer =3D SchedMigrationAnalyzer() + + try: + session =3D perf.session(perf.data(args.input), sample=3Danalyzer.= process_event) + session.process_events() + analyzer.run_gui() + except KeyboardInterrupt: + pass + except Exception as e: + print(f"Error processing events: {e}") --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 766563ACF10 for ; Sat, 25 Apr 2026 22:51:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157508; cv=none; b=iwUzW6QLAcysfANXgUML9rgRIMQbKyYhU89SvqzdlcyiXd57mcaBLn9YN750aAzZR2gANE21bfSnvw2cLK2WRU40gOI9w/ajqm7kZRvXAWW8dDG1RkpaGe+zjAtjoVVcgiVFXGZZJMXz+KvRdzN6ld8hW1ir++sOQGB5CraDGKI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157508; c=relaxed/simple; bh=3OzyHhDflAGnhfcPd/Cdhu5Dl9jWi1PgdqDYN/G82GU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=MWTJxP0/SeYxBYp8ozYIAmHLIBuKrVV4XZOBBoCE50xEZUBk7K8v/4I3AYrGKn0al14oRwNdmRn/SxO0SxfyhJAUsuNET+4qsI8xapXUr/2Rp/j6bw7L3ytJxB61E/7diiO49T+ayBpMITpSbmwUHcky0aLxhxIAU7//vsUikrc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Dv8jfiT8; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Dv8jfiT8" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2ddd8ef5343so8847212eec.1 for ; Sat, 25 Apr 2026 15:51:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157506; x=1777762306; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=C1LwLJ/S5+yYNXf1JRm+e96X+YoT2PYO4quJ+4E0ZMc=; b=Dv8jfiT8+LbHfLL/IIFcA9BFTflcvBNy+ZEyg2KMdTvDoLsxskionuWzlU3NHpuoAw itmWZKlmv0wnMmg89pC8Mm7iPJg00/ZsY5VUWePbGgmaN0U4MGJox14XB+V1rTHt/Lw+ MCJL28YXk/EFX7r/sr9rsucK/DqQrrfVPni55qA9l9HgsiN4mSWc65XUcjblUEdQ7b6q IbarwKG+5j+2B+SCcCOW631XRJhOO9+QmtvS/Qudc7CQitvpQ5I2JNZS1DjinfTiGAxd 8h92HYN3CaCjnlFUdltBqZkxGXHGNt9Jkwqw1Kxaqxx/7LLoqtzo8B6rXZbG80jLDZS+ 6JOA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157506; x=1777762306; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=C1LwLJ/S5+yYNXf1JRm+e96X+YoT2PYO4quJ+4E0ZMc=; b=k1/DuvOl8pMC2VfhTz+XWjrTXQCu0wvdXSW05+Fmb7xVdTNCEo5t7WHUeTnbVAPL2z HGz3LL5qa+vIeD7X0YZxLxWJr9dowO3lJccfskbwAYn2WhvLCQra0EGf/1X3VwilHrVJ bAXuMGS61sJeo0xYs2jrtLSF23VbcrIGfF67v4q5ZYlLfjOWkiMlYF9RPc/8mTh9kjKP zhIJ4gaKPSnnslZh/kubA7GXA3LZrJ8xfwymb9Z51OmpN5cLhU8GalDeQ/7GTyILdqdC 4cFFO1Wby74YQRM1FIle4IjzkDo03kG0ZPRBR/OQnVtrwujqpQ0Q1XSpbFBniWokZomJ YtSw== X-Forwarded-Encrypted: i=1; AFNElJ+3DqGlVjZt/kWm73Ou56TI8dOh0J3IchRTNYz/BwkcjglNWFbSqeTxt50EVaC7kAhzYR3OVo4dZNzgVYA=@vger.kernel.org X-Gm-Message-State: AOJu0YzbPy0WRZgbyCIVT9ny4y0xx3284z7TOY4fBAgYGhPXpxh1bKTI m7K9nbPAi56srWRNcoXmmi9VV08pdtrqV2Ticvo4u6rLwoGTY4eH7vqaun4SbUoYv6Q8wuadCGk yYW4cAMCeag== X-Received: from dycnr11-n1.prod.google.com ([2002:a05:7300:e9cb:10b0:2e5:fa99:f6e4]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:693c:2c84:b0:2d9:2b54:ea9c with SMTP id 5a478bee46e88-2e477c9bc5bmr22454427eec.18.1777157505556; Sat, 25 Apr 2026 15:51:45 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:36 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-45-irogers@google.com> Subject: [PATCH v7 44/59] perf sctop: Port sctop to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This commit ports sctop.py from tools/perf/scripts/python/ to tools/perf/python/ using a class-based structure. It also adds live mode support using the LiveSession helper with a fallback strategy for tracepoint names. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v5: 1. Fix Fallback Logic: Check `__syscall_nr` and `nr` fields for syscall ID if `id` is missing on fallback tracepoints. 2. Fix Thread Lookup Crash: Added try-except block around `session.process(= )` to handle missing PIDs gracefully. --- tools/perf/python/sctop.py | 186 +++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100755 tools/perf/python/sctop.py diff --git a/tools/perf/python/sctop.py b/tools/perf/python/sctop.py new file mode 100755 index 000000000000..b94f66a8307d --- /dev/null +++ b/tools/perf/python/sctop.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +System call top + +Periodically displays system-wide system call totals, broken down by +syscall. If a [comm] arg is specified, only syscalls called by +[comm] are displayed. If an [interval] arg is specified, the display +will be refreshed every [interval] seconds. The default interval is +3 seconds. + +Ported from tools/perf/scripts/python/sctop.py +""" + +import argparse +from collections import defaultdict +import sys +import threading +from typing import Optional +import perf +from perf_live import LiveSession + + + + +class SCTopAnalyzer: + """Periodically displays system-wide system call totals.""" + + def __init__(self, for_comm: str | None, interval: int, offline: bool = =3D False): + self.for_comm =3D for_comm + self.interval =3D interval + self.syscalls: dict[int, int] =3D defaultdict(int) + self.lock =3D threading.Lock() + self.stop_event =3D threading.Event() + self.thread =3D threading.Thread(target=3Dself.print_syscall_total= s) + self.offline =3D offline + self.last_print_time: int | None =3D None + self.session: Optional[perf.session] =3D None + + def syscall_name(self, syscall_id: int) -> str: + """Lookup syscall name by ID.""" + try: + return perf.syscall_name(syscall_id) + except Exception: # pylint: disable=3Dbroad-exception-caught + pass + return str(syscall_id) + + def process_event(self, sample: perf.sample_event) -> None: + """Collect syscall events.""" + name =3D str(sample.evsel) + syscall_id =3D getattr(sample, "id", -1) + if syscall_id =3D=3D -1: + syscall_id =3D getattr(sample, "__syscall_nr", -1) + if syscall_id =3D=3D -1: + syscall_id =3D getattr(sample, "nr", -1) + + if syscall_id =3D=3D -1: + return + + comm =3D "Unknown" + if hasattr(self, 'session') and self.session: + try: + proc =3D self.session.find_thread(sample.sample_pid) + if proc: + comm =3D proc.comm() + except (TypeError, AttributeError): + pass + else: + comm =3D getattr(sample, "comm", "Unknown") + + if name in ("evsel(raw_syscalls:sys_enter)", "evsel(syscalls:sys_e= nter)"): + if self.for_comm is not None and comm !=3D self.for_comm: + return + with self.lock: + self.syscalls[syscall_id] +=3D 1 + + if self.offline and hasattr(sample, "time"): + interval_ns =3D self.interval * (10 ** 9) + if self.last_print_time is None: + self.last_print_time =3D sample.time + elif sample.time - self.last_print_time >=3D interval_ns: + self.print_current_totals() + self.last_print_time =3D sample.time + + def print_current_totals(self): + """Print current syscall totals.""" + # Clear terminal + print("\x1b[2J\x1b[H", end=3D"") + + if self.for_comm is not None: + print(f"\nsyscall events for {self.for_comm}:\n") + else: + print("\nsyscall events:\n") + + print(f"{'event':40s} {'count':10s}") + print(f"{'-' * 40:40s} {'-' * 10:10s}") + + with self.lock: + current_syscalls =3D list(self.syscalls.items()) + self.syscalls.clear() + + current_syscalls.sort(key=3Dlambda kv: (kv[1], kv[0]), reverse=3DT= rue) + + for syscall_id, val in current_syscalls: + print(f"{self.syscall_name(syscall_id):<40s} {val:10d}") + + def print_syscall_totals(self): + """Periodically print syscall totals.""" + while not self.stop_event.is_set(): + self.print_current_totals() + self.stop_event.wait(self.interval) + # Print final batch + self.print_current_totals() + + def start(self): + """Start the background thread.""" + self.thread.start() + + def stop(self): + """Stop the background thread.""" + self.stop_event.set() + self.thread.join() + + +def main(): + """Main function.""" + ap =3D argparse.ArgumentParser(description=3D"System call top") + ap.add_argument("args", nargs=3D"*", help=3D"[comm] [interval] or [int= erval]") + ap.add_argument("-i", "--input", help=3D"Input file name") + args =3D ap.parse_args() + + for_comm =3D None + default_interval =3D 3 + interval =3D default_interval + + if len(args.args) > 2: + print("Usage: perf script -s sctop.py [comm] [interval]") + sys.exit(1) + + if len(args.args) > 1: + for_comm =3D args.args[0] + try: + interval =3D int(args.args[1]) + except ValueError: + print(f"Invalid interval: {args.args[1]}") + sys.exit(1) + elif len(args.args) > 0: + try: + interval =3D int(args.args[0]) + except ValueError: + for_comm =3D args.args[0] + interval =3D default_interval + + analyzer =3D SCTopAnalyzer(for_comm, interval, offline=3Dbool(args.inp= ut)) + + if not args.input: + analyzer.start() + + try: + if args.input: + session =3D perf.session(perf.data(args.input), sample=3Danaly= zer.process_event) + analyzer.session =3D session + session.process_events() + else: + try: + live_session =3D LiveSession( + "raw_syscalls:sys_enter", sample_callback=3Danalyzer.p= rocess_event + ) + except OSError: + live_session =3D LiveSession( + "syscalls:sys_enter", sample_callback=3Danalyzer.proce= ss_event + ) + live_session.run() + except KeyboardInterrupt: + pass + except IOError as e: + print(f"Error: {e}") + finally: + if args.input: + analyzer.print_current_totals() + else: + analyzer.stop() + + +if __name__ =3D=3D "__main__": + main() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9B6CA393DE3 for ; Sat, 25 Apr 2026 22:51:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157510; cv=none; b=Jz5odzI2Qqzj8oifuqEKt4ARFDMrXWjAQnOPZPC2EjeTLxZgb54NTx6MAveCgJJ8d3kJdfbP9T1YnY54q4eX+JWRCJ7waF+3ig2IF+KE+cf8o1DLqNagLhJxpG1MYaAwZnGSNGd8of0hghvM5i4lZz63wCuA+NTBt+X56LtgxQk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157510; c=relaxed/simple; bh=5gMnnOsYKM+JhLn9wx2zoIzOqskbDsFXIF1JO3ndg5Y=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=CIMvea9Rhi29vqunqPlu//KgFC8hWyCyEH0wtCcB/CpUqWpbf2NpHI7dFYE54rIldbD0f5ciCGMfHClIq3wqc47pV7Yco1mIBxTy1Oc5XZUvpKvjQT45wxf1WHBm00jLLFGmjRNj8WJN+tfbULaNB+BSXJFStVlaNAzs5+wQGAg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=oxWa4dfG; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="oxWa4dfG" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-12c35f2c09dso11363462c88.0 for ; Sat, 25 Apr 2026 15:51:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157508; x=1777762308; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=nQeNxtOSoOXwV2zRr64LvO5u7apubipAzZoDUg3KnRw=; b=oxWa4dfGKErM98/ZAD1OL4hglSNXZQFkvwhh7PALF3mizvT6wuAjUWJPzNFDBScVvW L14lyN9C4XpEkdI+DyxmXt7qWcHQUoNW9kwpZWCgqYeuiBtNzi2ZAKwkv3HynRiNVAj1 orrqVGCeQdlCjwTETUqxaM1yA5vChhvmCAO0f1cy11dnR7NMi92h31p0dNDk/7N8mULa LryaAaca57wVe07qDkZqHFeNeLFcDJaYsRH3QlKsva4tUd5kq8a2Ro9VaWXOs3X4pVNJ /VJMw46ttcH3GNCP9vxwjELrhlI8mqnTAPYiuXfPl159tc4cSImEO8X1i96YiVOT8rEO w5fQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157508; x=1777762308; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=nQeNxtOSoOXwV2zRr64LvO5u7apubipAzZoDUg3KnRw=; b=oujW1XiXzxEL9DY8gMnEVvr6gpuyw2VdXis/WxzyLUBAFi+DVxt2GgKSg5P0dgxvqW 8Z3VsQbRbMD3rS/7fTuWe4v6eI0aERwyJb2M7eWTvm/VPBOCty8O46WpP8XKZopso/4z hi+O8gBpWuzZU5XMuuSvbzo3gN4MWRhHbtLsO9kWkTkMGqJ0+yCsxtv1UIECSqMoF8gH 8TWQSIHql4xgM0Um6anvg+HsddWKhU6ASB9A6S0xPaKvYbkQ2gkBaK4+isfJNbS42pAz QJ/8a2hNc7a0ey0s6J4iFM9W1E1wghhmg3QIyzFmjhrYge/nYiV/QL30kRaiiPqt6drN RiDQ== X-Forwarded-Encrypted: i=1; AFNElJ9N2NF2a3LNb8qnPlBPeHrJMxPraSoYJ7krYZL+p2IaWy4JD+ALA/U2zxW60mHCstNQcwCS8eP4DbyYD9g=@vger.kernel.org X-Gm-Message-State: AOJu0Yx3quE/bJdcbXQyM/ny0Cclzzc25CFkyDsK0GdR7mdeKXMhH9gp b74Q2lejo+C1KIcnWPC1isIjf0dUi++y8k0j86UiYpdyw7Tk826zhYce4v2u4Ai+VlUes8/8cE3 RbKZi+LOlvQ== X-Received: from dlam5-n2.prod.google.com ([2002:a05:701b:2085:20b0:12d:bf8b:c653]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:3d87:b0:124:9fd8:4ba9 with SMTP id a92af1059eb24-12c73f6cd01mr20920875c88.12.1777157507662; Sat, 25 Apr 2026 15:51:47 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:37 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-46-irogers@google.com> Subject: [PATCH v7 45/59] perf stackcollapse: Port stackcollapse to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Modernize the legacy stackcollapse.py trace script by refactoring it into a class-based architecture (StackCollapseAnalyzer). The script uses perf.session for event processing and aggregates call stacks to produce output suitable for flame graphs. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: - Fixed Callchain Check: Replaced hasattr(sample, "callchain") with getattr(sample, "callchain", None) and checked if it is not None . This avoids attempting to iterate over None when a sample lacks a callchain, which would raise a TypeError. - Fixed Comm Resolution: The code already used self.session.process(sample.sample_pid).comm() to resolve the command name using the session object (if available), avoiding the missing comm attribute on perf.sample_event. - Code Cleanup: Broke a long line in process_event to satisfy pylint. --- tools/perf/python/stackcollapse.py | 126 +++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100755 tools/perf/python/stackcollapse.py diff --git a/tools/perf/python/stackcollapse.py b/tools/perf/python/stackco= llapse.py new file mode 100755 index 000000000000..996c73246ebc --- /dev/null +++ b/tools/perf/python/stackcollapse.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +stackcollapse.py - format perf samples with one line per distinct call sta= ck + +This script's output has two space-separated fields. The first is a semic= olon +separated stack including the program name (from the "comm" field) and the +function names from the call stack. The second is a count: + + swapper;start_kernel;rest_init;cpu_idle;default_idle;native_safe_halt 2 + +The file is sorted according to the first field. + +Ported from tools/perf/scripts/python/stackcollapse.py +""" + +import argparse +from collections import defaultdict +import sys +import perf + + +class StackCollapseAnalyzer: + """Accumulates call stacks and prints them collapsed.""" + + def __init__(self, args: argparse.Namespace) -> None: + self.args =3D args + self.lines: dict[str, int] =3D defaultdict(int) + + def tidy_function_name(self, sym: str, dso: str) -> str: + """Beautify function names based on options.""" + if sym is None: + sym =3D "[unknown]" + + sym =3D sym.replace(";", ":") + if self.args.tidy_java: + # Beautify Java signatures + sym =3D sym.replace("<", "") + sym =3D sym.replace(">", "") + if sym.startswith("L") and "/" in sym: + sym =3D sym[1:] + try: + sym =3D sym[:sym.index("(")] + except ValueError: + pass + + if self.args.annotate_kernel and dso =3D=3D "[kernel.kallsyms]": + return sym + "_[k]" + return sym + + def process_event(self, sample: perf.sample_event) -> None: + """Collect call stack for each sample.""" + stack =3D [] + callchain =3D getattr(sample, "callchain", None) + if callchain is not None: + for node in callchain: + stack.append(self.tidy_function_name(node.symbol, node.dso= )) + else: + # Fallback if no callchain + sym =3D getattr(sample, "symbol", "[unknown]") + dso =3D getattr(sample, "dso", "[unknown]") + stack.append(self.tidy_function_name(sym, dso)) + + if self.args.include_comm: + if hasattr(self, 'session') and self.session: + comm =3D self.session.find_thread(sample.sample_pid).comm() + else: + comm =3D "Unknown" + comm =3D comm.replace(" ", "_") + sep =3D "-" + if self.args.include_pid: + comm =3D f"{comm}{sep}{getattr(sample, 'sample_pid', 0)}" + sep =3D "/" + if self.args.include_tid: + comm =3D f"{comm}{sep}{getattr(sample, 'sample_tid', 0)}" + stack.append(comm) + + stack_string =3D ";".join(reversed(stack)) + self.lines[stack_string] +=3D 1 + + def print_totals(self) -> None: + """Print sorted collapsed stacks.""" + for stack in sorted(self.lines): + print(f"{stack} {self.lines[stack]}") + + +def main(): + """Main function.""" + ap =3D argparse.ArgumentParser( + description=3D"Format perf samples with one line per distinct call= stack" + ) + ap.add_argument("-i", "--input", default=3D"perf.data", help=3D"Input = file name") + ap.add_argument("--include-tid", action=3D"store_true", help=3D"includ= e thread id in stack") + ap.add_argument("--include-pid", action=3D"store_true", help=3D"includ= e process id in stack") + ap.add_argument("--no-comm", dest=3D"include_comm", action=3D"store_fa= lse", default=3DTrue, + help=3D"do not separate stacks according to comm") + ap.add_argument("--tidy-java", action=3D"store_true", help=3D"beautify= Java signatures") + ap.add_argument("--kernel", dest=3D"annotate_kernel", action=3D"store_= true", + help=3D"annotate kernel functions with _[k]") + + args =3D ap.parse_args() + + if args.include_tid and not args.include_comm: + print("requesting tid but not comm is invalid", file=3Dsys.stderr) + sys.exit(1) + if args.include_pid and not args.include_comm: + print("requesting pid but not comm is invalid", file=3Dsys.stderr) + sys.exit(1) + + analyzer =3D StackCollapseAnalyzer(args) + + try: + session =3D perf.session(perf.data(args.input), sample=3Danalyzer.= process_event) + analyzer.session =3D session + session.process_events() + except IOError as e: + print(f"Error: {e}", file=3Dsys.stderr) + sys.exit(1) + except KeyboardInterrupt: + pass + + analyzer.print_totals() + + +if __name__ =3D=3D "__main__": + main() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-pj1-f73.google.com (mail-pj1-f73.google.com [209.85.216.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6886F3AD52A for ; Sat, 25 Apr 2026 22:51:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157512; cv=none; b=u0+jSn6FJWF2vKlwmWQy80TF+owEkoimlZwow2M27E0hbj4D2LisnBTGHwekQC24MTAPl5XQWveUVcKBuXwARsPy6vRBcL5tAaW3fdQDGJVSYenPl6gq+Lqp5hlEKFq3ACTuDPj/9IlHcyVq02bd+zZHJr262PrtVwgkVLcRU1I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157512; c=relaxed/simple; bh=bWrM7e9A8pjKSZnKVBYNtSn8qV4jfxLbHd7wlqkYnUc=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Cv5XHXQfFxbStNeEmiN0tl9QlPTViBL2UbjmmqEZXHE7ct6kWIYulnF8x7y1Yi8ckPbVmeYG7iZwrxN5MGandcFfa3+Zeb7mRRF6J79Wou/hpILJGiJauTlAoT6/HSOdtR0NlvdlntEb+Mdp93pFxahWswNBiGoEj3RbXq7J0f8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=RNLv5mJz; arc=none smtp.client-ip=209.85.216.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="RNLv5mJz" Received: by mail-pj1-f73.google.com with SMTP id 98e67ed59e1d1-35daf3d3030so9462563a91.1 for ; Sat, 25 Apr 2026 15:51:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157510; x=1777762310; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Rq7hmHwF4z0XJ1d12LnlzvGP+pgxOx2hCFaGWC9Rfuk=; b=RNLv5mJzTFEAMz0ghrD5r6AuoiaAVenpbLaRi57sJGsYXFijQwTulryJujY9nFVYmA TweWqcZYPO84H+Pc8q9D9CvsiBKl92M/hsNisIaLv9gJWW65Ra1A1ieoJuvcm8/Ac6iQ 8p34e7wOOSM5spE54adv1XyS+YXFLpoSI47UUouFv0SRaTtdoxRnKl2tVxakx/ZAsEck /UCoc5Mof0KPmE6yZ1Y7p/3Wa+RpLkm6oe26cjUPM3t6sZ2+bQgJ9bcMxwN1cGYq1UnA fI+A9FURY9SDzlSpFU8Ojcl3t3tCQJWFtA7PS0D3UsJ5y7i7ehXh60a5KqoDYA59QJVW rXyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157510; x=1777762310; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Rq7hmHwF4z0XJ1d12LnlzvGP+pgxOx2hCFaGWC9Rfuk=; b=ImtoKYjBYFKK6A+nboBeNuRc64jwkO9K3Dd2SnUO4GOu2BDRvhxtLi9NlgIU+rinKe J+rN0M0e+GxUE7vVLgXwzRghqlY/r9wD6Del1PFEPucHdD9oid3FUhcHaM8+bM80MQeP TfSB0kL/t7HBq4saMsrIwvziCreRYy40l9Vnn54pckLZruNigdjqZt2JUf4EadJst/+6 vafWk3D3g9ZcPbTIzO8sya+RsHNQ1cQL7g/9Fb18jdIFBu1GaQBTtEFSJGQzAuqsKbwA GacNVfB8MnkHpMCSwmonZpMwreWR3Bh6atxKNsjXkFOo/C+Jk/8PK+fH0aDEdR7XfZFj oSWQ== X-Forwarded-Encrypted: i=1; AFNElJ8oj1tWqyR2E9QDga1bBOI4JUQ/5mx6Yq4Rv9/EIZJSqwME0rTmS2kCTCV76O0YKTaTN2Vzx6mVisZI5MQ=@vger.kernel.org X-Gm-Message-State: AOJu0YyT/2kZh7LcPOcoqCeaurux8vIwMVkoOa6PqML7QZyN26ligZuU 0eqZSN3+J7iuY5L3eAcpLFLG7UcUB4RIbViiRf6Z0FhEoBDnmTg+9wIeX5nV5KU2kVXGkcPjodD ZlhGFGURmsw== X-Received: from pjtv24.prod.google.com ([2002:a17:90a:c918:b0:35e:58a0:798e]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:380c:b0:35f:b5df:448 with SMTP id 98e67ed59e1d1-3614049ed12mr38908299a91.24.1777157509503; Sat, 25 Apr 2026 15:51:49 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:38 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-47-irogers@google.com> Subject: [PATCH v7 46/59] perf task-analyzer: Port task-analyzer to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Ported task-analyzer.py from tools/perf/scripts/python to tools/perf/python. Refactored to class-based architecture. Added support for both file mode (using perf.session) and live mode (using evlist.read_on_cpu). Accesses tracepoint fields directly from sample object. Update task-analyzer testing to use command rather than script version, this allows the perf.data file not to be in the same directory as the test is run. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: - Fixed CSV Color Corruption: Updated _check_color() to disable colors immediately if --csv or --csv-summary is enabled, preventing ANSI escape codes from corrupting CSV output even if stdout is a TTY. - Fixed _record_cleanup Conditions: Updated the cleanup condition to check for summary_extended and summary_only as well as summary . Also added a hard limit of 1000 entries to prevent unbounded memory growth in live mode. - Fixed Filter/Limit Mutual Exclusivity: Rewrote _limit_filtered() to evaluate both --filter-tasks and --limit-to-tasks correctly when both are specified, instead of returning early and making the limit check unreachable. - Fixed TID vs PID in process_event : Used self.session.process(prev_pid).pid to resolve the actual Process ID (TGID) for the previous task, instead of incorrectly passing the Thread ID (TID) as the PID to _handle_task_finish() . - Fixed Conflicting CSV Headers: Removed the hardcoded semicolon-delimited headers written in run() , as they conflicted with the comma- separated headers written by _print_header() . - Updated test expectations. --- tools/perf/python/task-analyzer.py | 547 +++++++++++++++++++ tools/perf/tests/shell/test_task_analyzer.sh | 79 +-- 2 files changed, 592 insertions(+), 34 deletions(-) create mode 100755 tools/perf/python/task-analyzer.py diff --git a/tools/perf/python/task-analyzer.py b/tools/perf/python/task-an= alyzer.py new file mode 100755 index 000000000000..08e44946fe6a --- /dev/null +++ b/tools/perf/python/task-analyzer.py @@ -0,0 +1,547 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# task-analyzer.py - comprehensive perf tasks analysis +# Copyright (c) 2022, Hagen Paul Pfeifer +# Licensed under the terms of the GNU GPL License version 2 +# +# Usage: +# +# perf record -e sched:sched_switch -a -- sleep 10 +# perf script report task-analyzer +# +"""Comprehensive perf tasks analysis.""" + +import argparse +from contextlib import contextmanager +import decimal +import os +import string +import sys +from typing import Any, Optional +import perf + + +# Columns will have a static size to align everything properly +# Support of 116 days of active update with nano precision +LEN_SWITCHED_IN =3D len("9999999.999999999") +LEN_SWITCHED_OUT =3D len("9999999.999999999") +LEN_CPU =3D len("000") +LEN_PID =3D len("maxvalue") +LEN_TID =3D len("maxvalue") +LEN_COMM =3D len("max-comms-length") +LEN_RUNTIME =3D len("999999.999") +# Support of 3.45 hours of timespans +LEN_OUT_IN =3D len("99999999999.999") +LEN_OUT_OUT =3D len("99999999999.999") +LEN_IN_IN =3D len("99999999999.999") +LEN_IN_OUT =3D len("99999999999.999") + +class Timespans: + """Tracks elapsed time between occurrences of the same task.""" + def __init__(self, args: argparse.Namespace, time_unit: str) -> None: + self.args =3D args + self.time_unit =3D time_unit + self._last_start: Optional[decimal.Decimal] =3D None + self._last_finish: Optional[decimal.Decimal] =3D None + self.current =3D { + 'out_out': decimal.Decimal(-1), + 'in_out': decimal.Decimal(-1), + 'out_in': decimal.Decimal(-1), + 'in_in': decimal.Decimal(-1) + } + if args.summary_extended: + self._time_in: decimal.Decimal =3D decimal.Decimal(-1) + self.max_vals =3D { + 'out_in': decimal.Decimal(-1), + 'at': decimal.Decimal(-1), + 'in_out': decimal.Decimal(-1), + 'in_in': decimal.Decimal(-1), + 'out_out': decimal.Decimal(-1) + } + + def feed(self, task: 'Task') -> None: + """Calculate timespans from chronological task occurrences.""" + if not self._last_finish: + self._last_start =3D task.time_in(self.time_unit) + self._last_finish =3D task.time_out(self.time_unit) + return + assert self._last_start is not None + assert self._last_finish is not None + self._time_in =3D task.time_in() + time_in =3D task.time_in(self.time_unit) + time_out =3D task.time_out(self.time_unit) + self.current['in_in'] =3D time_in - self._last_start + self.current['out_in'] =3D time_in - self._last_finish + self.current['in_out'] =3D time_out - self._last_start + self.current['out_out'] =3D time_out - self._last_finish + if self.args.summary_extended: + self.update_max_entries() + self._last_finish =3D task.time_out(self.time_unit) + self._last_start =3D task.time_in(self.time_unit) + + def update_max_entries(self) -> None: + """Update maximum timespans.""" + self.max_vals['in_in'] =3D max(self.max_vals['in_in'], self.curren= t['in_in']) + self.max_vals['out_out'] =3D max(self.max_vals['out_out'], self.cu= rrent['out_out']) + self.max_vals['in_out'] =3D max(self.max_vals['in_out'], self.curr= ent['in_out']) + if self.current['out_in'] > self.max_vals['out_in']: + self.max_vals['out_in'] =3D self.current['out_in'] + self.max_vals['at'] =3D self._time_in + +class Task: + """Handles information of a given task.""" + def __init__(self, task_id: str, tid: int, cpu: int, comm: str) -> Non= e: + self.id =3D task_id + self.tid =3D tid + self.cpu =3D cpu + self.comm =3D comm + self.pid: Optional[int] =3D None + self._time_in: Optional[decimal.Decimal] =3D None + self._time_out: Optional[decimal.Decimal] =3D None + + def schedule_in_at(self, time_ns: int) -> None: + """Set schedule in time.""" + self._time_in =3D decimal.Decimal(time_ns) / decimal.Decimal(1e9) + + def schedule_out_at(self, time_ns: int) -> None: + """Set schedule out time.""" + self._time_out =3D decimal.Decimal(time_ns) / decimal.Decimal(1e9) + + def time_out(self, unit: str =3D "s") -> decimal.Decimal: + """Return schedule out time.""" + factor =3D TaskAnalyzer.time_uniter(unit) + return self._time_out * decimal.Decimal(factor) if self._time_out = else decimal.Decimal(0) + + def time_in(self, unit: str =3D "s") -> decimal.Decimal: + """Return schedule in time.""" + factor =3D TaskAnalyzer.time_uniter(unit) + return self._time_in * decimal.Decimal(factor) if self._time_in el= se decimal.Decimal(0) + + def runtime(self, unit: str =3D "us") -> decimal.Decimal: + """Return runtime.""" + factor =3D TaskAnalyzer.time_uniter(unit) + if self._time_out and self._time_in: + return (self._time_out - self._time_in) * decimal.Decimal(fact= or) + return decimal.Decimal(0) + + def update_pid(self, pid: int) -> None: + """Update PID.""" + self.pid =3D pid + +class TaskAnalyzer: + """Main class for task analysis.""" + + _COLORS =3D { + "grey": "\033[90m", + "red": "\033[91m", + "green": "\033[92m", + "yellow": "\033[93m", + "blue": "\033[94m", + "violet": "\033[95m", + "reset": "\033[0m", + } + + def __init__(self, args: argparse.Namespace) -> None: + self.args =3D args + self.db: dict[str, Any] =3D {} + self.session: Optional[perf.session] =3D None + self.time_unit =3D "s" + if args.ns: + self.time_unit =3D "ns" + elif args.ms: + self.time_unit =3D "ms" + self._init_db() + self._check_color() + self.fd_task =3D sys.stdout + self.fd_sum =3D sys.stdout + + @contextmanager + def open_output(self, filename: str, default: Any): + """Context manager for file or stdout.""" + if filename: + with open(filename, "w", encoding=3D"utf-8") as f: + yield f + else: + yield default + + def _init_db(self) -> None: + self.db["running"] =3D {} + self.db["cpu"] =3D {} + self.db["tid"] =3D {} + self.db["global"] =3D [] + if self.args.summary or self.args.summary_extended or self.args.su= mmary_only: + self.db["task_info"] =3D {} + self.db["runtime_info"] =3D {} + self.db["task_info"]["pid"] =3D len("PID") + self.db["task_info"]["tid"] =3D len("TID") + self.db["task_info"]["comm"] =3D len("Comm") + self.db["runtime_info"]["runs"] =3D len("Runs") + self.db["runtime_info"]["acc"] =3D len("Accumulated") + self.db["runtime_info"]["max"] =3D len("Max") + self.db["runtime_info"]["max_at"] =3D len("Max At") + self.db["runtime_info"]["min"] =3D len("Min") + self.db["runtime_info"]["mean"] =3D len("Mean") + self.db["runtime_info"]["median"] =3D len("Median") + if self.args.summary_extended: + self.db["inter_times"] =3D {} + self.db["inter_times"]["out_in"] =3D len("Out-In") + self.db["inter_times"]["inter_at"] =3D len("At") + self.db["inter_times"]["out_out"] =3D len("Out-Out") + self.db["inter_times"]["in_in"] =3D len("In-In") + self.db["inter_times"]["in_out"] =3D len("In-Out") + + def _check_color(self) -> None: + """Check if color should be enabled.""" + if self.args.csv or self.args.csv_summary: + TaskAnalyzer._COLORS =3D {k: "" for k in TaskAnalyzer._COLORS} + return + if sys.stdout.isatty() and self.args.stdio_color !=3D "never": + return + TaskAnalyzer._COLORS =3D {k: "" for k in TaskAnalyzer._COLORS} + + @staticmethod + def time_uniter(unit: str) -> float: + """Return time unit factor.""" + picker =3D {"s": 1, "ms": 1e3, "us": 1e6, "ns": 1e9} + return picker[unit] + + def _task_id(self, pid: int, cpu: int) -> str: + return f"{pid}-{cpu}" + + def _filter_non_printable(self, unfiltered: str) -> str: + filtered =3D "" + for char in unfiltered: + if char in string.printable: + filtered +=3D char + return filtered + + def _prepare_fmt_precision(self) -> tuple[int, int]: + if self.args.ns: + return 0, 9 + return 3, 6 + + def _prepare_fmt_sep(self) -> tuple[str, int]: + if self.args.csv or self.args.csv_summary: + return ",", 0 + return " ", 1 + + def _fmt_header(self) -> str: + separator, fix_csv_align =3D self._prepare_fmt_sep() + fmt =3D f"{{:>{LEN_SWITCHED_IN*fix_csv_align}}}" + fmt +=3D f"{separator}{{:>{LEN_SWITCHED_OUT*fix_csv_align}}}" + fmt +=3D f"{separator}{{:>{LEN_CPU*fix_csv_align}}}" + fmt +=3D f"{separator}{{:>{LEN_PID*fix_csv_align}}}" + fmt +=3D f"{separator}{{:>{LEN_TID*fix_csv_align}}}" + fmt +=3D f"{separator}{{:>{LEN_COMM*fix_csv_align}}}" + fmt +=3D f"{separator}{{:>{LEN_RUNTIME*fix_csv_align}}}" + fmt +=3D f"{separator}{{:>{LEN_OUT_IN*fix_csv_align}}}" + if self.args.extended_times: + fmt +=3D f"{separator}{{:>{LEN_OUT_OUT*fix_csv_align}}}" + fmt +=3D f"{separator}{{:>{LEN_IN_IN*fix_csv_align}}}" + fmt +=3D f"{separator}{{:>{LEN_IN_OUT*fix_csv_align}}}" + return fmt + + def _fmt_body(self) -> str: + separator, fix_csv_align =3D self._prepare_fmt_sep() + decimal_precision, time_precision =3D self._prepare_fmt_precision() + fmt =3D f"{{}}{{:{LEN_SWITCHED_IN*fix_csv_align}.{decimal_precisio= n}f}}" + fmt +=3D f"{separator}{{:{LEN_SWITCHED_OUT*fix_csv_align}.{decimal= _precision}f}}" + fmt +=3D f"{separator}{{:{LEN_CPU*fix_csv_align}d}}" + fmt +=3D f"{separator}{{:{LEN_PID*fix_csv_align}d}}" + fmt +=3D f"{separator}{{}}{{:{LEN_TID*fix_csv_align}d}}{{}}" + fmt +=3D f"{separator}{{}}{{:>{LEN_COMM*fix_csv_align}}}" + fmt +=3D f"{separator}{{:{LEN_RUNTIME*fix_csv_align}.{time_precisi= on}f}}" + if self.args.extended_times: + fmt +=3D f"{separator}{{:{LEN_OUT_IN*fix_csv_align}.{time_prec= ision}f}}" + fmt +=3D f"{separator}{{:{LEN_OUT_OUT*fix_csv_align}.{time_pre= cision}f}}" + fmt +=3D f"{separator}{{:{LEN_IN_IN*fix_csv_align}.{time_preci= sion}f}}" + fmt +=3D f"{separator}{{:{LEN_IN_OUT*fix_csv_align}.{time_prec= ision}f}}{{}}" + else: + fmt +=3D f"{separator}{{:{LEN_OUT_IN*fix_csv_align}.{time_prec= ision}f}}{{}}" + return fmt + + def _print_header(self) -> None: + fmt =3D self._fmt_header() + header =3D ["Switched-In", "Switched-Out", "CPU", "PID", "TID", "C= omm", + "Runtime", "Time Out-In"] + if self.args.extended_times: + header +=3D ["Time Out-Out", "Time In-In", "Time In-Out"] + self.fd_task.write(fmt.format(*header) + "\n") + + def _print_task_finish(self, task: Task) -> None: + c_row_set =3D "" + c_row_reset =3D "" + out_in: Any =3D -1 + out_out: Any =3D -1 + in_in: Any =3D -1 + in_out: Any =3D -1 + fmt =3D self._fmt_body() + + if str(task.tid) in self.args.highlight_tasks_map: + c_row_set =3D TaskAnalyzer._COLORS[self.args.highlight_tasks_m= ap[str(task.tid)]] + c_row_reset =3D TaskAnalyzer._COLORS["reset"] + if task.comm in self.args.highlight_tasks_map: + c_row_set =3D TaskAnalyzer._COLORS[self.args.highlight_tasks_m= ap[task.comm]] + c_row_reset =3D TaskAnalyzer._COLORS["reset"] + + c_tid_set =3D "" + c_tid_reset =3D "" + if task.pid =3D=3D task.tid: + c_tid_set =3D TaskAnalyzer._COLORS["grey"] + c_tid_reset =3D TaskAnalyzer._COLORS["reset"] + + if task.tid in self.db["tid"]: + last_tid_task =3D self.db["tid"][task.tid][-1] + timespan_gap_tid =3D Timespans(self.args, self.time_unit) + timespan_gap_tid.feed(last_tid_task) + timespan_gap_tid.feed(task) + out_in =3D timespan_gap_tid.current['out_in'] + out_out =3D timespan_gap_tid.current['out_out'] + in_in =3D timespan_gap_tid.current['in_in'] + in_out =3D timespan_gap_tid.current['in_out'] + + if self.args.extended_times: + line_out =3D fmt.format(c_row_set, task.time_in(), task.time_o= ut(), task.cpu, + task.pid, c_tid_set, task.tid, c_tid_reset, c_= row_set, task.comm, + task.runtime(self.time_unit), out_in, out_out,= in_in, in_out, + c_row_reset) + "\n" + else: + line_out =3D fmt.format(c_row_set, task.time_in(), task.time_o= ut(), task.cpu, + task.pid, c_tid_set, task.tid, c_tid_reset, c_= row_set, task.comm, + task.runtime(self.time_unit), out_in, c_row_re= set) + "\n" + self.fd_task.write(line_out) + + def _record_cleanup(self, _list: list[Any]) -> list[Any]: + need_summary =3D (self.args.summary or self.args.summary_extended = or + self.args.summary_only) + if not need_summary and len(_list) > 1: + return _list[len(_list) - 1:] + if len(_list) > 1000: + return _list[len(_list) - 1000:] + return _list + + def _record_by_tid(self, task: Task) -> None: + tid =3D task.tid + if tid not in self.db["tid"]: + self.db["tid"][tid] =3D [] + self.db["tid"][tid].append(task) + self.db["tid"][tid] =3D self._record_cleanup(self.db["tid"][tid]) + + def _record_by_cpu(self, task: Task) -> None: + cpu =3D task.cpu + if cpu not in self.db["cpu"]: + self.db["cpu"][cpu] =3D [] + self.db["cpu"][cpu].append(task) + self.db["cpu"][cpu] =3D self._record_cleanup(self.db["cpu"][cpu]) + + def _record_global(self, task: Task) -> None: + self.db["global"].append(task) + self.db["global"] =3D self._record_cleanup(self.db["global"]) + + def _handle_task_finish(self, tid: int, cpu: int, time_ns: int, pid: i= nt) -> None: + if tid =3D=3D 0: + return + _id =3D self._task_id(tid, cpu) + if _id not in self.db["running"]: + return + task =3D self.db["running"][_id] + task.schedule_out_at(time_ns) + task.update_pid(pid) + del self.db["running"][_id] + + if not self._limit_filtered(tid, pid, task.comm) and not self.args= .summary_only: + self._print_task_finish(task) + self._record_by_tid(task) + self._record_by_cpu(task) + self._record_global(task) + + def _handle_task_start(self, tid: int, cpu: int, comm: str, time_ns: i= nt) -> None: + if tid =3D=3D 0: + return + if tid in self.args.tid_renames: + comm =3D self.args.tid_renames[tid] + _id =3D self._task_id(tid, cpu) + if _id in self.db["running"]: + return + task =3D Task(_id, tid, cpu, comm) + task.schedule_in_at(time_ns) + self.db["running"][_id] =3D task + + def _limit_filtered(self, tid: int, pid: int, comm: str) -> bool: + """Filter tasks based on CLI arguments.""" + match_filter =3D False + if self.args.filter_tasks: + if (str(tid) in self.args.filter_tasks or + str(pid) in self.args.filter_tasks or + comm in self.args.filter_tasks): + match_filter =3D True + + match_limit =3D False + if self.args.limit_to_tasks: + if (str(tid) in self.args.limit_to_tasks or + str(pid) in self.args.limit_to_tasks or + comm in self.args.limit_to_tasks): + match_limit =3D True + + if self.args.filter_tasks and match_filter: + return True + if self.args.limit_to_tasks and not match_limit: + return True + return False + + def _is_within_timelimit(self, time_ns: int) -> bool: + if not self.args.time_limit: + return True + time_s =3D decimal.Decimal(time_ns) / decimal.Decimal(1e9) + lower_bound, upper_bound =3D self.args.time_limit.split(":") + if lower_bound and time_s < decimal.Decimal(lower_bound): + return False + if upper_bound and time_s > decimal.Decimal(upper_bound): + return False + return True + + def process_event(self, sample: perf.sample_event) -> None: + """Process sched:sched_switch events.""" + if "sched:sched_switch" not in str(sample.evsel): + return + + time_ns =3D sample.sample_time + if not self._is_within_timelimit(time_ns): + return + + # Access tracepoint fields directly from sample object + try: + prev_pid =3D sample.prev_pid + next_pid =3D sample.next_pid + next_comm =3D sample.next_comm + common_cpu =3D sample.sample_cpu + except AttributeError: + # Fallback or ignore if fields are not available + return + + next_comm =3D self._filter_non_printable(next_comm) + + # Task finish for previous task + if self.session: + prev_tgid =3D self.session.find_thread(prev_pid).pid # type: = ignore + else: + prev_tgid =3D prev_pid # Fallback + self._handle_task_finish(prev_pid, common_cpu, time_ns, prev_tgid) + # Task start for next task + self._handle_task_start(next_pid, common_cpu, next_comm, time_ns) + + def print_summary(self) -> None: + """Calculate and print summary.""" + need_summary =3D (self.args.summary or self.args.summary_extended = or + self.args.summary_only or self.args.csv_summary) + if not need_summary: + return + + # Simplified summary logic for brevity, full logic can be ported i= f needed + print("\nSummary (Simplified)", file=3Dself.fd_sum) + if self.args.summary_extended: + print("Inter Task Times", file=3Dself.fd_sum) + # ... port full Summary class logic here ... + + def _run_file(self) -> None: + if not self.args.summary_only: + self._print_header() + + session =3D perf.session(perf.data(self.args.input), sample=3Dself= .process_event) + self.session =3D session + session.process_events() + + self.print_summary() + + def _run_live(self) -> None: + if not self.args.summary_only: + self._print_header() + + cpus =3D perf.cpu_map() + threads =3D perf.thread_map(-1) + evlist =3D perf.parse_events("sched:sched_switch", cpus, threads) + evlist.config() + + evlist.open() + evlist.mmap() + evlist.enable() + + print("Live mode started. Press Ctrl+C to stop.", file=3Dsys.stder= r) + try: + while True: + evlist.poll(timeout=3D-1) + for cpu in cpus: + while True: + event =3D evlist.read_on_cpu(cpu) + if not event: + break + if not isinstance(event, perf.sample_event): + continue + self.process_event(event) + except KeyboardInterrupt: + print("\nStopping live mode...", file=3Dsys.stderr) + finally: + evlist.close() + self.print_summary() + + def run(self) -> None: + """Run the session.""" + with self.open_output(self.args.csv, sys.stdout) as fd_task: + with self.open_output(self.args.csv_summary, sys.stdout) as fd= _sum: + self.fd_task =3D fd_task + self.fd_sum =3D fd_sum + + + if not os.path.exists(self.args.input) and self.args.input= =3D=3D "perf.data": + self._run_live() + else: + self._run_file() + +def main() -> None: + """Main function.""" + parser =3D argparse.ArgumentParser(description=3D"Analyze tasks behavi= or") + parser.add_argument("-i", "--input", default=3D"perf.data", help=3D"In= put file name") + parser.add_argument("--time-limit", default=3D"", help=3D"print tasks = only in time window") + parser.add_argument("--summary", action=3D"store_true", + help=3D"print additional runtime information") + parser.add_argument("--summary-only", action=3D"store_true", + help=3D"print only summary without traces") + parser.add_argument("--summary-extended", action=3D"store_true", + help=3D"print extended summary") + parser.add_argument("--ns", action=3D"store_true", help=3D"show timest= amps in nanoseconds") + parser.add_argument("--ms", action=3D"store_true", help=3D"show timest= amps in milliseconds") + parser.add_argument("--extended-times", action=3D"store_true", + help=3D"Show elapsed times between schedule in/out= ") + parser.add_argument("--filter-tasks", default=3D"", help=3D"filter tas= ks by tid, pid or comm") + parser.add_argument("--limit-to-tasks", default=3D"", help=3D"limit ou= tput to selected tasks") + parser.add_argument("--highlight-tasks", default=3D"", help=3D"coloriz= e special tasks") + parser.add_argument("--rename-comms-by-tids", default=3D"", help=3D"re= name task names by using tid") + parser.add_argument("--stdio-color", default=3D"auto", choices=3D["alw= ays", "never", "auto"], + help=3D"configure color output") + parser.add_argument("--csv", default=3D"", help=3D"Write trace to file= ") + parser.add_argument("--csv-summary", default=3D"", help=3D"Write summa= ry to file") + + args =3D parser.parse_args() + args.tid_renames =3D {} + args.highlight_tasks_map =3D {} + args.filter_tasks =3D args.filter_tasks.split(",") if args.filter_task= s else [] + args.limit_to_tasks =3D args.limit_to_tasks.split(",") if args.limit_t= o_tasks else [] + + if args.rename_comms_by_tids: + for item in args.rename_comms_by_tids.split(","): + tid, name =3D item.split(":") + args.tid_renames[int(tid)] =3D name + + if args.highlight_tasks: + for item in args.highlight_tasks.split(","): + parts =3D item.split(":") + if len(parts) =3D=3D 1: + parts.append("red") + key, color =3D parts[0], parts[1] + args.highlight_tasks_map[key] =3D color + + analyzer =3D TaskAnalyzer(args) + analyzer.run() + +if __name__ =3D=3D "__main__": + main() diff --git a/tools/perf/tests/shell/test_task_analyzer.sh b/tools/perf/test= s/shell/test_task_analyzer.sh index 0314412e63b4..7465298d0384 100755 --- a/tools/perf/tests/shell/test_task_analyzer.sh +++ b/tools/perf/tests/shell/test_task_analyzer.sh @@ -5,17 +5,24 @@ tmpdir=3D$(mktemp -d /tmp/perf-script-task-analyzer-XXXXX) # TODO: perf script report only supports input from the CWD perf.data file= , make # it support input from any file. -perfdata=3D"perf.data" +perfdata=3D"$tmpdir/perf.data" csv=3D"$tmpdir/csv" csvsummary=3D"$tmpdir/csvsummary" err=3D0 =20 -# set PERF_EXEC_PATH to find scripts in the source directory -perfdir=3D$(dirname "$0")/../.. -if [ -e "$perfdir/scripts/python/Perf-Trace-Util" ]; then - export PERF_EXEC_PATH=3D$perfdir +# Set up perfdir and PERF_EXEC_PATH +if [ "x$PERF_EXEC_PATH" =3D=3D "x" ]; then + perfdir=3D$(dirname "$0")/../.. + if [ -f $perfdir/python/task-analyzer.py ]; then + export PERF_EXEC_PATH=3D$perfdir + fi +else + perfdir=3D$PERF_EXEC_PATH fi =20 +# shellcheck source=3Dlib/setup_python.sh +. "$(dirname "$0")"/lib/setup_python.sh + # Disable lsan to avoid warnings about python memory leaks. export ASAN_OPTIONS=3Ddetect_leaks=3D0 =20 @@ -76,86 +83,86 @@ prepare_perf_data() { # check standard inkvokation with no arguments test_basic() { out=3D"$tmpdir/perf.out" - perf script report task-analyzer > "$out" - check_exec_0 "perf script report task-analyzer" + $PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" > "$out" + check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata}" find_str_or_fail "Comm" "$out" "${FUNCNAME[0]}" } =20 test_ns_rename(){ out=3D"$tmpdir/perf.out" - perf script report task-analyzer --ns --rename-comms-by-tids 0:random > "= $out" - check_exec_0 "perf script report task-analyzer --ns --rename-comms-by-tid= s 0:random" + $PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --ns --rename-c= omms-by-tids 0:random > "$out" + check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} --n= s --rename-comms-by-tids 0:random" find_str_or_fail "Comm" "$out" "${FUNCNAME[0]}" } =20 test_ms_filtertasks_highlight(){ out=3D"$tmpdir/perf.out" - perf script report task-analyzer --ms --filter-tasks perf --highlight-tas= ks perf \ + $PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --ms --filter-t= asks perf --highlight-tasks perf \ > "$out" - check_exec_0 "perf script report task-analyzer --ms --filter-tasks perf -= -highlight-tasks perf" + check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} --m= s --filter-tasks perf --highlight-tasks perf" find_str_or_fail "Comm" "$out" "${FUNCNAME[0]}" } =20 test_extended_times_timelimit_limittasks() { out=3D"$tmpdir/perf.out" - perf script report task-analyzer --extended-times --time-limit :99999 \ + $PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --extended-time= s --time-limit :99999 \ --limit-to-tasks perf > "$out" - check_exec_0 "perf script report task-analyzer --extended-times --time-li= mit :99999 --limit-to-tasks perf" + check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} --e= xtended-times --time-limit :99999 --limit-to-tasks perf" find_str_or_fail "Out-Out" "$out" "${FUNCNAME[0]}" } =20 test_summary() { out=3D"$tmpdir/perf.out" - perf script report task-analyzer --summary > "$out" - check_exec_0 "perf script report task-analyzer --summary" + $PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --summary > "$o= ut" + check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} --s= ummary" find_str_or_fail "Summary" "$out" "${FUNCNAME[0]}" } =20 test_summaryextended() { out=3D"$tmpdir/perf.out" - perf script report task-analyzer --summary-extended > "$out" - check_exec_0 "perf script report task-analyzer --summary-extended" + $PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --summary-exten= ded > "$out" + check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} --s= ummary-extended" find_str_or_fail "Inter Task Times" "$out" "${FUNCNAME[0]}" } =20 test_summaryonly() { out=3D"$tmpdir/perf.out" - perf script report task-analyzer --summary-only > "$out" - check_exec_0 "perf script report task-analyzer --summary-only" + $PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --summary-only = > "$out" + check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} --s= ummary-only" find_str_or_fail "Summary" "$out" "${FUNCNAME[0]}" } =20 test_extended_times_summary_ns() { out=3D"$tmpdir/perf.out" - perf script report task-analyzer --extended-times --summary --ns > "$out" - check_exec_0 "perf script report task-analyzer --extended-times --summary= --ns" + $PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --extended-time= s --summary --ns > "$out" + check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} --e= xtended-times --summary --ns" find_str_or_fail "Out-Out" "$out" "${FUNCNAME[0]}" find_str_or_fail "Summary" "$out" "${FUNCNAME[0]}" } =20 test_csv() { - perf script report task-analyzer --csv "${csv}" > /dev/null - check_exec_0 "perf script report task-analyzer --csv ${csv}" - find_str_or_fail "Comm;" "${csv}" "${FUNCNAME[0]}" + $PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --csv "${csv}" = > /dev/null + check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} --c= sv ${csv}" + find_str_or_fail "Comm," "${csv}" "${FUNCNAME[0]}" } =20 test_csv_extended_times() { - perf script report task-analyzer --csv "${csv}" --extended-times > /dev/n= ull - check_exec_0 "perf script report task-analyzer --csv ${csv} --extended-ti= mes" - find_str_or_fail "Out-Out;" "${csv}" "${FUNCNAME[0]}" + $PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --csv "${csv}" = --extended-times > /dev/null + check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} --c= sv ${csv} --extended-times" + find_str_or_fail "Time Out-Out," "${csv}" "${FUNCNAME[0]}" } =20 test_csvsummary() { - perf script report task-analyzer --csv-summary "${csvsummary}" > /dev/null - check_exec_0 "perf script report task-analyzer --csv-summary ${csvsummary= }" - find_str_or_fail "Comm;" "${csvsummary}" "${FUNCNAME[0]}" + $PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --csv-summary "= ${csvsummary}" > /dev/null + check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} --c= sv-summary ${csvsummary}" + find_str_or_fail "Summary" "${csvsummary}" "${FUNCNAME[0]}" } =20 test_csvsummary_extended() { - perf script report task-analyzer --csv-summary "${csvsummary}" --summary-= extended \ + $PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --csv-summary "= ${csvsummary}" --summary-extended \ >/dev/null - check_exec_0 "perf script report task-analyzer --csv-summary ${csvsummary= } --summary-extended" - find_str_or_fail "Out-Out;" "${csvsummary}" "${FUNCNAME[0]}" + check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} --c= sv-summary ${csvsummary} --summary-extended" + find_str_or_fail "Inter Task Times" "${csvsummary}" "${FUNCNAME[0]}" } =20 skip_no_probe_record_support @@ -165,7 +172,11 @@ if [ $err -ne 0 ]; then cleanup exit $err fi -prepare_perf_data +prepare_perf_data || { + echo "Skipping tests, failed to prepare perf.data" + cleanup + exit 2 +} test_basic test_ns_rename test_ms_filtertasks_highlight --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9A37139282F for ; Sat, 25 Apr 2026 22:51:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157514; cv=none; b=ksSSDdW8ifGxpAbFXEAevk0OvQIjIRj5H3fOhyo3PUAcDAlC9kdeuWaaxBI5w/HB0t1hH6Fn38B6JtDzAjiyXj8QKPfLqUxxhaxKuvlERFVofF4PTTLlBawOzED4V2gbezvPmnoU/iRVNJ9dLCnMdyHhvkeyfoFrCP6VCgyh6eI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157514; c=relaxed/simple; bh=HIj/4KwQviJzAdb5/BAWEjqXZ2sYv3oOaIee/0kObPI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=e26CPqHPMDnpVXBKz7IQocXTuFI06s50A7neUT4vCJ0p5wBShYDxG4IxFPfc46+oMQ0UwaEyG5pHOMjUr3XbR51988Ui2RX/ofkDLRyfl6CLKLV+sXJZBHbS1mcefj1Fij/tK7c/kYfMmW5HRdpd6/sDoEJ7PZSdv83+QiThFBk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=hW5unVs6; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="hW5unVs6" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2de07c12745so25491139eec.1 for ; Sat, 25 Apr 2026 15:51:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157512; x=1777762312; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=22aR7BdMkfc4F5hxQy/Qmu6pnO70TcMwJUmCFfE13QY=; b=hW5unVs6NGeFlrO5z2mB2/6XGy1wUrvtarLjLRBHAwS/t2+UhV8WGyoaTEWgMk+kI0 rFROc0TYiB1QSlZjhA44Hbme67+NEV+qboaPGILBhmodopqXvTHPt9hn9E6FqiXhzAA8 tMuhu8ev2PsqBHAaZL9usuYGXCVFX0UiWC1c8NH2mLYtTSG2v6LhO8sRTeApGKpkbYf2 vJXJitGr1pypIj1pccJO6ociLtQrxCCuH6pnAaoiTG96Q/1TyYXH6/IaR6i/E2kLNPZ9 6UijUixx1kfv6z5AFAOi6CDWVnDTCpuhBohmI0lZOFOCV+5dFeuxL0qqw9epMzU7lUlQ lLPw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157512; x=1777762312; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=22aR7BdMkfc4F5hxQy/Qmu6pnO70TcMwJUmCFfE13QY=; b=Sv3TnKwbEQ1s8w8zYL+vPJcAsyqNHt6mjc9RRIbm5/M7KWwOWF+//CboapVWPwsWB0 MsTvMjO/PBnpgJSG7drZG/3S+uKwPNDkw7Zv+iKeILwENlKlVTlnaY1jbeWHEEKMxbzG tlNPA8wsGf1Y75osYz4cMMDDxTjPtvkS9Tl7cYpd91FraJ8GegE3++AQi/ouhgmXrbbB O2rPJcCNHO0iv5saUTzaVtnEBgX1xy27dIU0akvCxCp/ZWU9H+W8INLP5RsPI7shCKYk 2FUlfeuojXTXKnGTkag3+04cN+LSkuXgnUkO45nBkOdwORH/F4g7cjZd1G23VFD/AS84 xTnw== X-Forwarded-Encrypted: i=1; AFNElJ+pP30Np0zCVJaALP2kiUeqmHxt7pzQRpslBh/MdgAqQdJ0GGm6zgKD4h9osgjvrE2Ho//M451IvnGOXEo=@vger.kernel.org X-Gm-Message-State: AOJu0YwfPDniIQaW3aphVqsoF2vIyZ6QNe0CD6ff+LKGCMsCLUwI0BTz RBH0kIQzOS3Og4tcTRHFWqcjGHgdhjNFsb1XA48DsdZ78kTKklideVy+SI5wzO6zlu9Dg8AmIVE WB/vGFBbeMg== X-Received: from dyblf37.prod.google.com ([2002:a05:7301:a25:b0:2da:4e8f:1752]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:6da7:b0:2c7:8e1c:956a with SMTP id 5a478bee46e88-2e479d076b0mr21143652eec.19.1777157511536; Sat, 25 Apr 2026 15:51:51 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:39 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-48-irogers@google.com> Subject: [PATCH v7 47/59] perf failed-syscalls: Port failed-syscalls to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Port the legacy Perl script failed-syscalls.pl to a python script using the perf module in tools/perf/python. The new script uses a class-based architecture and leverages the perf.session API for event processing, making it a standalone script that reads perf.data files. It filters for sys_exit events, checks for failed syscalls (where return value ret < 0), and aggregates counts per command name. Complications: - The script is designed for file-based processing using perf.session. - pylint warns about the module name not being snake_case, but it is kept for consistency with the original script name. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- tools/perf/python/failed-syscalls.py | 78 ++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100755 tools/perf/python/failed-syscalls.py diff --git a/tools/perf/python/failed-syscalls.py b/tools/perf/python/faile= d-syscalls.py new file mode 100755 index 000000000000..c3b58664eb57 --- /dev/null +++ b/tools/perf/python/failed-syscalls.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +"""Failed system call counts.""" + +import argparse +from collections import defaultdict +import sys +from typing import Optional +import perf + +class FailedSyscalls: + """Tracks and displays failed system call totals.""" + def __init__(self, comm: Optional[str] =3D None) -> None: + self.failed_syscalls: dict[str, int] =3D defaultdict(int) + self.for_comm =3D comm + self.session: Optional[perf.session] =3D None + self.unhandled: dict[str, int] =3D defaultdict(int) + + def process_event(self, sample: perf.sample_event) -> None: + """Process sys_exit events.""" + event_name =3D str(sample.evsel) + if not event_name.startswith("evsel(syscalls:sys_exit_") and \ + not event_name.startswith("evsel(raw_syscalls:sys_exit_"): + return + + try: + ret =3D sample.ret + except AttributeError: + self.unhandled[event_name] +=3D 1 + return + + if ret >=3D 0: + return + + pid =3D sample.sample_pid + assert self.session is not None + try: + comm =3D self.session.find_thread(pid).comm() + except Exception: # pylint: disable=3Dbroad-except + comm =3D "unknown" + + if self.for_comm and comm !=3D self.for_comm: + return + + self.failed_syscalls[comm] +=3D 1 + + def print_totals(self) -> None: + """Print summary table.""" + print("\nfailed syscalls by comm:\n") + print(f"{'comm':<20s} {'# errors':>10s}") + print(f"{'-'*20} {'-'*10}") + + for comm, val in sorted(self.failed_syscalls.items(), + key=3Dlambda kv: (kv[1], kv[0]), reverse= =3DTrue): + print(f"{comm:<20s} {val:10d}") + + def run(self, input_file: str) -> None: + """Run the session.""" + self.session =3D perf.session(perf.data(input_file), sample=3Dself= .process_event) + self.session.process_events() + self.print_totals() + +def main() -> None: + """Main function.""" + parser =3D argparse.ArgumentParser(description=3D"Trace failed syscall= s") + parser.add_argument("comm", nargs=3D"?", help=3D"Filter by command nam= e") + parser.add_argument("-i", "--input", default=3D"perf.data", help=3D"In= put file") + args =3D parser.parse_args() + + analyzer =3D FailedSyscalls(args.comm) + try: + analyzer.run(args.input) + except IOError as e: + print(e, file=3Dsys.stderr) + sys.exit(1) + +if __name__ =3D=3D "__main__": + main() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 63A123AEF4E for ; Sat, 25 Apr 2026 22:51:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157515; cv=none; b=G5X5I/dAN843ShbOhRqjNTaxNtG9uR0245I2sIFn/wVYlZhcscTvJDnJKt0S0iP1s4Xqpwm1nc3bLd2RtRYc3B6ynR3ZZd+ro80t5MNbFRmz/e4e+OGbm8I3kfWubMGlJFQUoAvbCpK1MWBYJS2tkfk3ZZsbsbHLDSDU7nshbi8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157515; c=relaxed/simple; bh=j+1DPJ7hHqMkCRtiNL7ZTTuDpfoCTqlYLmFWD1lm5jM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=mFWqQWBKOJg4DFyr3IIn84P/o87t+NlbEa/m9/fESWXwEeg27JQhTi6sBaUW034dGviBaVkQFs+DfoFrZ83c9HG2kI61RRwHFpp3MPvSKKjWmqbNdx0lWvAjV/PePMB/ZCDCcZBquFNZpVqX5+tYAhJ0y+slamXKiFfTLNs1dnI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=lCDrn245; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="lCDrn245" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2ba9a744f7dso11750590eec.0 for ; Sat, 25 Apr 2026 15:51:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157514; x=1777762314; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Z8uDU0iX99Tgv3o+fYqZ9yp1w2za7K0HbBrM8LxB5IA=; b=lCDrn245Gj2kRjUOv+H7fTT4nNqmrNXzzLG0LDp1CHqMeZ84n+VyvvMKGxj1peSHmo JJCeTBH9O6njQUXHUYUK8/YlbS0qQmqcNRITAYJ30Y22SW4JBiWRmkjUIk2KM8plAgQz da3V2s3Kn9rayQOIKQo7nSi9CRccQ80LqIZvq2p66fx2jT1mTvkK5yILCEIxsoXNlLqb z6z8virhZ5rISpTxCqdMYSCi5vRgckm8Jv6iUhKkMSKQeaRjqJWX9gf031N+cYSuqOHN AQG8tSYCasSpYo7wjNZsakE8GDHQ+gbvez/pkgSZIC/9RAnKJk6+q0/jUQ9Un/2Udb+q 0ZmA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157514; x=1777762314; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Z8uDU0iX99Tgv3o+fYqZ9yp1w2za7K0HbBrM8LxB5IA=; b=AyPZlDJzSwD3feEYUxAAzIaLHxTu1rC1oLECtARfvHuzxlToPWzZMIrka9Fp7QTWNG xGJWVF+Ek3TSf9hnp2dNDN+JqVmVU8yPROqxCmU1qRAJTvTq/VWry9ksmNrpxAQ1bEVD CGLFyjuirHAbr77yLf80FzEjt9foQYBrqGCFDkDURJgwJHIZjkpm09mMJl8jDBjQ+kDM tsFJCgpzCU4DkpVmVVr0jqjJXbatosq5nnTLgNq4flGApZvMkfttH6wfBkgx0eyesidz 4lRmXh69SXvJIDfqj5sjzo+gH28Vle/CtI0TV8WrqrRwgrLrFc8/dS6j8cO+fzQqdXe0 s9ag== X-Forwarded-Encrypted: i=1; AFNElJ/ERha5yUy4Fmo4rzQZJIzr/6EgdR21bxBR9lsQdkbaqWuOEsqexKbxpCCFngMveE5bqVgVxWBG3033lJ8=@vger.kernel.org X-Gm-Message-State: AOJu0YwAVSiTyxjM2WDcxKYv4XNnzz8LGANT1vANtrt68rjUC4c0dIMm +FewqOLZKPe9CVwjRlNbP8zTAknodG9z6ZTjUUx0joFs+saI1TphVu2hwZI5hp+lPTB//ZpS0q/ 3Gd3ZK9Eu+w== X-Received: from dyjf14.prod.google.com ([2002:a05:7300:680e:b0:2d9:9fa3:5aa6]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:693c:2b08:b0:2c1:7afc:df06 with SMTP id 5a478bee46e88-2e464ea7057mr18321401eec.5.1777157513342; Sat, 25 Apr 2026 15:51:53 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:40 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-49-irogers@google.com> Subject: [PATCH v7 48/59] perf rw-by-file: Port rw-by-file to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Port the legacy Perl script rw-by-file.pl to a python script using the perf module in tools/perf/python. The new script uses a class-based architecture and leverages the perf.session API for event processing. It tracks read and write activity by file descriptor for a given program name, aggregating bytes requested/written and total counts. Complications: - Had to split long lines in __init__ to satisfy pylint. - pylint warns about the module name not being snake_case, but it is kept for consistency with the original script name. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: - Fixed Substring Matching: Replaced if "sys_enter_read" in event_name: with an exact match against syscalls:sys_enter_read and raw_syscalls:sys_enter_read using sample.evsel.name . This prevents variants like readv or readlink from incorrectly triggering the read logic. Similar fixes were applied for write events. - Fixed Silent Error Dropping: Instead of silently returning when expected fields are missing (causing AttributeError ), the script now increments the self.unhandled counter for that event. This ensures that missing data or unexpected event variants are reported to the user instead of quietly skewing the results. v6: - Fixed `AttributeError` by using `str(sample.evsel)` to get event name. --- tools/perf/python/rw-by-file.py | 103 ++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100755 tools/perf/python/rw-by-file.py diff --git a/tools/perf/python/rw-by-file.py b/tools/perf/python/rw-by-file= .py new file mode 100755 index 000000000000..2103ac0412bb --- /dev/null +++ b/tools/perf/python/rw-by-file.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +"""Display r/w activity for files read/written to for a given program.""" + +import argparse +from collections import defaultdict +import sys +from typing import Optional, Dict +import perf + +class RwByFile: + """Tracks and displays read/write activity by file descriptor.""" + def __init__(self, comm: str) -> None: + self.for_comm =3D comm + self.reads: Dict[int, Dict[str, int]] =3D defaultdict( + lambda: {"bytes_requested": 0, "total_reads": 0} + ) + self.writes: Dict[int, Dict[str, int]] =3D defaultdict( + lambda: {"bytes_written": 0, "total_writes": 0} + ) + self.unhandled: Dict[str, int] =3D defaultdict(int) + self.session: Optional[perf.session] =3D None + + def process_event(self, sample: perf.sample_event) -> None: + """Process events.""" + event_name =3D str(sample.evsel)[6:-1] + + pid =3D sample.sample_pid + assert self.session is not None + try: + comm =3D self.session.find_thread(pid).comm() + except Exception: # pylint: disable=3Dbroad-except + comm =3D "unknown" + + if comm !=3D self.for_comm: + return + + if event_name in ("syscalls:sys_enter_read", "raw_syscalls:sys_ent= er_read"): + try: + fd =3D sample.fd + count =3D sample.count + self.reads[fd]["bytes_requested"] +=3D count + self.reads[fd]["total_reads"] +=3D 1 + except AttributeError: + self.unhandled[event_name] +=3D 1 + elif event_name in ("syscalls:sys_enter_write", "raw_syscalls:sys_= enter_write"): + try: + fd =3D sample.fd + count =3D sample.count + self.writes[fd]["bytes_written"] +=3D count + self.writes[fd]["total_writes"] +=3D 1 + except AttributeError: + self.unhandled[event_name] +=3D 1 + else: + self.unhandled[event_name] +=3D 1 + + def print_totals(self) -> None: + """Print summary tables.""" + print(f"file read counts for {self.for_comm}:\n") + print(f"{'fd':>6s} {'# reads':>10s} {'bytes_requested':>15s}") + print(f"{'-'*6} {'-'*10} {'-'*15}") + + for fd, data in sorted(self.reads.items(), + key=3Dlambda kv: kv[1]["bytes_requested"], = reverse=3DTrue): + print(f"{fd:6d} {data['total_reads']:10d} {data['bytes_reque= sted']:15d}") + + print(f"\nfile write counts for {self.for_comm}:\n") + print(f"{'fd':>6s} {'# writes':>10s} {'bytes_written':>15s}") + print(f"{'-'*6} {'-'*10} {'-'*15}") + + for fd, data in sorted(self.writes.items(), + key=3Dlambda kv: kv[1]["bytes_written"], re= verse=3DTrue): + print(f"{fd:6d} {data['total_writes']:10d} {data['bytes_writ= ten']:15d}") + + if self.unhandled: + print("\nunhandled events:\n") + print(f"{'event':<40s} {'count':>10s}") + print(f"{'-'*40} {'-'*10}") + for event_name, count in self.unhandled.items(): + print(f"{event_name:<40s} {count:10d}") + + def run(self, input_file: str) -> None: + """Run the session.""" + self.session =3D perf.session(perf.data(input_file), sample=3Dself= .process_event) + self.session.process_events() + self.print_totals() + +def main() -> None: + """Main function.""" + parser =3D argparse.ArgumentParser(description=3D"Trace r/w activity b= y file") + parser.add_argument("comm", help=3D"Filter by command name") + parser.add_argument("-i", "--input", default=3D"perf.data", help=3D"In= put file") + args =3D parser.parse_args() + + analyzer =3D RwByFile(args.comm) + try: + analyzer.run(args.input) + except IOError as e: + print(e, file=3Dsys.stderr) + sys.exit(1) + +if __name__ =3D=3D "__main__": + main() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 968F23B0AD8 for ; Sat, 25 Apr 2026 22:51:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157518; cv=none; b=dnUO2U/nug0QUpxu5MzcxC+d28aG1GoYVOGlbniZivGFtnp9CyWLwVDjhBX2OVCLi6tnB2XkZhzASRyylTobu7OQOTiGLgwDZqVoGpYA9hucBYDRzBc9Ai+guLaLAyJhbfXIiM9RKx+SaEgjT6si07yfnZ7A147qXWXYhpaw/wY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157518; c=relaxed/simple; bh=bZNdf2xRLALfsVIlIhihnaH+uaJBxWazbnpkNK5OY24=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=uBOBs6ljAF1xC3d3I1cl48r009vuef+owpaIb0EmFMMF8tTlWJnyOy7o2IIbdKvZEXywL86G2d0C3HR6SBrva9I0GMYE3evbtw6SUkegtw0mebWACcNVulpuolLgpg02Zf2BfjnCdGEICba4oPTOLANLVinMXD6sUYj4s/4O0cQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=T0BOLsaf; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="T0BOLsaf" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2cc75e79b97so24550679eec.1 for ; Sat, 25 Apr 2026 15:51:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157516; x=1777762316; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=qbPEhnMlYjLIyQYaNXILQynFaRfheBD/Hl8QlXDxSqM=; b=T0BOLsaf/pXz5V055Ax6kJv7LJAAHjUTSvl8Glz8TNpRAcDbZU9kMaV04nx5kd1o2l W3UDc/dlSRdVwVtCSiXU2l1V8cMltWx2JzkuoRfEZ/US/F8FMSbt2XnaCL8aE/wbU/0/ psGChY1LzP949E5/r03qnnZIHBoYrvQpIbv5ySRfXqHosywE/acnRrki+iT3VaVxtz9R aDdbjKyA81iyQV3hzYJ03HcKz7j0h0LYRVO3tR+CGB9EkS1Gf11j2QoUpcNQ0fehDqKb JaDNvFrQCT1wFHYw21avDYzCSW/LSykZz0JybrdauLm/gRglkSZBNFUB51NACxLR7c2u gK/w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157516; x=1777762316; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=qbPEhnMlYjLIyQYaNXILQynFaRfheBD/Hl8QlXDxSqM=; b=s3EmHrOtzcXK8mRb8shXaPGIP6rrBk8ofESSxG6ou1Nz291DQ1HODZg7P+DItA9kHm wKj0Q9lHDNCwpGPOykEdstLjWk8yDF/ygsuOj1ipLJUDx/omXr6u1d/GkmvC+SR9FkPu 9/550OgF2e2hc6dELvXWLHfeqS3DF4WtnOp0ZoYRxFhpdS2fL9JBuKeW8usRRF+db9ln 9jrqI6KIZEoVVVG76CUKHEjQgOrN2ll3hKcEXI8d2HWzzqyo4Hbnj4HmI1uHX9OSvLrX YwtWOzSIgiLQegPXJBHU0G2p4ODyu9KUKY9Jk3cl86X9AGOgLhkCj3IpNEgsiir4S/BV UHHg== X-Forwarded-Encrypted: i=1; AFNElJ88l1ZlGzSZ0akBf3m4eAAoEdgiErTYXBVOISlOTIFVRK3j03vE+u6MH/T+HQpu8X8q6+htrCywSG/Kv9w=@vger.kernel.org X-Gm-Message-State: AOJu0YxZe23zKpuXCMgkBdtNoNyb5NIVotscdcjZJJUdyFQaXAiicKhU W7BZYLAn3mURePvsh4W3xryI39H83+RkhptMviyV4/acHYFr4uZNBrzHHwy4R3Ke6qtlhqdH+do oIIXqNM1tCw== X-Received: from dycol2.prod.google.com ([2002:a05:7301:db82:b0:2dd:8e19:2d13]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:693c:2b04:b0:2df:919f:ce59 with SMTP id 5a478bee46e88-2e47901614bmr20625177eec.19.1777157515734; Sat, 25 Apr 2026 15:51:55 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:41 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-50-irogers@google.com> Subject: [PATCH v7 49/59] perf rw-by-pid: Port rw-by-pid to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Port the legacy Perl script rw-by-pid.pl to a python script using the perf module in tools/perf/python. The new script uses a class-based architecture and leverages the perf.session API for event processing. It tracks read and write activity by PID for all processes, aggregating bytes requested, bytes read, total reads, and errors. Complications: - Refactored process_event to extract helper methods (_handle_sys_enter_read, etc.) to reduce the number of branches and satisfy pylint. - Split long lines to comply with line length limits. - pylint warns about the module name not being snake_case, but it is kept for consistency with the original script name. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: - Fixed Substring Matching: Replaced loose substring checks like if "sys_enter_read" in event_name: with exact matches against syscalls:sys_enter_read and raw_syscalls:sys_enter_read using sample.evsel.name . This prevents unrelated syscalls with similar names (like readahead ) from being incorrectly aggregated. Similar fixes were applied for exit events and write events. - Inlined Handlers and Tracked Errors: Inlined the _handle_sys_* helper methods into process_event() to make error handling easier. Now, if a sample lacks expected fields (raising AttributeError ), it is added to the self.unhandled tracker instead of being silently dropped, providing better visibility to the user. - Code Cleanup: Fixed trailing whitespace and added a pylint disable comment for too-many-branches caused by the inlining. v6: - Fixed `AttributeError` by using `str(sample.evsel)` to get event name. --- tools/perf/python/rw-by-pid.py | 158 +++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100755 tools/perf/python/rw-by-pid.py diff --git a/tools/perf/python/rw-by-pid.py b/tools/perf/python/rw-by-pid.py new file mode 100755 index 000000000000..b206d2a575cd --- /dev/null +++ b/tools/perf/python/rw-by-pid.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +"""Display r/w activity for all processes.""" + +import argparse +from collections import defaultdict +import sys +from typing import Optional, Dict, List, Tuple, Any +import perf + +class RwByPid: + """Tracks and displays read/write activity by PID.""" + def __init__(self) -> None: + self.reads: Dict[int, Dict[str, Any]] =3D defaultdict( + lambda: { + "bytes_requested": 0, + "bytes_read": 0, + "total_reads": 0, + "comm": "", + "errors": defaultdict(int), + } + ) + self.writes: Dict[int, Dict[str, Any]] =3D defaultdict( + lambda: { + "bytes_written": 0, + "total_writes": 0, + "comm": "", + "errors": defaultdict(int), + } + ) + self.unhandled: Dict[str, int] =3D defaultdict(int) + self.session: Optional[perf.session] =3D None + + def process_event(self, sample: perf.sample_event) -> None: # pylint:= disable=3Dtoo-many-branches + """Process events.""" + event_name =3D str(sample.evsel)[6:-1] + pid =3D sample.sample_pid + + assert self.session is not None + try: + comm =3D self.session.find_thread(pid).comm() + except Exception: # pylint: disable=3Dbroad-except + comm =3D "unknown" + + if event_name in ("syscalls:sys_enter_read", "raw_syscalls:sys_ent= er_read"): + try: + count =3D sample.count + self.reads[pid]["bytes_requested"] +=3D count + self.reads[pid]["total_reads"] +=3D 1 + self.reads[pid]["comm"] =3D comm + except AttributeError: + self.unhandled[event_name] +=3D 1 + elif event_name in ("syscalls:sys_exit_read", "raw_syscalls:sys_ex= it_read"): + try: + ret =3D sample.ret + if ret > 0: + self.reads[pid]["bytes_read"] +=3D ret + else: + self.reads[pid]["errors"][ret] +=3D 1 + except AttributeError: + self.unhandled[event_name] +=3D 1 + elif event_name in ("syscalls:sys_enter_write", "raw_syscalls:sys_= enter_write"): + try: + count =3D sample.count + self.writes[pid]["bytes_written"] +=3D count + self.writes[pid]["total_writes"] +=3D 1 + self.writes[pid]["comm"] =3D comm + except AttributeError: + self.unhandled[event_name] +=3D 1 + elif event_name in ("syscalls:sys_exit_write", "raw_syscalls:sys_e= xit_write"): + try: + ret =3D sample.ret + if ret <=3D 0: + self.writes[pid]["errors"][ret] +=3D 1 + except AttributeError: + self.unhandled[event_name] +=3D 1 + else: + self.unhandled[event_name] +=3D 1 + + def print_totals(self) -> None: + """Print summary tables.""" + print("read counts by pid:\n") + print( + f"{'pid':>6s} {'comm':<20s} {'# reads':>10s} " + f"{'bytes_requested':>15s} {'bytes_read':>10s}" + ) + print(f"{'-'*6} {'-'*20} {'-'*10} {'-'*15} {'-'*10}") + + for pid, data in sorted(self.reads.items(), + key=3Dlambda kv: kv[1]["bytes_read"], reve= rse=3DTrue): + print( + f"{pid:6d} {data['comm']:<20s} {data['total_reads']:10d}= " + f"{data['bytes_requested']:15d} {data['bytes_read']:10d}" + ) + + print("\nfailed reads by pid:\n") + print(f"{'pid':>6s} {'comm':<20s} {'error #':>6s} {'# errors':>= 10s}") + print(f"{'-'*6} {'-'*20} {'-'*6} {'-'*10}") + + errcounts: List[Tuple[int, str, int, int]] =3D [] + for pid, data in self.reads.items(): + for error, count in data["errors"].items(): + errcounts.append((pid, data["comm"], error, count)) + + for pid, comm, error, count in sorted(errcounts, key=3Dlambda x: x= [3], reverse=3DTrue): + print(f"{pid:6d} {comm:<20s} {error:6d} {count:10d}") + + print("\nwrite counts by pid:\n") + print(f"{'pid':>6s} {'comm':<20s} {'# writes':>10s} {'bytes_wri= tten':>15s}") + print(f"{'-'*6} {'-'*20} {'-'*10} {'-'*15}") + + for pid, data in sorted(self.writes.items(), + key=3Dlambda kv: kv[1]["bytes_written"], r= everse=3DTrue): + print( + f"{pid:6d} {data['comm']:<20s} " + f"{data['total_writes']:10d} {data['bytes_written']:15d}" + ) + + print("\nfailed writes by pid:\n") + print(f"{'pid':>6s} {'comm':<20s} {'error #':>6s} {'# errors':>= 10s}") + print(f"{'-'*6} {'-'*20} {'-'*6} {'-'*10}") + + errcounts =3D [] + for pid, data in self.writes.items(): + for error, count in data["errors"].items(): + errcounts.append((pid, data["comm"], error, count)) + + for pid, comm, error, count in sorted(errcounts, key=3Dlambda x: x= [3], reverse=3DTrue): + print(f"{pid:6d} {comm:<20s} {error:6d} {count:10d}") + + if self.unhandled: + print("\nunhandled events:\n") + print(f"{'event':<40s} {'count':>10s}") + print(f"{'-'*40} {'-'*10}") + for event_name, count in self.unhandled.items(): + print(f"{event_name:<40s} {count:10d}") + + def run(self, input_file: str) -> None: + """Run the session.""" + self.session =3D perf.session(perf.data(input_file), sample=3Dself= .process_event) + self.session.process_events() + self.print_totals() + +def main() -> None: + """Main function.""" + parser =3D argparse.ArgumentParser(description=3D"Trace r/w activity b= y PID") + parser.add_argument("-i", "--input", default=3D"perf.data", help=3D"In= put file") + args =3D parser.parse_args() + + analyzer =3D RwByPid() + try: + analyzer.run(args.input) + except IOError as e: + print(e, file=3Dsys.stderr) + sys.exit(1) + +if __name__ =3D=3D "__main__": + main() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A9A993B19D5 for ; Sat, 25 Apr 2026 22:51:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157520; cv=none; b=fJMfgN2J7xPa7F4KsnmZqZ3sHBNeBIsbW1AG/czZlzHi80hFndSIVDG+HUjelcYcSQaWilSLA4OHEAvr0J8BwUEdlljqtCvUpfRwAPPUSUITqypFmaHozxYTP6I/pNvxTegDIGPwl6jY5I/YfZgH1wq09RDs3Y3QQ/0ut+RvVKw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157520; c=relaxed/simple; bh=OIxfivDpeuuN830T2/W03ihn1okrUsuSb2nWiRZZk44=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ZEiJan02Qymqqd8686LuOMGPp+GAJeRdtE+ZGwjvAb+6CuPZo+nOq9xfr6zTS8AZR4GvfuvJBh+jAV8Dxd8HBfj7UXWPKfhfe2J1J57mMl9HCOaSSBtxOos6R/zwVxeTvpXZnxtZeXvGE4U0CaE1HfOXONg+c5oQTC4LrC8rw6I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=HhZ48QOe; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="HhZ48QOe" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2de07c12745so25491407eec.1 for ; Sat, 25 Apr 2026 15:51:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157518; x=1777762318; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=zeOgWX73kSMhcsGvtzXC4JAdu5ODROL7Kq4CP+Wwcu4=; b=HhZ48QOeV/PicMnY07524/6eaHELeFM9ajRO4AvUhH78MiVJ9Vz3R8o16qxqC+N/fP VB1k7EajYWaGMPP60eG9jIXFAc8q9G1fBTI6WtK7L6POXzz8RTfJtDfrS79HC8OS1roS qj53g6IA4Mw6MSNDheMNGsFI9npTZFNRySKxub99z8NGmgJmtkMJBqPwYtQP0PYkTUe0 JA8W5mlC9P46ZWodNkPjynEAcw4hND3+/hZ1co3qOKRsMn7Zi3lFQxrXIrdjTvoOWrbH ZWyCezN2kHErDzV7WZegqhj3k2PjlPnFu9PMTjacCO55NUAepwW4aXsJz55onN4VhORQ MLUg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157518; x=1777762318; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=zeOgWX73kSMhcsGvtzXC4JAdu5ODROL7Kq4CP+Wwcu4=; b=scQySloI7BBcrlZMMGm85Yfh4xJ5BSDwH9MLx3tWCV9viLjyvd/qABpAj+QrI9s1QE zoDgNbaS2i+o8YVYCbYWpEDyfH629mHk3UNtLy1U07xtROCTGF7ubV3jQHZKBdoqzED4 nz4mv7iJyGnorU3i9/kqY8go8Yn/zrMrn/bxribYx/qPSHKtGXBS0kTNQJ3KNbgu39W3 M5q9srp7dhGzk8DS69gyAiEf+UYexNT62b3kwHDnG3k44PIpihhRuVEjlDBQjilvGPVe ouYhmVB1xy7Qrq6hVHK1OBOwI7BDtmYa7ptxckaJD00ooTl6XKiNf11hfi7oW+wDDL5e 5RMw== X-Forwarded-Encrypted: i=1; AFNElJ91FoZXzQqrtbCFsxgeOApusnGl7XwSbL6HoWF04kCIKHFbVlgRstxQWeFzklEKcs1YLwyp7+TQMalTqp0=@vger.kernel.org X-Gm-Message-State: AOJu0YxF6Ilge2mQYFQRgMLcmHJuoOX7ETGdmO+Ld3oqU1i4oRE7yTJU liUpIHsgSi4JtXnE4RZpseGjEvpwpzs/Zr7vdwk9dQU4WIoSBhkihz9cBhlv9Y8+rk4yE8+tVp9 LweIfBwogUg== X-Received: from dybnj5.prod.google.com ([2002:a05:7300:d085:b0:2dd:4573:2897]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:ff45:b0:2ea:5057:a319 with SMTP id 5a478bee46e88-2ea5057b78amr7831947eec.6.1777157517737; Sat, 25 Apr 2026 15:51:57 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:42 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-51-irogers@google.com> Subject: [PATCH v7 50/59] perf rwtop: Port rwtop to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Port the legacy Perl script rwtop.pl to a python script using the perf module in tools/perf/python. The new script uses a class-based architecture and leverages the perf.session API for event processing. It periodically displays system-wide r/w call activity, broken down by PID, refreshed every interval. Complications: - Implemented periodic display based on event timestamps (sample.sample_time) instead of relying on SIGALRM, making it robust for file-based processing. - Used ANSI escape codes (\x1b[H\x1b[2J) to clear the terminal. - Fixed unused imports and indentation issues identified by pylint. - pylint warns about the module name not being snake_case, but it is kept for consistency with the original script name. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: - Added Live Session Support: Updated main() to start a LiveSession when the input file does not exist (or is the default "perf.data" and doesn't exist). It traces read and write entry/exit tracepoints. - Fixed Live Mode Comm Resolution: Fixed a bug in process_event() where it would attempt to use self.session to resolve the command name when running in live mode (where self.session is None ). It now falls back to f"PID({pid})" when in live mode or if resolution fails. - Fixed Substring Matching: Replaced loose substring checks like if "sys_enter_read" in event_name: with exact matches against "evsel(syscalls:sys_enter_read)" and "evsel(raw_syscalls:sys_enter_read)" using str(sample.evsel) . This prevents unrelated syscalls with similar names (like readv or readahead ) from being incorrectly aggregated. Similar fixes were applied for exit events and write events. - Inlined Handlers and Tracked Errors: Inlined the _handle_sys_* helper methods into process_event() . Now, if a sample lacks expected fields, it is added to the self.unhandled tracker instead of being silently ignored. - Fixed Write Byte Counting: Updated the write exit handler to use sample.ret to count actual bytes written on success, and tracked requested bytes separately in the enter handler, matching the read behavior. - Added Error Tables to Output: Added tables to display failed reads and writes by PID in print_totals() , which were previously tracked but never displayed. - Fixed Offline Output (Ghosting): Removed the hardcoded ANSI clear-screen escape codes in print_totals() , as they corrupted output when processing offline trace files at CPU speed or when piping the output. - Code Cleanup: Fixed a bug where fd was printed instead of pid in the read counts table, and broke long lines to satisfy pylint. --- tools/perf/python/rwtop.py | 219 +++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100755 tools/perf/python/rwtop.py diff --git a/tools/perf/python/rwtop.py b/tools/perf/python/rwtop.py new file mode 100755 index 000000000000..895ebab9af10 --- /dev/null +++ b/tools/perf/python/rwtop.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +"""Periodically displays system-wide r/w call activity, broken down by pid= .""" + +import argparse +from collections import defaultdict +import os +import sys +from typing import Optional, Dict, Any +import perf +from perf_live import LiveSession + +class RwTop: + """Periodically displays system-wide r/w call activity.""" + def __init__(self, interval: int =3D 3, nlines: int =3D 20) -> None: + self.interval_ns =3D interval * 1000000000 + self.nlines =3D nlines + self.reads: Dict[int, Dict[str, Any]] =3D defaultdict( + lambda: { + "bytes_requested": 0, + "bytes_read": 0, + "total_reads": 0, + "comm": "", + "errors": defaultdict(int), + } + ) + self.writes: Dict[int, Dict[str, Any]] =3D defaultdict( + lambda: { + "bytes_requested": 0, + "bytes_written": 0, + "total_writes": 0, + "comm": "", + "errors": defaultdict(int), + } + ) + self.unhandled: Dict[str, int] =3D defaultdict(int) + self.session: Optional[perf.session] =3D None + self.last_print_time: int =3D 0 + + def process_event(self, sample: perf.sample_event) -> None: # pylint:= disable=3Dtoo-many-branches + """Process events.""" + event_name =3D str(sample.evsel) + pid =3D sample.sample_pid + sample_time =3D sample.sample_time + + if self.last_print_time =3D=3D 0: + self.last_print_time =3D sample_time + + # Check if interval has passed + if sample_time - self.last_print_time >=3D self.interval_ns: + self.print_totals() + self.last_print_time =3D sample_time + + try: + comm =3D f"PID({pid})" if not self.session else self.session.f= ind_thread(pid).comm() + except Exception: # pylint: disable=3Dbroad-except + comm =3D f"PID({pid})" + + if event_name in ("evsel(syscalls:sys_enter_read)", "evsel(raw_sys= calls:sys_enter_read)"): + try: + count =3D sample.count + self.reads[pid]["bytes_requested"] +=3D count + self.reads[pid]["total_reads"] +=3D 1 + self.reads[pid]["comm"] =3D comm + except AttributeError: + self.unhandled[event_name] +=3D 1 + elif event_name in ("evsel(syscalls:sys_exit_read)", "evsel(raw_sy= scalls:sys_exit_read)"): + try: + ret =3D sample.ret + if ret > 0: + self.reads[pid]["bytes_read"] +=3D ret + else: + self.reads[pid]["errors"][ret] +=3D 1 + except AttributeError: + self.unhandled[event_name] +=3D 1 + elif event_name in ("evsel(syscalls:sys_enter_write)", + "evsel(raw_syscalls:sys_enter_write)"): + try: + count =3D sample.count + self.writes[pid]["bytes_requested"] +=3D count + self.writes[pid]["total_writes"] +=3D 1 + self.writes[pid]["comm"] =3D comm + except AttributeError: + self.unhandled[event_name] +=3D 1 + elif event_name in ("evsel(syscalls:sys_exit_write)", "evsel(raw_s= yscalls:sys_exit_write)"): + try: + ret =3D sample.ret + if ret > 0: + self.writes[pid]["bytes_written"] +=3D ret + else: + self.writes[pid]["errors"][ret] +=3D 1 + except AttributeError: + self.unhandled[event_name] +=3D 1 + else: + self.unhandled[event_name] +=3D 1 + + def print_totals(self) -> None: + """Print summary tables.""" + print("read counts by pid:\n") + print( + f"{'pid':>6s} {'comm':<20s} {'# reads':>10s} " + f"{'bytes_req':>10s} {'bytes_read':>10s}" + ) + print(f"{'-'*6} {'-'*20} {'-'*10} {'-'*10} {'-'*10}") + + count =3D 0 + for pid, data in sorted(self.reads.items(), + key=3Dlambda kv: kv[1]["bytes_read"], reve= rse=3DTrue): + print( + f"{pid:6d} {data['comm']:<20s} {data['total_reads']:10d}= " + f"{data['bytes_requested']:10d} {data['bytes_read']:10d}" + ) + count +=3D 1 + if count >=3D self.nlines: + break + + print("\nfailed reads by pid:\n") + print(f"{'pid':>6s} {'comm':<20s} {'error #':>6s} {'# errors':>= 10s}") + print(f"{'-'*6} {'-'*20} {'-'*6} {'-'*10}") + + errcounts =3D [] + for pid, data in self.reads.items(): + for error, cnt in data["errors"].items(): + errcounts.append((pid, data["comm"], error, cnt)) + + sorted_errcounts =3D sorted(errcounts, key=3Dlambda x: x[3], rever= se=3DTrue) + for pid, comm, error, cnt in sorted_errcounts[:self.nlines]: + print(f"{pid:6d} {comm:<20s} {error:6d} {cnt:10d}") + + print("\nwrite counts by pid:\n") + print( + f"{'pid':>6s} {'comm':<20s} {'# writes':>10s} " + f"{'bytes_req':>10s} {'bytes_written':>13s}" + ) + print(f"{'-'*6} {'-'*20} {'-'*10} {'-'*10} {'-'*13}") + + count =3D 0 + for pid, data in sorted(self.writes.items(), + key=3Dlambda kv: kv[1]["bytes_written"], r= everse=3DTrue): + print( + f"{pid:6d} {data['comm']:<20s} {data['total_writes']:10d= } " + f"{data['bytes_requested']:10d} {data['bytes_written']:13= d}" + ) + count +=3D 1 + if count >=3D self.nlines: + break + + print("\nfailed writes by pid:\n") + print(f"{'pid':>6s} {'comm':<20s} {'error #':>6s} {'# errors':>= 10s}") + print(f"{'-'*6} {'-'*20} {'-'*6} {'-'*10}") + + errcounts =3D [] + for pid, data in self.writes.items(): + for error, cnt in data["errors"].items(): + errcounts.append((pid, data["comm"], error, cnt)) + + sorted_errcounts =3D sorted(errcounts, key=3Dlambda x: x[3], rever= se=3DTrue) + for pid, comm, error, cnt in sorted_errcounts[:self.nlines]: + print(f"{pid:6d} {comm:<20s} {error:6d} {cnt:10d}") + + # Reset counts + self.reads.clear() + self.writes.clear() + + def run(self, input_file: str) -> None: + """Run the session.""" + self.session =3D perf.session(perf.data(input_file), sample=3Dself= .process_event) + self.session.process_events() + + # Print final totals if there are any left + if self.reads or self.writes: + self.print_totals() + + if self.unhandled: + print("\nunhandled events:\n") + print(f"{'event':<40s} {'count':>10s}") + print(f"{'-'*40} {'-'*10}") + for event_name, count in self.unhandled.items(): + print(f"{event_name:<40s} {count:10d}") + +def main() -> None: + """Main function.""" + parser =3D argparse.ArgumentParser(description=3D"Trace r/w activity b= y PID") + parser.add_argument( + "interval", type=3Dint, nargs=3D"?", default=3D3, help=3D"Refresh = interval in seconds" + ) + parser.add_argument("-i", "--input", default=3D"perf.data", help=3D"In= put file") + args =3D parser.parse_args() + + analyzer =3D RwTop(args.interval) + try: + if not os.path.exists(args.input) and args.input =3D=3D "perf.data= ": + # Live mode + events =3D ( + "syscalls:sys_enter_read,syscalls:sys_exit_read," + "syscalls:sys_enter_write,syscalls:sys_exit_write" + ) + try: + live_session =3D LiveSession(events, sample_callback=3Dana= lyzer.process_event) + except OSError: + events =3D ( + "raw_syscalls:sys_enter_read,raw_syscalls:sys_exit_rea= d," + "raw_syscalls:sys_enter_write,raw_syscalls:sys_exit_wr= ite" + ) + live_session =3D LiveSession(events, sample_callback=3Dana= lyzer.process_event) + print("Live mode started. Press Ctrl+C to stop.", file=3Dsys.s= tderr) + live_session.run() + else: + analyzer.run(args.input) + except IOError as e: + print(e, file=3Dsys.stderr) + sys.exit(1) + except KeyboardInterrupt: + print("\nStopping live mode...", file=3Dsys.stderr) + if analyzer.reads or analyzer.writes: + analyzer.print_totals() + +if __name__ =3D=3D "__main__": + main() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C8F683B27F3 for ; Sat, 25 Apr 2026 22:52:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157522; cv=none; b=D0dKCOfY8GWSYdFFaNltYs2elhVH9CQ6HgfxQzROvUF7tjJLHuCmmVAGgcxX3/uklPTb7QM5q8gooebNRxwW7P62foF012F9ObW14bohNZUf0niKX8XjCxTIvN3fhTo3/vo3LsQLYhK6qLNiZZ72RVyPvpaXC6NReEvmv9FPj8o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157522; c=relaxed/simple; bh=jeR4Y5tYrqM+VRciyZ38jmj8C+EvBF6bkN0wvPo/M1Y=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=hpnzK/zgu/A6Zv7DPwwCAtkw5httzIJFFU5LtEVvMXWjmiDsGa8iqaaDPYqmpvhw1G8NpF2Dx/ZG/Uzp8m5Jkn8SsytDh+aS3qT9pqKLR6bMPoN6ujng2tPT8EGKrB5DIjouUu5FzJUmR13+6kQoMAvNA3Q1awRXa61hEgDyBJ0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=WAuqNxSG; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="WAuqNxSG" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2ddd8ef5343so8847287eec.1 for ; Sat, 25 Apr 2026 15:52:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157520; x=1777762320; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=4vO51mkUv8czx4QMNQNbvlHwQFmUDbpKAVfeUaPjKEk=; b=WAuqNxSGVtbUCjNTKJOJdafRLxr4736F0MwgyrhkkZclXFKvxVn3msLMMKqeTNhJ+V ZAgVl8637QU0lnm9zO0TxVQZecAuw/eqqUqvfqiSr9aHaJ5brZoMo+DMRpZ91Q6o3NcF BPEwjdpQtz7XYqV72Fl26ogcnl6Lnl4TYYk/smyYMIdUXl4Z213Xg+oRvSYne6WA4Rby D5mfKDeSDrGTQTMaiu3i6UKeb/ECWk5QXK3uY8OUqPNtltV9kTLhSsw9wR7wdne0asjf 8BiUTkB3rZePspnQCeJFiDnVObAGWw7O2b5aD81tacLCN8tl9oSKnhYYclySknjtW6K6 RJRw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157520; x=1777762320; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=4vO51mkUv8czx4QMNQNbvlHwQFmUDbpKAVfeUaPjKEk=; b=Hn5xnB1aprowmDxKewd4XEry8ECE4mbgfc759AYJ5P1Z+o65rvcEJG4BReLJ2nxyyZ vYkWT5z/+IOUNiOZJ1628pKR2/uRUJMhDhTH8wr6qJWu0MehuzCsSNqDKzchnwv7rGc4 GEKExZ9TEtcbbfA0G0pnxznh8sVU1JtOMh4Hvt7EOBScWk1+xF1qZyoMs/IEjCdkir5l KzGdcYTsOQPGgaZjoq8QbNxpiY1AnYkdw5uRvPScatv/AWBIV3GgaBAyGjzYv8pswEB+ XhoXbS/DrQfUNqcpplbelg/eQzBjdk2rkYMsF5iIbd8F/6N2X7ucGNC1/Pn9K2JqDhZH d2FQ== X-Forwarded-Encrypted: i=1; AFNElJ/NkCt6iOmAFdXQufaxjhGgjmxfgBY4mcDWDjAEQb8w+dzRCdQjIJNesaJnK95p4Gwu3sB/eG/oD5ItNW0=@vger.kernel.org X-Gm-Message-State: AOJu0YwTJyAuLntVKzPaCmyMe9AlOXKJriY42XlAfdUWkojH06bFe5N4 OOGs+ly+SXwIOilnjx+e2BeEl19E69BzzVnfWisvrCuguVM0BThHfIvqXAlFrHC1KyI7Skg1oAl TZWxY6NzCCg== X-Received: from dly15-n1.prod.google.com ([2002:a05:701b:204f:10b0:12a:83c5:a16f]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:90a:b0:127:33e0:ea40 with SMTP id a92af1059eb24-12c73f7fbaamr18492849c88.15.1777157519599; Sat, 25 Apr 2026 15:51:59 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:43 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-52-irogers@google.com> Subject: [PATCH v7 51/59] perf wakeup-latency: Port wakeup-latency to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Port the legacy Perl script wakeup-latency.pl to a python script using the perf module in tools/perf/python. The new script uses a class-based architecture and leverages the perf.session API for event processing. It measures wakeup latency by tracking timestamps of sched:sched_wakeup and sched:sched_switch events. Complications: - Used min() and max() built-in functions instead of if blocks to satisfy pylint recommendations. - pylint warns about the module name not being snake_case, but it is kept for consistency with the original script name. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: - Fixed Wakeup Latency Logic: Modified the script to track wakeup timestamps per task (using sample.pid as the key) instead of per CPU. This ensures that context switches are correctly paired with the specific task that was woken up, even if multiple tasks are woken up on the same CPU or if a task is migrated to a different CPU before running. - Prevented Memory Growth: Added del self.last_wakeup[next_pid] after successful latency calculation to prevent the dictionary from growing unbounded over time. - Added Error Tracking: Added try-except blocks around tracepoint field access in process_event() and tracked missing fields in self. unhandled instead of ignoring them. --- tools/perf/python/wakeup-latency.py | 88 +++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100755 tools/perf/python/wakeup-latency.py diff --git a/tools/perf/python/wakeup-latency.py b/tools/perf/python/wakeup= -latency.py new file mode 100755 index 000000000000..1b0db115abcf --- /dev/null +++ b/tools/perf/python/wakeup-latency.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +"""Display avg/min/max wakeup latency.""" + +import argparse +from collections import defaultdict +import sys +from typing import Optional, Dict +import perf + +class WakeupLatency: + """Tracks and displays wakeup latency statistics.""" + def __init__(self) -> None: + self.last_wakeup: Dict[int, int] =3D defaultdict(int) + self.max_wakeup_latency =3D 0 + self.min_wakeup_latency =3D 1000000000 + self.total_wakeup_latency =3D 0 + self.total_wakeups =3D 0 + self.unhandled: Dict[str, int] =3D defaultdict(int) + self.session: Optional[perf.session] =3D None + + def process_event(self, sample: perf.sample_event) -> None: + """Process events.""" + event_name =3D str(sample.evsel) + sample_time =3D sample.sample_time + + if "sched:sched_wakeup" in event_name: + try: + pid =3D sample.pid + self.last_wakeup[pid] =3D sample_time + except AttributeError: + self.unhandled[event_name] +=3D 1 + elif "sched:sched_switch" in event_name: + try: + next_pid =3D sample.next_pid + wakeup_ts =3D self.last_wakeup.get(next_pid, 0) + if wakeup_ts: + latency =3D sample_time - wakeup_ts + self.max_wakeup_latency =3D max(self.max_wakeup_latenc= y, latency) + self.min_wakeup_latency =3D min(self.min_wakeup_latenc= y, latency) + self.total_wakeup_latency +=3D latency + self.total_wakeups +=3D 1 + del self.last_wakeup[next_pid] + except AttributeError: + self.unhandled[event_name] +=3D 1 + else: + self.unhandled[event_name] +=3D 1 + + def print_totals(self) -> None: + """Print summary statistics.""" + print("wakeup_latency stats:\n") + print(f"total_wakeups: {self.total_wakeups}") + if self.total_wakeups: + avg =3D self.total_wakeup_latency // self.total_wakeups + print(f"avg_wakeup_latency (ns): {avg}") + else: + print("avg_wakeup_latency (ns): N/A") + print(f"min_wakeup_latency (ns): {self.min_wakeup_latency}") + print(f"max_wakeup_latency (ns): {self.max_wakeup_latency}") + + if self.unhandled: + print("\nunhandled events:\n") + print(f"{'event':<40s} {'count':>10s}") + print(f"{'-'*40} {'-'*10}") + for event_name, count in self.unhandled.items(): + print(f"{event_name:<40s} {count:10d}") + + def run(self, input_file: str) -> None: + """Run the session.""" + self.session =3D perf.session(perf.data(input_file), sample=3Dself= .process_event) + self.session.process_events() + self.print_totals() + +def main() -> None: + """Main function.""" + parser =3D argparse.ArgumentParser(description=3D"Trace wakeup latency= ") + parser.add_argument("-i", "--input", default=3D"perf.data", help=3D"In= put file") + args =3D parser.parse_args() + + analyzer =3D WakeupLatency() + try: + analyzer.run(args.input) + except IOError as e: + print(e, file=3Dsys.stderr) + sys.exit(1) + +if __name__ =3D=3D "__main__": + main() --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E4B0A3B3880 for ; Sat, 25 Apr 2026 22:52:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157525; cv=none; b=f+b8qN3kkUfREfOggPO6Yr8yJlMi7OmsBkdqIJVVGYHfl52ygwoaoMgRYoCDQPHSNyXU3yOC94TdJC/gxY1NAPLDyaVxskwsUv4tHr7fGVZ9z+SYr7hE7Ue+Aw2ghxDtFn3tPltDSxppSRmIQzs72WlyNz0WRpHe4x/U7Tyx34k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157525; c=relaxed/simple; bh=ZrJ2nullNFdPahYP4cmzB/NIEZ9UukgcFFOHNvNmBo4=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=RTYzebqsWHH+Egeoezwsrrgsu4OcJzXrIR95wWQp/ZHYHNrck5sISJP760sQR8uhtfCh4X/rLUbOUhYrlTUgSiBRChfrX0uxO7+DBhm20AwdFz0OFaxhEbFfgGoydb2osC2Uiy+7cgCXxu2Fv5jRPfTWh/53c5gwCbYF1s1J2SM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Xjy9CaQm; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Xjy9CaQm" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2c0f6593ef5so11440203eec.1 for ; Sat, 25 Apr 2026 15:52:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157522; x=1777762322; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=a4cIRIqFZJ5y1BubPplg9oWe019B443zoI45B5rxaHA=; b=Xjy9CaQmPZCrYl18kBAde+IGDu+qilwUaQNP8mSuYzN7ZdB7r5miC91jS0avQIBIqS kliZMjsQ6YOAB5F7fVGiLCAJ6M3xyKIO3hpuYVZNMTA305uNgb7OtCoUbQ0Dl1mZ8BbN LIRUD4bRoUh62POlruYFyG4FhwYwIBII4NjfuH4eHjTQU+dm9fbvQJ3TIyy+ChlVlIV6 mmEakqyjMr6EpxZPucQudsNETudK/9p7FsGJJ4p58IzlmEaKLHW6HtSnPsTazUsBeZR9 pkfY3bsCToz/wilVainhWyIumi1FB+fn+e2xaH+dHOQgg61D3WP+2iTNtOO2RR9J8m2g bumA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157522; x=1777762322; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=a4cIRIqFZJ5y1BubPplg9oWe019B443zoI45B5rxaHA=; b=jIBoSvLESD34eayQd4bU8VCFlkn/KtNsv7Lfaho4cJkTOevbpZKTI4EPYVEUodSE6i HRaT6IYPuBF1hdFnEzjbiy+YFb/qL9NFNDkeg4kPYZsi1t9jY7Nh/cn6yQToJgb4BXaz ppm/VGLr6sRPvQ2Qzc67z0qxMDsWJZYl6xquKnxQaq8WyHQjqf4cOGNvvZhYyOBCzZrG JCepeMMxq4qZgTRebme9EMw9mAsVN0/+o3y8ImBxhv+Z1vRL8l70a+WJVJUtsuH64Coo dpv4zRTdK4w0kBoNq1hGDUSN8R/382fCeXF7PhNc22YHENrvWert77X2pSH/nAFZpZkz eE+w== X-Forwarded-Encrypted: i=1; AFNElJ91P4IEuDxZ21jA+5n8fL5+aThWCAHyilkaoh/PtF1km9iAoR5ko5iEcVQs/sQJ9qdogoROjP1FweLj+qM=@vger.kernel.org X-Gm-Message-State: AOJu0Yx/uCvfp4V+CeDpCVawPtXZS9poBwRUGGeGe/yJC74ptGvEKwn/ DrIpGWw1FEskCvyQfzNa4ZygjR4AUStj486/hvsn9KJJEftCy5SOdCTKo6njMNWX1Xl0cQ+vQF7 X8OBR3AirAA== X-Received: from dyaw25-n1.prod.google.com ([2002:a05:693c:4159:10b0:2df:5e19:9e69]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7301:5784:b0:2dd:c066:c02 with SMTP id 5a478bee46e88-2e47a10653dmr20021430eec.22.1777157521693; Sat, 25 Apr 2026 15:52:01 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:44 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-53-irogers@google.com> Subject: [PATCH v7 52/59] perf test: Migrate Intel PT virtual LBR test to use Python API From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The Intel PT virtual LBR test used an ad-hoc Python script written on the fly to parse branch stacks. This change migrates it to use the newly added `brstack` iterator API in the `perf` Python module. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v7: - Fixed permanent iterator exhaustion on brstack by converting it to a list. --- .../perf/tests/shell/lib/perf_brstack_max.py | 43 +++++++++++++++++++ tools/perf/tests/shell/test_intel_pt.sh | 35 +++++---------- 2 files changed, 53 insertions(+), 25 deletions(-) create mode 100644 tools/perf/tests/shell/lib/perf_brstack_max.py diff --git a/tools/perf/tests/shell/lib/perf_brstack_max.py b/tools/perf/te= sts/shell/lib/perf_brstack_max.py new file mode 100644 index 000000000000..bd1cdb463f28 --- /dev/null +++ b/tools/perf/tests/shell/lib/perf_brstack_max.py @@ -0,0 +1,43 @@ +#!/usr/bin/python +# SPDX-License-Identifier: GPL-2.0 +# Determine the maximum size of branch stacks in a perf.data file. + +import argparse +import sys + +import os + +script_dir =3D os.path.dirname(os.path.abspath(__file__)) +python_dir =3D os.path.abspath(os.path.join(script_dir, "../../../python")) +sys.path.insert(0, python_dir) + +import perf + +def main(): + ap =3D argparse.ArgumentParser() + ap.add_argument("-i", "--input", default=3D"perf.data", help=3D"Input = file name") + args =3D ap.parse_args() + + bmax =3D 0 + + def process_event(sample): + nonlocal bmax + try: + brstack =3D sample.brstack + if brstack: + n =3D len(list(brstack)) + if n > bmax: + bmax =3D n + except AttributeError: + pass + + try: + session =3D perf.session(perf.data(args.input), sample=3Dprocess_e= vent) + session.process_events() + print("max brstack", bmax) + except Exception as e: + print(f"Error processing events: {e}", file=3Dsys.stderr) + sys.exit(1) + +if __name__ =3D=3D "__main__": + main() diff --git a/tools/perf/tests/shell/test_intel_pt.sh b/tools/perf/tests/she= ll/test_intel_pt.sh index 8ee761f03c38..d711ecdf5be0 100755 --- a/tools/perf/tests/shell/test_intel_pt.sh +++ b/tools/perf/tests/shell/test_intel_pt.sh @@ -24,7 +24,6 @@ errfile=3D"${temp_dir}/test-err.txt" workload=3D"${temp_dir}/workload" awkscript=3D"${temp_dir}/awkscript" jitdump_workload=3D"${temp_dir}/jitdump_workload" -maxbrstack=3D"${temp_dir}/maxbrstack.py" =20 cleanup() { @@ -539,34 +538,20 @@ test_kernel_trace() test_virtual_lbr() { echo "--- Test virtual LBR ---" - # Check if python script is supported - libpython=3D$(perf version --build-options | grep python | grep -cv OFF) - if [ "${libpython}" !=3D "1" ] ; then - echo "SKIP: python scripting is not supported" + # shellcheck source=3Dlib/setup_python.sh + . "$(dirname "$0")"/lib/setup_python.sh + + if [ -z "$PYTHON" ] ; then + echo "SKIP: Python not found" return 2 fi =20 - # Python script to determine the maximum size of branch stacks - cat << "_end_of_file_" > "${maxbrstack}" -from __future__ import print_function - -bmax =3D 0 - -def process_event(param_dict): - if "brstack" in param_dict: - brstack =3D param_dict["brstack"] - n =3D len(brstack) - global bmax - if n > bmax: - bmax =3D n - -def trace_end(): - print("max brstack", bmax) -_end_of_file_ - # Check if virtual lbr is working - perf_record_no_bpf -o "${perfdatafile}" --aux-sample -e '{intel_pt//,cycl= es}:u' uname - times_val=3D$(perf script -i "${perfdatafile}" --itrace=3DL -s "${maxbrst= ack}" 2>/dev/null | grep "max brstack " | cut -d " " -f 3) + perf_record_no_bpf -o "${tmpfile}" --aux-sample -e '{intel_pt//,cycles}:u= ' perf test -w brstack + perf inject --itrace=3DL -i "${tmpfile}" -o "${perfdatafile}" + output=3D$($PYTHON "$(dirname "$0")"/lib/perf_brstack_max.py -i "${perfda= tafile}") + echo "Debug: perf_brstack_max.py output: $output" + times_val=3D$(echo "$output" | grep "max brstack " | cut -d " " -f 3) case "${times_val}" in [0-9]*) ;; *) times_val=3D0;; --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 947193B2FE9 for ; Sat, 25 Apr 2026 22:52:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157529; cv=none; b=Utlola96fDDkdxfaAmvY/qOKnYf1TWheapXYijWTZ6eVYT8wL5BuEEp1OzLltc9TbDFvGF+WIiEZKnhSs1V88WKuPUtSIPFY2Dxt9QMIJQZarWUb44dgj/RtCQaN52Fxod2C9aAl87NpZ+74O2C2k9nOfhQhcb3bFo13A2b3srA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157529; c=relaxed/simple; bh=mvWMtX7yyCUOoitP1V5P+IQ+2yO0NkSnPyQJQEVlx7I=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=bax3HQAMuroXTTsFt5zHT3jNL3qrCZLGT0FBg+6+kbUIeXWi8/JHcEDB7cY25FVvMGZkbBnu81+YYeMRhx9Bdz9ADM36/xjyfkS6zzqE4aIggwX7YfSuZ0Oi5TupN8oUnVvVD56Ix1VCo+m8aNeymjicQ/kz8gmfmfe7ELW3nks= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=FOaf1Kzf; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="FOaf1Kzf" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2de07c12745so25491710eec.1 for ; Sat, 25 Apr 2026 15:52:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157524; x=1777762324; darn=vger.kernel.org; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:from:to:cc:subject:date:message-id :reply-to; bh=B3+cWJevLjvEH5KJKbFyl52jn1VIAtDDIGnwsxupOuY=; b=FOaf1Kzfbyxwo/nBGqbk516O0PLVwCNVhr2NxlQosblRRGiBdzSMSa7IJCSElTGTOj WSOI68B/rt1ELXon89cNNN7Csi6Ce/Rb2pN7LDAQtGVJCZiNLbXbfE1T+5bGX6/Ar2+R QzsVhXJeX269DpoI2H7/W3PKJhzH0vmGuYxG1Azu2/+UqpZTtAU7zEmTslQVqwpprelM giqKOaMjFoqwIM7hBf0lAOt5clzfyD5WZjtyAtgJPIV/e70nYVVwRIqrHbj7Wy9uLgHU q9CVgTfcN0QJO0ZuMH5MoKan9xtw21IpK/A2d6cqfSuLfnWdPaXv+mcmzA14gklspNhS KX7g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157524; x=1777762324; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to; bh=B3+cWJevLjvEH5KJKbFyl52jn1VIAtDDIGnwsxupOuY=; b=VDbNk8w2GOG30uf//SMP2w5IXJ4UiJKqLoWZTFsl1JT3tMpi7H1E1c+0HhFi3B3Pf7 mFcKmFlfXo4OdkzmdQLKD9EU3EmIZjbsyFBe171I71Ee5Fz/7GlE4hgmwdLPO0vw+Hl8 nLj0EC0ABwCbLRrzX0QABl4v0/4sAZWvBa+AEtANE7Ke4CgARjaLvf9b4/ccRCC0tVD8 rqKKyxUTgrqczEQSwYs+TtJx62mNHwsMQrXpaG9sHLy3O4epBqviGrF+TGWMyjvp7jU1 1cXFtQIdPsZfF8wCn64RColEHvDBI4QNpItQvgxYHZWPyw3LqnYgnndd5DP6zxbT7SB/ 1Z3A== X-Forwarded-Encrypted: i=1; AFNElJ9YXsal6bcp6OG2STGftVP+ydHqPodPzbc7iQvEf5SHd2a/zyAo0iFUa7XWSanGiIe87TapvYmDlKCUcGs=@vger.kernel.org X-Gm-Message-State: AOJu0Ywaj+9WvxraurwZcuIa5CVIKdFf43lQSUamEpltPNSom5zTHj+Z 89+2FTxRjVSfZxz8ZEsMzxmm4Z+opCJgHfhrq1vxZkd0W4jEZF7H4KNYQ3bf1e0Rgoa6UaG7Xb7 FSyztgbuC6Q== X-Received: from dybml37.prod.google.com ([2002:a05:7301:1525:b0:2db:47a8:4b3c]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:8c9f:b0:2e2:3381:2fba with SMTP id 5a478bee46e88-2e4660475e5mr19398650eec.3.1777157523668; Sat, 25 Apr 2026 15:52:03 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:45 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-54-irogers@google.com> Subject: [PATCH v7 53/59] perf: Remove libperl support, legacy Perl scripts and tests From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Remove libperl support from perf, along with legacy Perl scripts and their corresponding tests. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v5: 1. Fix Buffer Overflows: Added bounds checks in `check_ev_match()` and `find_scripts()` to prevent stack and heap buffer overflows when parsing long event or script names. --- tools/build/Makefile.feature | 1 - tools/build/feature/Makefile | 19 +- tools/build/feature/test-libperl.c | 10 - tools/perf/Documentation/perf-check.txt | 1 - tools/perf/Makefile.config | 22 +- tools/perf/Makefile.perf | 11 +- tools/perf/builtin-check.c | 2 +- tools/perf/builtin-script.c | 4 +- tools/perf/scripts/Build | 4 +- tools/perf/scripts/perl/Perf-Trace-Util/Build | 9 - .../scripts/perl/Perf-Trace-Util/Context.c | 122 --- .../scripts/perl/Perf-Trace-Util/Context.xs | 42 - .../scripts/perl/Perf-Trace-Util/Makefile.PL | 18 - .../perf/scripts/perl/Perf-Trace-Util/README | 59 -- .../Perf-Trace-Util/lib/Perf/Trace/Context.pm | 55 -- .../Perf-Trace-Util/lib/Perf/Trace/Core.pm | 192 ----- .../Perf-Trace-Util/lib/Perf/Trace/Util.pm | 94 --- .../perf/scripts/perl/Perf-Trace-Util/typemap | 1 - .../scripts/perl/bin/check-perf-trace-record | 2 - .../scripts/perl/bin/failed-syscalls-record | 3 - .../scripts/perl/bin/failed-syscalls-report | 10 - tools/perf/scripts/perl/bin/rw-by-file-record | 3 - tools/perf/scripts/perl/bin/rw-by-file-report | 10 - tools/perf/scripts/perl/bin/rw-by-pid-record | 2 - tools/perf/scripts/perl/bin/rw-by-pid-report | 3 - tools/perf/scripts/perl/bin/rwtop-record | 2 - tools/perf/scripts/perl/bin/rwtop-report | 20 - .../scripts/perl/bin/wakeup-latency-record | 6 - .../scripts/perl/bin/wakeup-latency-report | 3 - tools/perf/scripts/perl/check-perf-trace.pl | 106 --- tools/perf/scripts/perl/failed-syscalls.pl | 47 -- tools/perf/scripts/perl/rw-by-file.pl | 106 --- tools/perf/scripts/perl/rw-by-pid.pl | 184 ----- tools/perf/scripts/perl/rwtop.pl | 203 ----- tools/perf/scripts/perl/wakeup-latency.pl | 107 --- tools/perf/tests/make | 4 +- tools/perf/tests/shell/script_perl.sh | 102 --- tools/perf/ui/browsers/scripts.c | 21 +- tools/perf/util/scripting-engines/Build | 6 +- .../util/scripting-engines/trace-event-perl.c | 773 ------------------ tools/perf/util/trace-event-scripting.c | 65 -- tools/perf/util/trace-event.h | 2 +- 42 files changed, 25 insertions(+), 2431 deletions(-) delete mode 100644 tools/build/feature/test-libperl.c delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/Build delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/Context.c delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/Context.xs delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/README delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/= Context.pm delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/= Core.pm delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/= Util.pm delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/typemap delete mode 100644 tools/perf/scripts/perl/bin/check-perf-trace-record delete mode 100644 tools/perf/scripts/perl/bin/failed-syscalls-record delete mode 100644 tools/perf/scripts/perl/bin/failed-syscalls-report delete mode 100644 tools/perf/scripts/perl/bin/rw-by-file-record delete mode 100644 tools/perf/scripts/perl/bin/rw-by-file-report delete mode 100644 tools/perf/scripts/perl/bin/rw-by-pid-record delete mode 100644 tools/perf/scripts/perl/bin/rw-by-pid-report delete mode 100644 tools/perf/scripts/perl/bin/rwtop-record delete mode 100644 tools/perf/scripts/perl/bin/rwtop-report delete mode 100644 tools/perf/scripts/perl/bin/wakeup-latency-record delete mode 100644 tools/perf/scripts/perl/bin/wakeup-latency-report delete mode 100644 tools/perf/scripts/perl/check-perf-trace.pl delete mode 100644 tools/perf/scripts/perl/failed-syscalls.pl delete mode 100644 tools/perf/scripts/perl/rw-by-file.pl delete mode 100644 tools/perf/scripts/perl/rw-by-pid.pl delete mode 100644 tools/perf/scripts/perl/rwtop.pl delete mode 100644 tools/perf/scripts/perl/wakeup-latency.pl delete mode 100755 tools/perf/tests/shell/script_perl.sh delete mode 100644 tools/perf/util/scripting-engines/trace-event-perl.c diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 0b7a7c38cb88..96d4382144c4 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -118,7 +118,6 @@ FEATURE_TESTS_EXTRA :=3D \ libbfd-liberty \ libbfd-liberty-z \ libopencsd \ - libperl \ cxx \ llvm \ clang \ diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index f163a245837a..60e3df8142a5 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -30,7 +30,6 @@ FILES=3D \ test-libdebuginfod.bin \ test-libnuma.bin \ test-numa_num_possible_cpus.bin \ - test-libperl.bin \ test-libpython.bin \ test-libslang.bin \ test-libtraceevent.bin \ @@ -113,7 +112,7 @@ __BUILD =3D $(CC) $(CFLAGS) -MD -Wall -Werror -o $@ $(p= atsubst %.bin,%.c,$(@F)) $( BUILD =3D $(__BUILD) > $(@:.bin=3D.make.output) 2>&1 BUILD_BFD =3D $(BUILD) -DPACKAGE=3D'"perf"' -lbfd -ldl BUILD_ALL =3D $(BUILD) -fstack-protector-all -O2 -D_FORTIFY_SOURCE=3D2 -= ldw -lelf -lnuma -lelf -lslang \ - $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -ldl -lz -llzma -lzstd \ + $(FLAGS_PYTHON_EMBED) -ldl -lz -llzma -lzstd \ $(shell $(PKG_CONFIG) --libs --cflags openssl 2>/dev/null) =20 __BUILDXX =3D $(CXX) $(CXXFLAGS) -MD -Wall -Werror -o $@ $(patsubst %.bin,= %.cpp,$(@F)) $(LDFLAGS) @@ -253,22 +252,6 @@ $(OUTPUT)test-gtk2-infobar.bin: grep-libs =3D $(filter -l%,$(1)) strip-libs =3D $(filter-out -l%,$(1)) =20 -PERL_EMBED_LDOPTS =3D $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null) -PERL_EMBED_LDFLAGS =3D $(call strip-libs,$(PERL_EMBED_LDOPTS)) -PERL_EMBED_LIBADD =3D $(call grep-libs,$(PERL_EMBED_LDOPTS)) -PERL_EMBED_CCOPTS =3D $(shell perl -MExtUtils::Embed -e ccopts 2>/dev/null) -FLAGS_PERL_EMBED=3D$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) - -ifeq ($(CC_NO_CLANG), 0) - PERL_EMBED_LDOPTS :=3D $(filter-out -specs=3D%,$(PERL_EMBED_LDOPTS)) - PERL_EMBED_CCOPTS :=3D $(filter-out -flto=3Dauto -ffat-lto-objects, $(PE= RL_EMBED_CCOPTS)) - PERL_EMBED_CCOPTS :=3D $(filter-out -specs=3D%,$(PERL_EMBED_CCOPTS)) - FLAGS_PERL_EMBED +=3D -Wno-compound-token-split-by-macro -endif - -$(OUTPUT)test-libperl.bin: - $(BUILD) $(FLAGS_PERL_EMBED) - $(OUTPUT)test-libpython.bin: $(BUILD) $(FLAGS_PYTHON_EMBED) =20 diff --git a/tools/build/feature/test-libperl.c b/tools/build/feature/test-= libperl.c deleted file mode 100644 index 0415f437eb31..000000000000 --- a/tools/build/feature/test-libperl.c +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -int main(void) -{ - perl_alloc(); - - return 0; -} diff --git a/tools/perf/Documentation/perf-check.txt b/tools/perf/Documenta= tion/perf-check.txt index 09e1d35677f5..60fa9ea43a58 100644 --- a/tools/perf/Documentation/perf-check.txt +++ b/tools/perf/Documentation/perf-check.txt @@ -58,7 +58,6 @@ feature:: libLLVM / HAVE_LIBLLVM_SUPPORT libnuma / HAVE_LIBNUMA_SUPPORT libopencsd / HAVE_CSTRACE_SUPPORT - libperl / HAVE_LIBPERL_SUPPORT libpfm4 / HAVE_LIBPFM libpython / HAVE_LIBPYTHON_SUPPORT libslang / HAVE_SLANG_SUPPORT diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 333ddd0e4bd8..db30e73c5efc 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -820,26 +820,7 @@ ifdef GTK2 endif endif =20 -ifdef LIBPERL - PERL_EMBED_LDOPTS =3D $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/nu= ll) - PERL_EMBED_LDFLAGS =3D $(call strip-libs,$(PERL_EMBED_LDOPTS)) - PERL_EMBED_LIBADD =3D $(call grep-libs,$(PERL_EMBED_LDOPTS)) - PERL_EMBED_CCOPTS =3D $(shell perl -MExtUtils::Embed -e ccopts 2>/dev/nu= ll) - PERL_EMBED_CCOPTS :=3D $(filter-out -specs=3D%,$(PERL_EMBED_CCOPTS)) - PERL_EMBED_CCOPTS :=3D $(filter-out -flto% -ffat-lto-objects, $(PERL_EMB= ED_CCOPTS)) - PERL_EMBED_LDOPTS :=3D $(filter-out -specs=3D%,$(PERL_EMBED_LDOPTS)) - FLAGS_PERL_EMBED=3D$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) - - $(call feature_check,libperl) - ifneq ($(feature-libperl), 1) - $(error Missing perl devel files. Please install perl-ExtUtils-Embed/l= ibperl-dev) - else - LDFLAGS +=3D $(PERL_EMBED_LDFLAGS) - EXTLIBS +=3D $(PERL_EMBED_LIBADD) - CFLAGS +=3D -DHAVE_LIBPERL_SUPPORT - $(call detected,CONFIG_LIBPERL) - endif -endif + =20 ifeq ($(feature-timerfd), 1) CFLAGS +=3D -DHAVE_TIMERFD_SUPPORT @@ -1321,7 +1302,6 @@ $(call detected_var,tipdir_SQ) $(call detected_var,srcdir_SQ) $(call detected_var,LIBDIR) $(call detected_var,GTK_CFLAGS) -$(call detected_var,PERL_EMBED_CCOPTS) $(call detected_var,PYTHON_EMBED_CCOPTS) ifneq ($(BISON_FILE_PREFIX_MAP),) $(call detected_var,BISON_FILE_PREFIX_MAP) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index cee19c923c06..7bf349198622 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -17,7 +17,7 @@ include ../scripts/utilities.mak # # Define CROSS_COMPILE as prefix name of compiler if you want cross-builds. # -# Define LIBPERL to enable perl script extension. + # # Define NO_LIBPYTHON to disable python script extension. # @@ -1098,14 +1098,7 @@ endif $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' $(call QUIET_INSTALL, perf-iostat) \ $(INSTALL) $(OUTPUT)perf-iostat -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' -ifdef LIBPERL - $(call QUIET_INSTALL, perl-scripts) \ - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/P= erf-Trace-Util/lib/Perf/Trace'; \ - $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -m 644 -t '$(DE= STDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace= '; \ - $(INSTALL) scripts/perl/*.pl -m 644 -t '$(DESTDIR_SQ)$(perfexec_instdir_= SQ)/scripts/perl'; \ - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/b= in'; \ - $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/sc= ripts/perl/bin' -endif + ifndef NO_LIBPYTHON $(call QUIET_INSTALL, python-scripts) \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python= /Perf-Trace-Util/lib/Perf/Trace'; \ diff --git a/tools/perf/builtin-check.c b/tools/perf/builtin-check.c index 3641d263b345..944038814d62 100644 --- a/tools/perf/builtin-check.c +++ b/tools/perf/builtin-check.c @@ -51,7 +51,7 @@ struct feature_status supported_features[] =3D { FEATURE_STATUS("libLLVM", HAVE_LIBLLVM_SUPPORT), FEATURE_STATUS("libnuma", HAVE_LIBNUMA_SUPPORT), FEATURE_STATUS("libopencsd", HAVE_CSTRACE_SUPPORT), - FEATURE_STATUS_TIP("libperl", HAVE_LIBPERL_SUPPORT, "Deprecated, use LIBP= ERL=3D1 and install perl-ExtUtils-Embed/libperl-dev to build with it"), + FEATURE_STATUS("libpfm4", HAVE_LIBPFM), FEATURE_STATUS("libpython", HAVE_LIBPYTHON_SUPPORT), FEATURE_STATUS("libslang", HAVE_SLANG_SUPPORT), diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 3e3692088154..c0949556d1bb 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -2621,9 +2621,7 @@ static void process_stat_interval(u64 tstamp) =20 static void setup_scripting(void) { -#ifdef HAVE_LIBTRACEEVENT - setup_perl_scripting(); -#endif + setup_python_scripting(); } =20 diff --git a/tools/perf/scripts/Build b/tools/perf/scripts/Build index 91229a1fe3ff..d72cf9ad45fe 100644 --- a/tools/perf/scripts/Build +++ b/tools/perf/scripts/Build @@ -1,6 +1,4 @@ -ifeq ($(CONFIG_LIBTRACEEVENT),y) - perf-util-$(CONFIG_LIBPERL) +=3D perl/Perf-Trace-Util/ -endif + perf-util-$(CONFIG_LIBPYTHON) +=3D python/Perf-Trace-Util/ =20 ifdef MYPY diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Build b/tools/perf/scr= ipts/perl/Perf-Trace-Util/Build deleted file mode 100644 index 01a1a0ed51ae..000000000000 --- a/tools/perf/scripts/perl/Perf-Trace-Util/Build +++ /dev/null @@ -1,9 +0,0 @@ -perf-util-y +=3D Context.o - -CFLAGS_Context.o +=3D $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-stric= t-prototypes -Wno-bad-function-cast -Wno-declaration-after-statement -Wno-s= witch-enum -CFLAGS_Context.o +=3D -Wno-unused-parameter -Wno-nested-externs -Wno-undef -CFLAGS_Context.o +=3D -Wno-switch-default -Wno-shadow -Wno-thread-safety-a= nalysis - -ifeq ($(CC_NO_CLANG), 1) - CFLAGS_Context.o +=3D -Wno-unused-command-line-argument -endif diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Context.c b/tools/perf= /scripts/perl/Perf-Trace-Util/Context.c deleted file mode 100644 index 25c47d23a130..000000000000 --- a/tools/perf/scripts/perl/Perf-Trace-Util/Context.c +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * This file was generated automatically by ExtUtils::ParseXS version 2.18= _02 from the - * contents of Context.xs. Do not edit this file, edit Context.xs instead. - * - * ANY CHANGES MADE HERE WILL BE LOST!=20 - */ -#include -#ifndef HAS_BOOL -# define HAS_BOOL 1 -#endif -#line 1 "Context.xs" -/* - * Context.xs. XS interfaces for perf script. - * - * Copyright (C) 2009 Tom Zanussi - */ - -#include "EXTERN.h" -#include "perl.h" -#include "XSUB.h" -#include "../../../util/trace-event.h" - -#ifndef PERL_UNUSED_VAR -# define PERL_UNUSED_VAR(var) if (0) var =3D var -#endif - -#line 42 "Context.c" - -XS(XS_Perf__Trace__Context_common_pc); /* prototype to pass -Wmissing-prot= otypes */ -XS(XS_Perf__Trace__Context_common_pc) -{ -#ifdef dVAR - dVAR; dXSARGS; -#else - dXSARGS; -#endif - if (items !=3D 1) - Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_pc"= , "context"); - PERL_UNUSED_VAR(cv); /* -W */ - { - struct scripting_context * context =3D INT2PTR(struct scripting_context *= ,SvIV(ST(0))); - int RETVAL; - dXSTARG; - - RETVAL =3D common_pc(context); - XSprePUSH; PUSHi((IV)RETVAL); - } - XSRETURN(1); -} - - -XS(XS_Perf__Trace__Context_common_flags); /* prototype to pass -Wmissing-p= rototypes */ -XS(XS_Perf__Trace__Context_common_flags) -{ -#ifdef dVAR - dVAR; dXSARGS; -#else - dXSARGS; -#endif - if (items !=3D 1) - Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_fla= gs", "context"); - PERL_UNUSED_VAR(cv); /* -W */ - { - struct scripting_context * context =3D INT2PTR(struct scripting_context *= ,SvIV(ST(0))); - int RETVAL; - dXSTARG; - - RETVAL =3D common_flags(context); - XSprePUSH; PUSHi((IV)RETVAL); - } - XSRETURN(1); -} - - -XS(XS_Perf__Trace__Context_common_lock_depth); /* prototype to pass -Wmiss= ing-prototypes */ -XS(XS_Perf__Trace__Context_common_lock_depth) -{ -#ifdef dVAR - dVAR; dXSARGS; -#else - dXSARGS; -#endif - if (items !=3D 1) - Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_loc= k_depth", "context"); - PERL_UNUSED_VAR(cv); /* -W */ - { - struct scripting_context * context =3D INT2PTR(struct scripting_context *= ,SvIV(ST(0))); - int RETVAL; - dXSTARG; - - RETVAL =3D common_lock_depth(context); - XSprePUSH; PUSHi((IV)RETVAL); - } - XSRETURN(1); -} - -#ifdef __cplusplus -extern "C" -#endif -XS(boot_Perf__Trace__Context); /* prototype to pass -Wmissing-prototypes */ -XS(boot_Perf__Trace__Context) -{ -#ifdef dVAR - dVAR; dXSARGS; -#else - dXSARGS; -#endif - const char* file =3D __FILE__; - - PERL_UNUSED_VAR(cv); /* -W */ - PERL_UNUSED_VAR(items); /* -W */ - XS_VERSION_BOOTCHECK ; - - newXSproto("Perf::Trace::Context::common_pc", XS_Perf__Trace__Cont= ext_common_pc, file, "$"); - newXSproto("Perf::Trace::Context::common_flags", XS_Perf__Trace__C= ontext_common_flags, file, "$"); - newXSproto("Perf::Trace::Context::common_lock_depth", XS_Perf__Tra= ce__Context_common_lock_depth, file, "$"); - if (PL_unitcheckav) - call_list(PL_scopestack_ix, PL_unitcheckav); - XSRETURN_YES; -} - diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs b/tools/per= f/scripts/perl/Perf-Trace-Util/Context.xs deleted file mode 100644 index 8c7ea42444d1..000000000000 --- a/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Context.xs. XS interfaces for perf script. - * - * Copyright (C) 2009 Tom Zanussi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 = USA - * - */ - -#include "EXTERN.h" -#include "perl.h" -#include "XSUB.h" -#include "../../../perf.h" -#include "../../../util/trace-event.h" - -MODULE =3D Perf::Trace::Context PACKAGE =3D Perf::Trace::Context -PROTOTYPES: ENABLE - -int -common_pc(context) - struct scripting_context * context - -int -common_flags(context) - struct scripting_context * context - -int -common_lock_depth(context) - struct scripting_context * context - diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL b/tools/pe= rf/scripts/perl/Perf-Trace-Util/Makefile.PL deleted file mode 100644 index e8994332d7dc..000000000000 --- a/tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -use 5.010000; -use ExtUtils::MakeMaker; -# See lib/ExtUtils/MakeMaker.pm for details of how to influence -# the contents of the Makefile that is written. -WriteMakefile( - NAME =3D> 'Perf::Trace::Context', - VERSION_FROM =3D> 'lib/Perf/Trace/Context.pm', # finds $VERSION - PREREQ_PM =3D> {}, # e.g., Module::Name =3D> 1.1 - ($] >=3D 5.005 ? ## Add these new keywords supported since 5.005 - (ABSTRACT_FROM =3D> 'lib/Perf/Trace/Context.pm', # retrieve abstrac= t from module - AUTHOR =3D> 'Tom Zanussi ') : ()), - LIBS =3D> [''], # e.g., '-lm' - DEFINE =3D> '-I ../..', # e.g., '-DHAVE_SOMETHING' - INC =3D> '-I.', # e.g., '-I. -I/usr/include/other' - # Un-comment this if you add C files to link with later: - OBJECT =3D> 'Context.o', # link all the C files too -); diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/README b/tools/perf/sc= ripts/perl/Perf-Trace-Util/README deleted file mode 100644 index 2f0c7f3043ee..000000000000 --- a/tools/perf/scripts/perl/Perf-Trace-Util/README +++ /dev/null @@ -1,59 +0,0 @@ -Perf-Trace-Util version 0.01 -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D - -This module contains utility functions for use with perf script. - -Core.pm and Util.pm are pure Perl modules; Core.pm contains routines -that the core perf support for Perl calls on and should always be -'used', while Util.pm contains useful but optional utility functions -that scripts may want to use. Context.pm contains the Perl->C -interface that allows scripts to access data in the embedding perf -executable; scripts wishing to do that should 'use Context.pm'. - -The Perl->C perf interface is completely driven by Context.xs. If you -want to add new Perl functions that end up accessing C data in the -perf executable, you add desciptions of the new functions here. -scripting_context is a pointer to the perf data in the perf executable -that you want to access - it's passed as the second parameter, -$context, to all handler functions. - -After you do that: - - perl Makefile.PL # to create a Makefile for the next step - make # to create Context.c - - edit Context.c to add const to the char* file =3D __FILE__ line in - XS(boot_Perf__Trace__Context) to silence a warning/error. - - You can delete the Makefile, object files and anything else that was - generated e.g. blib and shared library, etc, except for of course - Context.c - - You should then be able to run the normal perf make as usual. - -INSTALLATION - -Building perf with perf script Perl scripting should install this -module in the right place. - -You should make sure libperl and ExtUtils/Embed.pm are installed first -e.g. apt-get install libperl-dev or yum install perl-ExtUtils-Embed. - -DEPENDENCIES - -This module requires these other modules and libraries: - - None - -COPYRIGHT AND LICENCE - -Copyright (C) 2009 by Tom Zanussi - -This library is free software; you can redistribute it and/or modify -it under the same terms as Perl itself, either Perl version 5.10.0 or, -at your option, any later version of Perl 5 you may have available. - -Alternatively, this software may be distributed under the terms of the -GNU General Public License ("GPL") version 2 as published by the Free -Software Foundation. - diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context= .pm b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm deleted file mode 100644 index 4e2f6039ac92..000000000000 --- a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm +++ /dev/null @@ -1,55 +0,0 @@ -package Perf::Trace::Context; - -use 5.010000; -use strict; -use warnings; - -require Exporter; - -our @ISA =3D qw(Exporter); - -our %EXPORT_TAGS =3D ( 'all' =3D> [ qw( -) ] ); - -our @EXPORT_OK =3D ( @{ $EXPORT_TAGS{'all'} } ); - -our @EXPORT =3D qw( - common_pc common_flags common_lock_depth -); - -our $VERSION =3D '0.01'; - -require XSLoader; -XSLoader::load('Perf::Trace::Context', $VERSION); - -1; -__END__ -=3Dhead1 NAME - -Perf::Trace::Context - Perl extension for accessing functions in perf. - -=3Dhead1 SYNOPSIS - - use Perf::Trace::Context; - -=3Dhead1 SEE ALSO - -Perf (script) documentation - -=3Dhead1 AUTHOR - -Tom Zanussi, Etzanussi@gmail.com - -=3Dhead1 COPYRIGHT AND LICENSE - -Copyright (C) 2009 by Tom Zanussi - -This library is free software; you can redistribute it and/or modify -it under the same terms as Perl itself, either Perl version 5.10.0 or, -at your option, any later version of Perl 5 you may have available. - -Alternatively, this software may be distributed under the terms of the -GNU General Public License ("GPL") version 2 as published by the Free -Software Foundation. - -=3Dcut diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm= b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm deleted file mode 100644 index 9158458d3eeb..000000000000 --- a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm +++ /dev/null @@ -1,192 +0,0 @@ -package Perf::Trace::Core; - -use 5.010000; -use strict; -use warnings; - -require Exporter; - -our @ISA =3D qw(Exporter); - -our %EXPORT_TAGS =3D ( 'all' =3D> [ qw( -) ] ); - -our @EXPORT_OK =3D ( @{ $EXPORT_TAGS{'all'} } ); - -our @EXPORT =3D qw( -define_flag_field define_flag_value flag_str dump_flag_fields -define_symbolic_field define_symbolic_value symbol_str dump_symbolic_fields -trace_flag_str -); - -our $VERSION =3D '0.01'; - -my %trace_flags =3D (0x00 =3D> "NONE", - 0x01 =3D> "IRQS_OFF", - 0x02 =3D> "IRQS_NOSUPPORT", - 0x04 =3D> "NEED_RESCHED", - 0x08 =3D> "HARDIRQ", - 0x10 =3D> "SOFTIRQ"); - -sub trace_flag_str -{ - my ($value) =3D @_; - - my $string; - - my $print_delim =3D 0; - - foreach my $idx (sort {$a <=3D> $b} keys %trace_flags) { - if (!$value && !$idx) { - $string .=3D "NONE"; - last; - } - - if ($idx && ($value & $idx) =3D=3D $idx) { - if ($print_delim) { - $string .=3D " | "; - } - $string .=3D "$trace_flags{$idx}"; - $print_delim =3D 1; - $value &=3D ~$idx; - } - } - - return $string; -} - -my %flag_fields; -my %symbolic_fields; - -sub flag_str -{ - my ($event_name, $field_name, $value) =3D @_; - - my $string; - - if ($flag_fields{$event_name}{$field_name}) { - my $print_delim =3D 0; - foreach my $idx (sort {$a <=3D> $b} keys %{$flag_fields{$event_name}{$fie= ld_name}{"values"}}) { - if (!$value && !$idx) { - $string .=3D "$flag_fields{$event_name}{$field_name}{'values'}{$idx}"; - last; - } - if ($idx && ($value & $idx) =3D=3D $idx) { - if ($print_delim && $flag_fields{$event_name}{$field_name}{'delim'}) { - $string .=3D " $flag_fields{$event_name}{$field_name}{'delim'} "; - } - $string .=3D "$flag_fields{$event_name}{$field_name}{'values'}{$idx}"; - $print_delim =3D 1; - $value &=3D ~$idx; - } - } - } - - return $string; -} - -sub define_flag_field -{ - my ($event_name, $field_name, $delim) =3D @_; - - $flag_fields{$event_name}{$field_name}{"delim"} =3D $delim; -} - -sub define_flag_value -{ - my ($event_name, $field_name, $value, $field_str) =3D @_; - - $flag_fields{$event_name}{$field_name}{"values"}{$value} =3D $field_st= r; -} - -sub dump_flag_fields -{ - for my $event (keys %flag_fields) { - print "event $event:\n"; - for my $field (keys %{$flag_fields{$event}}) { - print " field: $field:\n"; - print " delim: $flag_fields{$event}{$field}{'delim'}\n"; - foreach my $idx (sort {$a <=3D> $b} keys %{$flag_fields{$event}{$fiel= d}{"values"}}) { - print " value $idx: $flag_fields{$event}{$field}{'values'}{$idx}\= n"; - } - } - } -} - -sub symbol_str -{ - my ($event_name, $field_name, $value) =3D @_; - - if ($symbolic_fields{$event_name}{$field_name}) { - foreach my $idx (sort {$a <=3D> $b} keys %{$symbolic_fields{$event_name}{= $field_name}{"values"}}) { - if (!$value && !$idx) { - return "$symbolic_fields{$event_name}{$field_name}{'values'}{$idx}"; - last; - } - if ($value =3D=3D $idx) { - return "$symbolic_fields{$event_name}{$field_name}{'values'}{$idx}"; - } - } - } - - return undef; -} - -sub define_symbolic_field -{ - my ($event_name, $field_name) =3D @_; - - # nothing to do, really -} - -sub define_symbolic_value -{ - my ($event_name, $field_name, $value, $field_str) =3D @_; - - $symbolic_fields{$event_name}{$field_name}{"values"}{$value} =3D $fiel= d_str; -} - -sub dump_symbolic_fields -{ - for my $event (keys %symbolic_fields) { - print "event $event:\n"; - for my $field (keys %{$symbolic_fields{$event}}) { - print " field: $field:\n"; - foreach my $idx (sort {$a <=3D> $b} keys %{$symbolic_fields{$event}{$= field}{"values"}}) { - print " value $idx: $symbolic_fields{$event}{$field}{'values'}{$i= dx}\n"; - } - } - } -} - -1; -__END__ -=3Dhead1 NAME - -Perf::Trace::Core - Perl extension for perf script - -=3Dhead1 SYNOPSIS - - use Perf::Trace::Core - -=3Dhead1 SEE ALSO - -Perf (script) documentation - -=3Dhead1 AUTHOR - -Tom Zanussi, Etzanussi@gmail.com - -=3Dhead1 COPYRIGHT AND LICENSE - -Copyright (C) 2009 by Tom Zanussi - -This library is free software; you can redistribute it and/or modify -it under the same terms as Perl itself, either Perl version 5.10.0 or, -at your option, any later version of Perl 5 you may have available. - -Alternatively, this software may be distributed under the terms of the -GNU General Public License ("GPL") version 2 as published by the Free -Software Foundation. - -=3Dcut diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm= b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm deleted file mode 100644 index 053500114625..000000000000 --- a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm +++ /dev/null @@ -1,94 +0,0 @@ -package Perf::Trace::Util; - -use 5.010000; -use strict; -use warnings; - -require Exporter; - -our @ISA =3D qw(Exporter); - -our %EXPORT_TAGS =3D ( 'all' =3D> [ qw( -) ] ); - -our @EXPORT_OK =3D ( @{ $EXPORT_TAGS{'all'} } ); - -our @EXPORT =3D qw( -avg nsecs nsecs_secs nsecs_nsecs nsecs_usecs print_nsecs -clear_term -); - -our $VERSION =3D '0.01'; - -sub avg -{ - my ($total, $n) =3D @_; - - return $total / $n; -} - -my $NSECS_PER_SEC =3D 1000000000; - -sub nsecs -{ - my ($secs, $nsecs) =3D @_; - - return $secs * $NSECS_PER_SEC + $nsecs; -} - -sub nsecs_secs { - my ($nsecs) =3D @_; - - return $nsecs / $NSECS_PER_SEC; -} - -sub nsecs_nsecs { - my ($nsecs) =3D @_; - - return $nsecs % $NSECS_PER_SEC; -} - -sub nsecs_str { - my ($nsecs) =3D @_; - - my $str =3D sprintf("%5u.%09u", nsecs_secs($nsecs), nsecs_nsecs($nsecs= )); - - return $str; -} - -sub clear_term -{ - print "\x1b[H\x1b[2J"; -} - -1; -__END__ -=3Dhead1 NAME - -Perf::Trace::Util - Perl extension for perf script - -=3Dhead1 SYNOPSIS - - use Perf::Trace::Util; - -=3Dhead1 SEE ALSO - -Perf (script) documentation - -=3Dhead1 AUTHOR - -Tom Zanussi, Etzanussi@gmail.com - -=3Dhead1 COPYRIGHT AND LICENSE - -Copyright (C) 2009 by Tom Zanussi - -This library is free software; you can redistribute it and/or modify -it under the same terms as Perl itself, either Perl version 5.10.0 or, -at your option, any later version of Perl 5 you may have available. - -Alternatively, this software may be distributed under the terms of the -GNU General Public License ("GPL") version 2 as published by the Free -Software Foundation. - -=3Dcut diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/typemap b/tools/perf/s= cripts/perl/Perf-Trace-Util/typemap deleted file mode 100644 index 840836804aa7..000000000000 --- a/tools/perf/scripts/perl/Perf-Trace-Util/typemap +++ /dev/null @@ -1 +0,0 @@ -struct scripting_context * T_PTR diff --git a/tools/perf/scripts/perl/bin/check-perf-trace-record b/tools/pe= rf/scripts/perl/bin/check-perf-trace-record deleted file mode 100644 index 423ad6aed056..000000000000 --- a/tools/perf/scripts/perl/bin/check-perf-trace-record +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -perf record -a -e kmem:kmalloc -e irq:softirq_entry -e kmem:kfree diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-record b/tools/per= f/scripts/perl/bin/failed-syscalls-record deleted file mode 100644 index 74685f318379..000000000000 --- a/tools/perf/scripts/perl/bin/failed-syscalls-record +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -(perf record -e raw_syscalls:sys_exit $@ || \ - perf record -e syscalls:sys_exit $@) 2> /dev/null diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-report b/tools/per= f/scripts/perl/bin/failed-syscalls-report deleted file mode 100644 index 9f83cc1ad8ba..000000000000 --- a/tools/perf/scripts/perl/bin/failed-syscalls-report +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# description: system-wide failed syscalls -# args: [comm] -if [ $# -gt 0 ] ; then - if ! expr match "$1" "-" > /dev/null ; then - comm=3D$1 - shift - fi -fi -perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/failed-syscalls.pl $comm diff --git a/tools/perf/scripts/perl/bin/rw-by-file-record b/tools/perf/scr= ipts/perl/bin/rw-by-file-record deleted file mode 100644 index 33efc8673aae..000000000000 --- a/tools/perf/scripts/perl/bin/rw-by-file-record +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -perf record -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@ - diff --git a/tools/perf/scripts/perl/bin/rw-by-file-report b/tools/perf/scr= ipts/perl/bin/rw-by-file-report deleted file mode 100644 index 77200b3f3100..000000000000 --- a/tools/perf/scripts/perl/bin/rw-by-file-report +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# description: r/w activity for a program, by file -# args: -if [ $# -lt 1 ] ; then - echo "usage: rw-by-file " - exit -fi -comm=3D$1 -shift -perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-file.pl $comm diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-record b/tools/perf/scri= pts/perl/bin/rw-by-pid-record deleted file mode 100644 index 7cb9db230448..000000000000 --- a/tools/perf/scripts/perl/bin/rw-by-pid-record +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -perf record -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscal= ls:sys_enter_write -e syscalls:sys_exit_write $@ diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-report b/tools/perf/scri= pts/perl/bin/rw-by-pid-report deleted file mode 100644 index a27b9f311f95..000000000000 --- a/tools/perf/scripts/perl/bin/rw-by-pid-report +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -# description: system-wide r/w activity -perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-pid.pl diff --git a/tools/perf/scripts/perl/bin/rwtop-record b/tools/perf/scripts/= perl/bin/rwtop-record deleted file mode 100644 index 7cb9db230448..000000000000 --- a/tools/perf/scripts/perl/bin/rwtop-record +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -perf record -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscal= ls:sys_enter_write -e syscalls:sys_exit_write $@ diff --git a/tools/perf/scripts/perl/bin/rwtop-report b/tools/perf/scripts/= perl/bin/rwtop-report deleted file mode 100644 index 83e11ec2e190..000000000000 --- a/tools/perf/scripts/perl/bin/rwtop-report +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# description: system-wide r/w top -# args: [interval] -n_args=3D0 -for i in "$@" -do - if expr match "$i" "-" > /dev/null ; then - break - fi - n_args=3D$(( $n_args + 1 )) -done -if [ "$n_args" -gt 1 ] ; then - echo "usage: rwtop-report [interval]" - exit -fi -if [ "$n_args" -gt 0 ] ; then - interval=3D$1 - shift -fi -perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rwtop.pl $interval diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-record b/tools/perf= /scripts/perl/bin/wakeup-latency-record deleted file mode 100644 index 464251a1bd7e..000000000000 --- a/tools/perf/scripts/perl/bin/wakeup-latency-record +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -perf record -e sched:sched_switch -e sched:sched_wakeup $@ - - - - diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-report b/tools/perf= /scripts/perl/bin/wakeup-latency-report deleted file mode 100644 index 889e8130cca5..000000000000 --- a/tools/perf/scripts/perl/bin/wakeup-latency-report +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -# description: system-wide min/max/avg wakeup latency -perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/wakeup-latency.pl diff --git a/tools/perf/scripts/perl/check-perf-trace.pl b/tools/perf/scrip= ts/perl/check-perf-trace.pl deleted file mode 100644 index d307ce8fd6ed..000000000000 --- a/tools/perf/scripts/perl/check-perf-trace.pl +++ /dev/null @@ -1,106 +0,0 @@ -# perf script event handlers, generated by perf script -g perl -# (c) 2009, Tom Zanussi -# Licensed under the terms of the GNU GPL License version 2 - -# This script tests basic functionality such as flag and symbol -# strings, common_xxx() calls back into perf, begin, end, unhandled -# events, etc. Basically, if this script runs successfully and -# displays expected results, perl scripting support should be ok. - -use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; -use lib "./Perf-Trace-Util/lib"; -use Perf::Trace::Core; -use Perf::Trace::Context; -use Perf::Trace::Util; - -sub trace_begin -{ - print "trace_begin\n"; -} - -sub trace_end -{ - print "trace_end\n"; - - print_unhandled(); -} - -sub irq::softirq_entry -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain, - $vec) =3D @_; - - print_header($event_name, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm); - - print_uncommon($context); - - printf("vec=3D%s\n", - symbol_str("irq::softirq_entry", "vec", $vec)); -} - -sub kmem::kmalloc -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain, - $call_site, $ptr, $bytes_req, $bytes_alloc, - $gfp_flags) =3D @_; - - print_header($event_name, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm); - - print_uncommon($context); - - printf("call_site=3D%p, ptr=3D%p, bytes_req=3D%u, bytes_alloc=3D%u, ". - "gfp_flags=3D%s\n", - $call_site, $ptr, $bytes_req, $bytes_alloc, - - flag_str("kmem::kmalloc", "gfp_flags", $gfp_flags)); -} - -# print trace fields not included in handler args -sub print_uncommon -{ - my ($context) =3D @_; - - printf("common_preempt_count=3D%d, common_flags=3D%s, common_lock_dept= h=3D%d, ", - common_pc($context), trace_flag_str(common_flags($context)), - common_lock_depth($context)); - -} - -my %unhandled; - -sub print_unhandled -{ - if ((scalar keys %unhandled) =3D=3D 0) { - return; - } - - print "\nunhandled events:\n\n"; - - printf("%-40s %10s\n", "event", "count"); - printf("%-40s %10s\n", "----------------------------------------", - "-----------"); - - foreach my $event_name (keys %unhandled) { - printf("%-40s %10d\n", $event_name, $unhandled{$event_name}); - } -} - -sub trace_unhandled -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain) =3D @_; - - $unhandled{$event_name}++; -} - -sub print_header -{ - my ($event_name, $cpu, $secs, $nsecs, $pid, $comm) =3D @_; - - printf("%-20s %5u %05u.%09u %8u %-20s ", - $event_name, $cpu, $secs, $nsecs, $pid, $comm); -} diff --git a/tools/perf/scripts/perl/failed-syscalls.pl b/tools/perf/script= s/perl/failed-syscalls.pl deleted file mode 100644 index 05954a8f363a..000000000000 --- a/tools/perf/scripts/perl/failed-syscalls.pl +++ /dev/null @@ -1,47 +0,0 @@ -# failed system call counts -# (c) 2010, Tom Zanussi -# Licensed under the terms of the GNU GPL License version 2 -# -# Displays system-wide failed system call totals -# If a [comm] arg is specified, only syscalls called by [comm] are display= ed. - -use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; -use lib "./Perf-Trace-Util/lib"; -use Perf::Trace::Core; -use Perf::Trace::Context; -use Perf::Trace::Util; - -my $for_comm =3D shift; - -my %failed_syscalls; - -sub raw_syscalls::sys_exit -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain, - $id, $ret) =3D @_; - - if ($ret < 0) { - $failed_syscalls{$common_comm}++; - } -} - -sub syscalls::sys_exit -{ - raw_syscalls::sys_exit(@_) -} - -sub trace_end -{ - printf("\nfailed syscalls by comm:\n\n"); - - printf("%-20s %10s\n", "comm", "# errors"); - printf("%-20s %6s %10s\n", "--------------------", "----------"); - - foreach my $comm (sort {$failed_syscalls{$b} <=3D> $failed_syscalls{$a= }} - keys %failed_syscalls) { - next if ($for_comm && $comm ne $for_comm); - - printf("%-20s %10s\n", $comm, $failed_syscalls{$comm}); - } -} diff --git a/tools/perf/scripts/perl/rw-by-file.pl b/tools/perf/scripts/per= l/rw-by-file.pl deleted file mode 100644 index 92a750b8552b..000000000000 --- a/tools/perf/scripts/perl/rw-by-file.pl +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/perl -w -# SPDX-License-Identifier: GPL-2.0-only -# (c) 2009, Tom Zanussi - -# Display r/w activity for files read/written to for a given program - -# The common_* event handler fields are the most useful fields common to -# all events. They don't necessarily correspond to the 'common_*' fields -# in the status files. Those fields not available as handler params can -# be retrieved via script functions of the form get_common_*(). - -use 5.010000; -use strict; -use warnings; - -use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; -use lib "./Perf-Trace-Util/lib"; -use Perf::Trace::Core; -use Perf::Trace::Util; - -my $usage =3D "perf script -s rw-by-file.pl \n"; - -my $for_comm =3D shift or die $usage; - -my %reads; -my %writes; - -sub syscalls::sys_enter_read -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain, $nr, $fd, $buf, $count) =3D= @_; - - if ($common_comm eq $for_comm) { - $reads{$fd}{bytes_requested} +=3D $count; - $reads{$fd}{total_reads}++; - } -} - -sub syscalls::sys_enter_write -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain, $nr, $fd, $buf, $count) =3D= @_; - - if ($common_comm eq $for_comm) { - $writes{$fd}{bytes_written} +=3D $count; - $writes{$fd}{total_writes}++; - } -} - -sub trace_end -{ - printf("file read counts for $for_comm:\n\n"); - - printf("%6s %10s %10s\n", "fd", "# reads", "bytes_requested"); - printf("%6s %10s %10s\n", "------", "----------", "-----------"); - - foreach my $fd (sort {$reads{$b}{bytes_requested} <=3D> - $reads{$a}{bytes_requested}} keys %reads) { - my $total_reads =3D $reads{$fd}{total_reads}; - my $bytes_requested =3D $reads{$fd}{bytes_requested}; - printf("%6u %10u %10u\n", $fd, $total_reads, $bytes_requested); - } - - printf("\nfile write counts for $for_comm:\n\n"); - - printf("%6s %10s %10s\n", "fd", "# writes", "bytes_written"); - printf("%6s %10s %10s\n", "------", "----------", "-----------"); - - foreach my $fd (sort {$writes{$b}{bytes_written} <=3D> - $writes{$a}{bytes_written}} keys %writes) { - my $total_writes =3D $writes{$fd}{total_writes}; - my $bytes_written =3D $writes{$fd}{bytes_written}; - printf("%6u %10u %10u\n", $fd, $total_writes, $bytes_written); - } - - print_unhandled(); -} - -my %unhandled; - -sub print_unhandled -{ - if ((scalar keys %unhandled) =3D=3D 0) { - return; - } - - print "\nunhandled events:\n\n"; - - printf("%-40s %10s\n", "event", "count"); - printf("%-40s %10s\n", "----------------------------------------", - "-----------"); - - foreach my $event_name (keys %unhandled) { - printf("%-40s %10d\n", $event_name, $unhandled{$event_name}); - } -} - -sub trace_unhandled -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain) =3D @_; - - $unhandled{$event_name}++; -} - - diff --git a/tools/perf/scripts/perl/rw-by-pid.pl b/tools/perf/scripts/perl= /rw-by-pid.pl deleted file mode 100644 index d789fe39caab..000000000000 --- a/tools/perf/scripts/perl/rw-by-pid.pl +++ /dev/null @@ -1,184 +0,0 @@ -#!/usr/bin/perl -w -# SPDX-License-Identifier: GPL-2.0-only -# (c) 2009, Tom Zanussi - -# Display r/w activity for all processes - -# The common_* event handler fields are the most useful fields common to -# all events. They don't necessarily correspond to the 'common_*' fields -# in the status files. Those fields not available as handler params can -# be retrieved via script functions of the form get_common_*(). - -use 5.010000; -use strict; -use warnings; - -use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; -use lib "./Perf-Trace-Util/lib"; -use Perf::Trace::Core; -use Perf::Trace::Util; - -my %reads; -my %writes; - -sub syscalls::sys_exit_read -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain, - $nr, $ret) =3D @_; - - if ($ret > 0) { - $reads{$common_pid}{bytes_read} +=3D $ret; - } else { - if (!defined ($reads{$common_pid}{bytes_read})) { - $reads{$common_pid}{bytes_read} =3D 0; - } - $reads{$common_pid}{errors}{$ret}++; - } -} - -sub syscalls::sys_enter_read -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain, - $nr, $fd, $buf, $count) =3D @_; - - $reads{$common_pid}{bytes_requested} +=3D $count; - $reads{$common_pid}{total_reads}++; - $reads{$common_pid}{comm} =3D $common_comm; -} - -sub syscalls::sys_exit_write -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain, - $nr, $ret) =3D @_; - - if ($ret <=3D 0) { - $writes{$common_pid}{errors}{$ret}++; - } -} - -sub syscalls::sys_enter_write -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain, - $nr, $fd, $buf, $count) =3D @_; - - $writes{$common_pid}{bytes_written} +=3D $count; - $writes{$common_pid}{total_writes}++; - $writes{$common_pid}{comm} =3D $common_comm; -} - -sub trace_end -{ - printf("read counts by pid:\n\n"); - - printf("%6s %20s %10s %10s %10s\n", "pid", "comm", - "# reads", "bytes_requested", "bytes_read"); - printf("%6s %-20s %10s %10s %10s\n", "------", "------------------= --", - "-----------", "----------", "----------"); - - foreach my $pid (sort { ($reads{$b}{bytes_read} || 0) <=3D> - ($reads{$a}{bytes_read} || 0) } keys %reads) { - my $comm =3D $reads{$pid}{comm} || ""; - my $total_reads =3D $reads{$pid}{total_reads} || 0; - my $bytes_requested =3D $reads{$pid}{bytes_requested} || 0; - my $bytes_read =3D $reads{$pid}{bytes_read} || 0; - - printf("%6s %-20s %10s %10s %10s\n", $pid, $comm, - $total_reads, $bytes_requested, $bytes_read); - } - - printf("\nfailed reads by pid:\n\n"); - - printf("%6s %20s %6s %10s\n", "pid", "comm", "error #", "# errors"); - printf("%6s %20s %6s %10s\n", "------", "--------------------", - "------", "----------"); - - my @errcounts =3D (); - - foreach my $pid (keys %reads) { - foreach my $error (keys %{$reads{$pid}{errors}}) { - my $comm =3D $reads{$pid}{comm} || ""; - my $errcount =3D $reads{$pid}{errors}{$error} || 0; - push @errcounts, [$pid, $comm, $error, $errcount]; - } - } - - @errcounts =3D sort { $b->[3] <=3D> $a->[3] } @errcounts; - - for my $i (0 .. $#errcounts) { - printf("%6d %-20s %6d %10s\n", $errcounts[$i][0], - $errcounts[$i][1], $errcounts[$i][2], $errcounts[$i][3]); - } - - printf("\nwrite counts by pid:\n\n"); - - printf("%6s %20s %10s %10s\n", "pid", "comm", - "# writes", "bytes_written"); - printf("%6s %-20s %10s %10s\n", "------", "--------------------", - "-----------", "----------"); - - foreach my $pid (sort { ($writes{$b}{bytes_written} || 0) <=3D> - ($writes{$a}{bytes_written} || 0)} keys %writes) { - my $comm =3D $writes{$pid}{comm} || ""; - my $total_writes =3D $writes{$pid}{total_writes} || 0; - my $bytes_written =3D $writes{$pid}{bytes_written} || 0; - - printf("%6s %-20s %10s %10s\n", $pid, $comm, - $total_writes, $bytes_written); - } - - printf("\nfailed writes by pid:\n\n"); - - printf("%6s %20s %6s %10s\n", "pid", "comm", "error #", "# errors"); - printf("%6s %20s %6s %10s\n", "------", "--------------------", - "------", "----------"); - - @errcounts =3D (); - - foreach my $pid (keys %writes) { - foreach my $error (keys %{$writes{$pid}{errors}}) { - my $comm =3D $writes{$pid}{comm} || ""; - my $errcount =3D $writes{$pid}{errors}{$error} || 0; - push @errcounts, [$pid, $comm, $error, $errcount]; - } - } - - @errcounts =3D sort { $b->[3] <=3D> $a->[3] } @errcounts; - - for my $i (0 .. $#errcounts) { - printf("%6d %-20s %6d %10s\n", $errcounts[$i][0], - $errcounts[$i][1], $errcounts[$i][2], $errcounts[$i][3]); - } - - print_unhandled(); -} - -my %unhandled; - -sub print_unhandled -{ - if ((scalar keys %unhandled) =3D=3D 0) { - return; - } - - print "\nunhandled events:\n\n"; - - printf("%-40s %10s\n", "event", "count"); - printf("%-40s %10s\n", "----------------------------------------", - "-----------"); - - foreach my $event_name (keys %unhandled) { - printf("%-40s %10d\n", $event_name, $unhandled{$event_name}); - } -} - -sub trace_unhandled -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain) =3D @_; - - $unhandled{$event_name}++; -} diff --git a/tools/perf/scripts/perl/rwtop.pl b/tools/perf/scripts/perl/rwt= op.pl deleted file mode 100644 index eba4df67af6b..000000000000 --- a/tools/perf/scripts/perl/rwtop.pl +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/perl -w -# SPDX-License-Identifier: GPL-2.0-only -# (c) 2010, Tom Zanussi - -# read/write top -# -# Periodically displays system-wide r/w call activity, broken down by -# pid. If an [interval] arg is specified, the display will be -# refreshed every [interval] seconds. The default interval is 3 -# seconds. - -use 5.010000; -use strict; -use warnings; - -use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; -use lib "./Perf-Trace-Util/lib"; -use Perf::Trace::Core; -use Perf::Trace::Util; -use POSIX qw/SIGALRM SA_RESTART/; - -my $default_interval =3D 3; -my $nlines =3D 20; -my $print_thread; -my $print_pending =3D 0; - -my %reads; -my %writes; - -my $interval =3D shift; -if (!$interval) { - $interval =3D $default_interval; -} - -sub syscalls::sys_exit_read -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain, - $nr, $ret) =3D @_; - - print_check(); - - if ($ret > 0) { - $reads{$common_pid}{bytes_read} +=3D $ret; - } else { - if (!defined ($reads{$common_pid}{bytes_read})) { - $reads{$common_pid}{bytes_read} =3D 0; - } - $reads{$common_pid}{errors}{$ret}++; - } -} - -sub syscalls::sys_enter_read -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain, - $nr, $fd, $buf, $count) =3D @_; - - print_check(); - - $reads{$common_pid}{bytes_requested} +=3D $count; - $reads{$common_pid}{total_reads}++; - $reads{$common_pid}{comm} =3D $common_comm; -} - -sub syscalls::sys_exit_write -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain, - $nr, $ret) =3D @_; - - print_check(); - - if ($ret <=3D 0) { - $writes{$common_pid}{errors}{$ret}++; - } -} - -sub syscalls::sys_enter_write -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain, - $nr, $fd, $buf, $count) =3D @_; - - print_check(); - - $writes{$common_pid}{bytes_written} +=3D $count; - $writes{$common_pid}{total_writes}++; - $writes{$common_pid}{comm} =3D $common_comm; -} - -sub trace_begin -{ - my $sa =3D POSIX::SigAction->new(\&set_print_pending); - $sa->flags(SA_RESTART); - $sa->safe(1); - POSIX::sigaction(SIGALRM, $sa) or die "Can't set SIGALRM handler: $!\n= "; - alarm 1; -} - -sub trace_end -{ - print_unhandled(); - print_totals(); -} - -sub print_check() -{ - if ($print_pending =3D=3D 1) { - $print_pending =3D 0; - print_totals(); - } -} - -sub set_print_pending() -{ - $print_pending =3D 1; - alarm $interval; -} - -sub print_totals -{ - my $count; - - $count =3D 0; - - clear_term(); - - printf("\nread counts by pid:\n\n"); - - printf("%6s %20s %10s %10s %10s\n", "pid", "comm", - "# reads", "bytes_req", "bytes_read"); - printf("%6s %-20s %10s %10s %10s\n", "------", "------------------= --", - "----------", "----------", "----------"); - - foreach my $pid (sort { ($reads{$b}{bytes_read} || 0) <=3D> - ($reads{$a}{bytes_read} || 0) } keys %reads) { - my $comm =3D $reads{$pid}{comm} || ""; - my $total_reads =3D $reads{$pid}{total_reads} || 0; - my $bytes_requested =3D $reads{$pid}{bytes_requested} || 0; - my $bytes_read =3D $reads{$pid}{bytes_read} || 0; - - printf("%6s %-20s %10s %10s %10s\n", $pid, $comm, - $total_reads, $bytes_requested, $bytes_read); - - if (++$count =3D=3D $nlines) { - last; - } - } - - $count =3D 0; - - printf("\nwrite counts by pid:\n\n"); - - printf("%6s %20s %10s %13s\n", "pid", "comm", - "# writes", "bytes_written"); - printf("%6s %-20s %10s %13s\n", "------", "--------------------", - "----------", "-------------"); - - foreach my $pid (sort { ($writes{$b}{bytes_written} || 0) <=3D> - ($writes{$a}{bytes_written} || 0)} keys %writes) { - my $comm =3D $writes{$pid}{comm} || ""; - my $total_writes =3D $writes{$pid}{total_writes} || 0; - my $bytes_written =3D $writes{$pid}{bytes_written} || 0; - - printf("%6s %-20s %10s %13s\n", $pid, $comm, - $total_writes, $bytes_written); - - if (++$count =3D=3D $nlines) { - last; - } - } - - %reads =3D (); - %writes =3D (); -} - -my %unhandled; - -sub print_unhandled -{ - if ((scalar keys %unhandled) =3D=3D 0) { - return; - } - - print "\nunhandled events:\n\n"; - - printf("%-40s %10s\n", "event", "count"); - printf("%-40s %10s\n", "----------------------------------------", - "-----------"); - - foreach my $event_name (keys %unhandled) { - printf("%-40s %10d\n", $event_name, $unhandled{$event_name}); - } -} - -sub trace_unhandled -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain) =3D @_; - - $unhandled{$event_name}++; -} diff --git a/tools/perf/scripts/perl/wakeup-latency.pl b/tools/perf/scripts= /perl/wakeup-latency.pl deleted file mode 100644 index 53444ff4ec7f..000000000000 --- a/tools/perf/scripts/perl/wakeup-latency.pl +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/perl -w -# SPDX-License-Identifier: GPL-2.0-only -# (c) 2009, Tom Zanussi - -# Display avg/min/max wakeup latency - -# The common_* event handler fields are the most useful fields common to -# all events. They don't necessarily correspond to the 'common_*' fields -# in the status files. Those fields not available as handler params can -# be retrieved via script functions of the form get_common_*(). - -use 5.010000; -use strict; -use warnings; - -use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; -use lib "./Perf-Trace-Util/lib"; -use Perf::Trace::Core; -use Perf::Trace::Util; - -my %last_wakeup; - -my $max_wakeup_latency; -my $min_wakeup_latency; -my $total_wakeup_latency =3D 0; -my $total_wakeups =3D 0; - -sub sched::sched_switch -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain, - $prev_comm, $prev_pid, $prev_prio, $prev_state, $next_comm, $next_pid, - $next_prio) =3D @_; - - my $wakeup_ts =3D $last_wakeup{$common_cpu}{ts}; - if ($wakeup_ts) { - my $switch_ts =3D nsecs($common_secs, $common_nsecs); - my $wakeup_latency =3D $switch_ts - $wakeup_ts; - if ($wakeup_latency > $max_wakeup_latency) { - $max_wakeup_latency =3D $wakeup_latency; - } - if ($wakeup_latency < $min_wakeup_latency) { - $min_wakeup_latency =3D $wakeup_latency; - } - $total_wakeup_latency +=3D $wakeup_latency; - $total_wakeups++; - } - $last_wakeup{$common_cpu}{ts} =3D 0; -} - -sub sched::sched_wakeup -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain, - $comm, $pid, $prio, $success, $target_cpu) =3D @_; - - $last_wakeup{$target_cpu}{ts} =3D nsecs($common_secs, $common_nsecs); -} - -sub trace_begin -{ - $min_wakeup_latency =3D 1000000000; - $max_wakeup_latency =3D 0; -} - -sub trace_end -{ - printf("wakeup_latency stats:\n\n"); - print "total_wakeups: $total_wakeups\n"; - if ($total_wakeups) { - printf("avg_wakeup_latency (ns): %u\n", - avg($total_wakeup_latency, $total_wakeups)); - } else { - printf("avg_wakeup_latency (ns): N/A\n"); - } - printf("min_wakeup_latency (ns): %u\n", $min_wakeup_latency); - printf("max_wakeup_latency (ns): %u\n", $max_wakeup_latency); - - print_unhandled(); -} - -my %unhandled; - -sub print_unhandled -{ - if ((scalar keys %unhandled) =3D=3D 0) { - return; - } - - print "\nunhandled events:\n\n"; - - printf("%-40s %10s\n", "event", "count"); - printf("%-40s %10s\n", "----------------------------------------", - "-----------"); - - foreach my $event_name (keys %unhandled) { - printf("%-40s %10d\n", $event_name, $unhandled{$event_name}); - } -} - -sub trace_unhandled -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, $common_callchain) =3D @_; - - $unhandled{$event_name}++; -} diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 6587dc326d1b..31b064928cfc 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -74,7 +74,7 @@ make_no_jevents :=3D NO_JEVENTS=3D1 make_jevents_all :=3D JEVENTS_ARCH=3Dall make_no_bpf_skel :=3D BUILD_BPF_SKEL=3D0 make_gen_vmlinux_h :=3D GEN_VMLINUX_H=3D1 -make_libperl :=3D LIBPERL=3D1 + make_no_libpython :=3D NO_LIBPYTHON=3D1 make_no_scripts :=3D NO_LIBPYTHON=3D1 make_no_slang :=3D NO_SLANG=3D1 @@ -149,7 +149,7 @@ run +=3D make_no_jevents run +=3D make_jevents_all run +=3D make_no_bpf_skel run +=3D make_gen_vmlinux_h -run +=3D make_libperl + run +=3D make_no_libpython run +=3D make_no_scripts run +=3D make_no_slang diff --git a/tools/perf/tests/shell/script_perl.sh b/tools/perf/tests/shell= /script_perl.sh deleted file mode 100755 index b6d65b6fbda1..000000000000 --- a/tools/perf/tests/shell/script_perl.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/bash -# perf script perl tests -# SPDX-License-Identifier: GPL-2.0 - -set -e - -# set PERF_EXEC_PATH to find scripts in the source directory -perfdir=3D$(dirname "$0")/../.. -if [ -e "$perfdir/scripts/perl/Perf-Trace-Util" ]; then - export PERF_EXEC_PATH=3D$perfdir -fi - - -perfdata=3D$(mktemp /tmp/__perf_test_script_perl.perf.data.XXXXX) -generated_script=3D$(mktemp /tmp/__perf_test_script.XXXXX.pl) - -cleanup() { - rm -f "${perfdata}" - rm -f "${generated_script}" - trap - EXIT TERM INT -} - -trap_cleanup() { - echo "Unexpected signal in ${FUNCNAME[1]}" - cleanup - exit 1 -} -trap trap_cleanup TERM INT -trap cleanup EXIT - -check_perl_support() { - if perf check feature -q libperl; then - return 0 - fi - echo "perf script perl test [Skipped: no libperl support]" - return 2 -} - -test_script() { - local event_name=3D$1 - local expected_output=3D$2 - local record_opts=3D$3 - - echo "Testing event: $event_name" - - # Try to record. If this fails, it might be permissions or lack of suppor= t. - # We return 2 to indicate "skip this event" rather than "fail test". - if ! perf record -o "${perfdata}" -e "$event_name" $record_opts -- perf t= est -w thloop > /dev/null 2>&1; then - echo "perf script perl test [Skipped: failed to record $event_name]" - return 2 - fi - - echo "Generating perl script..." - if ! perf script -i "${perfdata}" -g "${generated_script}"; then - echo "perf script perl test [Failed: script generation for $event_name]" - return 1 - fi - - if [ ! -f "${generated_script}" ]; then - echo "perf script perl test [Failed: script not generated for $event_nam= e]" - return 1 - fi - - echo "Executing perl script..." - output=3D$(perf script -i "${perfdata}" -s "${generated_script}" 2>&1) - - if echo "$output" | grep -q "$expected_output"; then - echo "perf script perl test [Success: $event_name triggered $expected_ou= tput]" - return 0 - else - echo "perf script perl test [Failed: $event_name did not trigger $expect= ed_output]" - echo "Output was:" - echo "$output" | head -n 20 - return 1 - fi -} - -check_perl_support || exit 2 - -# Try tracepoint first -test_script "sched:sched_switch" "sched::sched_switch" "-c 1" && res=3D0 |= | res=3D$? - -if [ $res -eq 0 ]; then - exit 0 -elif [ $res -eq 1 ]; then - exit 1 -fi - -# If tracepoint skipped (res=3D2), try task-clock -# For generic events like task-clock, the generated script uses process_ev= ent() -# which dumps data using Data::Dumper. We check for "$VAR1" which is stand= ard Dumper output. -test_script "task-clock" "\$VAR1" "-c 100" && res=3D0 || res=3D$? - -if [ $res -eq 0 ]; then - exit 0 -elif [ $res -eq 1 ]; then - exit 1 -fi - -# If both skipped -echo "perf script perl test [Skipped: Could not record tracepoint or task-= clock]" -exit 2 diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scri= pts.c index 1e8c2c2f952d..db5559311a1f 100644 --- a/tools/perf/ui/browsers/scripts.c +++ b/tools/perf/ui/browsers/scripts.c @@ -126,8 +126,10 @@ static int check_ev_match(int dir_fd, const char *scri= ptname, struct perf_sessio len =3D strcspn(p, " \t"); if (!len) break; + if ((size_t)len >=3D sizeof(evname)) + len =3D sizeof(evname) - 1; =20 - snprintf(evname, len + 1, "%s", p); + snprintf(evname, sizeof(evname), "%s", p); =20 match =3D 0; evlist__for_each_entry(session->evlist, pos) { @@ -200,14 +202,13 @@ static int find_scripts(char **scripts_array, char **= scripts_path_array, int num if (!strcmp(lang_dirent->d_name, ".") || !strcmp(lang_dirent->d_name, ".= .")) continue; =20 -#ifndef HAVE_LIBPERL_SUPPORT - if (strstr(lang_dirent->d_name, "perl")) - continue; -#endif + #ifndef HAVE_LIBPYTHON_SUPPORT if (strstr(lang_dirent->d_name, "python")) continue; #endif + if (strstr(lang_dirent->d_name, "perl")) + continue; =20 lang_dir_fd =3D openat(scripts_dir_fd, lang_dirent->d_name, O_DIRECTORY); if (lang_dir_fd =3D=3D -1) @@ -218,6 +219,8 @@ static int find_scripts(char **scripts_array, char **sc= ripts_path_array, int num continue; } while ((script_dirent =3D readdir(lang_dir)) !=3D NULL) { + int script_len; + if (script_dirent->d_type =3D=3D DT_DIR) continue; if (script_dirent->d_type =3D=3D DT_UNKNOWN && @@ -233,9 +236,11 @@ static int find_scripts(char **scripts_array, char **s= cripts_path_array, int num lang_dirent->d_name, script_dirent->d_name); temp =3D strchr(script_dirent->d_name, '.'); - snprintf(scripts_array[i], - (temp - script_dirent->d_name) + 1, - "%s", script_dirent->d_name); + script_len =3D temp ? (temp - script_dirent->d_name) : (int)strlen(scri= pt_dirent->d_name); + + if (script_len >=3D SCRIPT_NAMELEN) + script_len =3D SCRIPT_NAMELEN - 1; + snprintf(scripts_array[i], script_len + 1, "%s", script_dirent->d_name); =20 if (check_ev_match(lang_dir_fd, scripts_array[i], session)) continue; diff --git a/tools/perf/util/scripting-engines/Build b/tools/perf/util/scri= pting-engines/Build index 24f087b0cd11..ce14ef44b200 100644 --- a/tools/perf/util/scripting-engines/Build +++ b/tools/perf/util/scripting-engines/Build @@ -1,9 +1,7 @@ -ifeq ($(CONFIG_LIBTRACEEVENT),y) - perf-util-$(CONFIG_LIBPERL) +=3D trace-event-perl.o -endif + perf-util-$(CONFIG_LIBPYTHON) +=3D trace-event-python.o =20 -CFLAGS_trace-event-perl.o +=3D $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -= Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-nested-externs= -Wno-undef -Wno-switch-default -Wno-bad-function-cast -Wno-declaration-aft= er-statement -Wno-switch-enum -Wno-thread-safety-analysis + =20 # -Wno-declaration-after-statement: The python headers have mixed code wit= h declarations (decls after asserts, for instance) CFLAGS_trace-event-python.o +=3D $(PYTHON_EMBED_CCOPTS) -Wno-redundant-dec= ls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-deprecated= -declarations -Wno-switch-enum -Wno-declaration-after-statement diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/p= erf/util/scripting-engines/trace-event-perl.c deleted file mode 100644 index e261a57b87d4..000000000000 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ /dev/null @@ -1,773 +0,0 @@ -/* - * trace-event-perl. Feed perf script events to an embedded Perl interpre= ter. - * - * Copyright (C) 2009 Tom Zanussi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 = USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -/* perl needs the following define, right after including stdbool.h */ -#define HAS_BOOL -#include -#include - -#include "../callchain.h" -#include "../dso.h" -#include "../machine.h" -#include "../map.h" -#include "../symbol.h" -#include "../thread.h" -#include "../event.h" -#include "../trace-event.h" -#include "../evsel.h" -#include "../debug.h" - -void boot_Perf__Trace__Context(pTHX_ CV *cv); -void boot_DynaLoader(pTHX_ CV *cv); -typedef PerlInterpreter * INTERP; - -void xs_init(pTHX); - -void xs_init(pTHX) -{ - const char *file =3D __FILE__; - dXSUB_SYS; - - newXS("Perf::Trace::Context::bootstrap", boot_Perf__Trace__Context, - file); - newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); -} - -INTERP my_perl; - -#define TRACE_EVENT_TYPE_MAX \ - ((1 << (sizeof(unsigned short) * 8)) - 1) - -extern struct scripting_context *scripting_context; - -static char *cur_field_name; -static int zero_flag_atom; - -static void define_symbolic_value(const char *ev_name, - const char *field_name, - const char *field_value, - const char *field_str) -{ - unsigned long long value; - dSP; - - value =3D eval_flag(field_value); - - ENTER; - SAVETMPS; - PUSHMARK(SP); - - XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); - XPUSHs(sv_2mortal(newSVpv(field_name, 0))); - XPUSHs(sv_2mortal(newSVuv(value))); - XPUSHs(sv_2mortal(newSVpv(field_str, 0))); - - PUTBACK; - if (get_cv("main::define_symbolic_value", 0)) - call_pv("main::define_symbolic_value", G_SCALAR); - SPAGAIN; - PUTBACK; - FREETMPS; - LEAVE; -} - -static void define_symbolic_values(struct tep_print_flag_sym *field, - const char *ev_name, - const char *field_name) -{ - define_symbolic_value(ev_name, field_name, field->value, field->str); - if (field->next) - define_symbolic_values(field->next, ev_name, field_name); -} - -static void define_symbolic_field(const char *ev_name, - const char *field_name) -{ - dSP; - - ENTER; - SAVETMPS; - PUSHMARK(SP); - - XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); - XPUSHs(sv_2mortal(newSVpv(field_name, 0))); - - PUTBACK; - if (get_cv("main::define_symbolic_field", 0)) - call_pv("main::define_symbolic_field", G_SCALAR); - SPAGAIN; - PUTBACK; - FREETMPS; - LEAVE; -} - -static void define_flag_value(const char *ev_name, - const char *field_name, - const char *field_value, - const char *field_str) -{ - unsigned long long value; - dSP; - - value =3D eval_flag(field_value); - - ENTER; - SAVETMPS; - PUSHMARK(SP); - - XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); - XPUSHs(sv_2mortal(newSVpv(field_name, 0))); - XPUSHs(sv_2mortal(newSVuv(value))); - XPUSHs(sv_2mortal(newSVpv(field_str, 0))); - - PUTBACK; - if (get_cv("main::define_flag_value", 0)) - call_pv("main::define_flag_value", G_SCALAR); - SPAGAIN; - PUTBACK; - FREETMPS; - LEAVE; -} - -static void define_flag_values(struct tep_print_flag_sym *field, - const char *ev_name, - const char *field_name) -{ - define_flag_value(ev_name, field_name, field->value, field->str); - if (field->next) - define_flag_values(field->next, ev_name, field_name); -} - -static void define_flag_field(const char *ev_name, - const char *field_name, - const char *delim) -{ - dSP; - - ENTER; - SAVETMPS; - PUSHMARK(SP); - - XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); - XPUSHs(sv_2mortal(newSVpv(field_name, 0))); - XPUSHs(sv_2mortal(newSVpv(delim, 0))); - - PUTBACK; - if (get_cv("main::define_flag_field", 0)) - call_pv("main::define_flag_field", G_SCALAR); - SPAGAIN; - PUTBACK; - FREETMPS; - LEAVE; -} - -static void define_event_symbols(struct tep_event *event, - const char *ev_name, - struct tep_print_arg *args) -{ - if (args =3D=3D NULL) - return; - - switch (args->type) { - case TEP_PRINT_NULL: - break; - case TEP_PRINT_ATOM: - define_flag_value(ev_name, cur_field_name, "0", - args->atom.atom); - zero_flag_atom =3D 0; - break; - case TEP_PRINT_FIELD: - free(cur_field_name); - cur_field_name =3D strdup(args->field.name); - break; - case TEP_PRINT_FLAGS: - define_event_symbols(event, ev_name, args->flags.field); - define_flag_field(ev_name, cur_field_name, args->flags.delim); - define_flag_values(args->flags.flags, ev_name, cur_field_name); - break; - case TEP_PRINT_SYMBOL: - define_event_symbols(event, ev_name, args->symbol.field); - define_symbolic_field(ev_name, cur_field_name); - define_symbolic_values(args->symbol.symbols, ev_name, - cur_field_name); - break; - case TEP_PRINT_HEX: - case TEP_PRINT_HEX_STR: - define_event_symbols(event, ev_name, args->hex.field); - define_event_symbols(event, ev_name, args->hex.size); - break; - case TEP_PRINT_INT_ARRAY: - define_event_symbols(event, ev_name, args->int_array.field); - define_event_symbols(event, ev_name, args->int_array.count); - define_event_symbols(event, ev_name, args->int_array.el_size); - break; - case TEP_PRINT_BSTRING: - case TEP_PRINT_DYNAMIC_ARRAY: - case TEP_PRINT_DYNAMIC_ARRAY_LEN: - case TEP_PRINT_STRING: - case TEP_PRINT_BITMASK: - break; - case TEP_PRINT_TYPE: - define_event_symbols(event, ev_name, args->typecast.item); - break; - case TEP_PRINT_OP: - if (strcmp(args->op.op, ":") =3D=3D 0) - zero_flag_atom =3D 1; - define_event_symbols(event, ev_name, args->op.left); - define_event_symbols(event, ev_name, args->op.right); - break; - case TEP_PRINT_FUNC: - default: - pr_err("Unsupported print arg type\n"); - /* we should warn... */ - return; - } - - if (args->next) - define_event_symbols(event, ev_name, args->next); -} - -static SV *perl_process_callchain(struct perf_sample *sample, - struct evsel *evsel, - struct addr_location *al) -{ - struct callchain_cursor *cursor; - AV *list; - - list =3D newAV(); - if (!list) - goto exit; - - if (!symbol_conf.use_callchain || !sample->callchain) - goto exit; - - cursor =3D get_tls_callchain_cursor(); - - if (thread__resolve_callchain(al->thread, cursor, evsel, - sample, NULL, NULL, scripting_max_stack) !=3D 0) { - pr_err("Failed to resolve callchain. Skipping\n"); - goto exit; - } - callchain_cursor_commit(cursor); - - - while (1) { - HV *elem; - struct callchain_cursor_node *node; - node =3D callchain_cursor_current(cursor); - if (!node) - break; - - elem =3D newHV(); - if (!elem) - goto exit; - - if (!hv_stores(elem, "ip", newSVuv(node->ip))) { - hv_undef(elem); - goto exit; - } - - if (node->ms.sym) { - HV *sym =3D newHV(); - if (!sym) { - hv_undef(elem); - goto exit; - } - if (!hv_stores(sym, "start", newSVuv(node->ms.sym->start)) || - !hv_stores(sym, "end", newSVuv(node->ms.sym->end)) || - !hv_stores(sym, "binding", newSVuv(node->ms.sym->binding)) || - !hv_stores(sym, "name", newSVpvn(node->ms.sym->name, - node->ms.sym->namelen)) || - !hv_stores(elem, "sym", newRV_noinc((SV*)sym))) { - hv_undef(sym); - hv_undef(elem); - goto exit; - } - } - - if (node->ms.map) { - struct map *map =3D node->ms.map; - struct dso *dso =3D map ? map__dso(map) : NULL; - const char *dsoname =3D "[unknown]"; - - if (dso) { - if (symbol_conf.show_kernel_path && dso__long_name(dso)) - dsoname =3D dso__long_name(dso); - else - dsoname =3D dso__name(dso); - } - if (!hv_stores(elem, "dso", newSVpv(dsoname,0))) { - hv_undef(elem); - goto exit; - } - } - - callchain_cursor_advance(cursor); - av_push(list, newRV_noinc((SV*)elem)); - } - -exit: - return newRV_noinc((SV*)list); -} - -static void perl_process_tracepoint(struct perf_sample *sample, - struct evsel *evsel, - struct addr_location *al) -{ - struct thread *thread =3D al->thread; - struct tep_event *event; - struct tep_format_field *field; - static char handler[256]; - unsigned long long val; - unsigned long s, ns; - int pid; - int cpu =3D sample->cpu; - void *data =3D sample->raw_data; - unsigned long long nsecs =3D sample->time; - const char *comm =3D thread__comm_str(thread); - DECLARE_BITMAP(events_defined, TRACE_EVENT_TYPE_MAX); - - bitmap_zero(events_defined, TRACE_EVENT_TYPE_MAX); - dSP; - - if (evsel->core.attr.type !=3D PERF_TYPE_TRACEPOINT) - return; - - event =3D evsel__tp_format(evsel); - if (!event) { - pr_debug("ug! no event found for type %" PRIu64, (u64)evsel->core.attr.c= onfig); - return; - } - - pid =3D raw_field_value(event, "common_pid", data); - - sprintf(handler, "%s::%s", event->system, event->name); - - if (!__test_and_set_bit(event->id, events_defined)) - define_event_symbols(event, handler, event->print_fmt.args); - - s =3D nsecs / NSEC_PER_SEC; - ns =3D nsecs - s * NSEC_PER_SEC; - - ENTER; - SAVETMPS; - PUSHMARK(SP); - - XPUSHs(sv_2mortal(newSVpv(handler, 0))); - XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context)))); - XPUSHs(sv_2mortal(newSVuv(cpu))); - XPUSHs(sv_2mortal(newSVuv(s))); - XPUSHs(sv_2mortal(newSVuv(ns))); - XPUSHs(sv_2mortal(newSViv(pid))); - XPUSHs(sv_2mortal(newSVpv(comm, 0))); - XPUSHs(sv_2mortal(perl_process_callchain(sample, evsel, al))); - - /* common fields other than pid can be accessed via xsub fns */ - - for (field =3D event->format.fields; field; field =3D field->next) { - if (field->flags & TEP_FIELD_IS_STRING) { - int offset; - if (field->flags & TEP_FIELD_IS_DYNAMIC) { - offset =3D *(int *)(data + field->offset); - offset &=3D 0xffff; - if (tep_field_is_relative(field->flags)) - offset +=3D field->offset + field->size; - } else - offset =3D field->offset; - XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0))); - } else { /* FIELD_IS_NUMERIC */ - val =3D read_size(event, data + field->offset, - field->size); - if (field->flags & TEP_FIELD_IS_SIGNED) { - XPUSHs(sv_2mortal(newSViv(val))); - } else { - XPUSHs(sv_2mortal(newSVuv(val))); - } - } - } - - PUTBACK; - - if (get_cv(handler, 0)) - call_pv(handler, G_SCALAR); - else if (get_cv("main::trace_unhandled", 0)) { - XPUSHs(sv_2mortal(newSVpv(handler, 0))); - XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context)))); - XPUSHs(sv_2mortal(newSVuv(cpu))); - XPUSHs(sv_2mortal(newSVuv(nsecs))); - XPUSHs(sv_2mortal(newSViv(pid))); - XPUSHs(sv_2mortal(newSVpv(comm, 0))); - XPUSHs(sv_2mortal(perl_process_callchain(sample, evsel, al))); - call_pv("main::trace_unhandled", G_SCALAR); - } - SPAGAIN; - PUTBACK; - FREETMPS; - LEAVE; -} - -static void perl_process_event_generic(union perf_event *event, - struct perf_sample *sample, - struct evsel *evsel) -{ - dSP; - - if (!get_cv("process_event", 0)) - return; - - ENTER; - SAVETMPS; - PUSHMARK(SP); - XPUSHs(sv_2mortal(newSVpvn((const char *)event, event->header.size))); - XPUSHs(sv_2mortal(newSVpvn((const char *)&evsel->core.attr, sizeof(evsel-= >core.attr)))); - XPUSHs(sv_2mortal(newSVpvn((const char *)sample, sizeof(*sample)))); - XPUSHs(sv_2mortal(newSVpvn((const char *)sample->raw_data, sample->raw_si= ze))); - PUTBACK; - call_pv("process_event", G_SCALAR); - SPAGAIN; - PUTBACK; - FREETMPS; - LEAVE; -} - -static void perl_process_event(union perf_event *event, - struct perf_sample *sample, - struct evsel *evsel, - struct addr_location *al, - struct addr_location *addr_al) -{ - scripting_context__update(scripting_context, event, sample, evsel, al, ad= dr_al); - perl_process_tracepoint(sample, evsel, al); - perl_process_event_generic(event, sample, evsel); -} - -static void run_start_sub(void) -{ - dSP; /* access to Perl stack */ - PUSHMARK(SP); - - if (get_cv("main::trace_begin", 0)) - call_pv("main::trace_begin", G_DISCARD | G_NOARGS); -} - -/* - * Start trace script - */ -static int perl_start_script(const char *script, int argc, const char **ar= gv, - struct perf_session *session) -{ - const char **command_line; - int i, err =3D 0; - - scripting_context->session =3D session; - - command_line =3D malloc((argc + 2) * sizeof(const char *)); - if (!command_line) - return -ENOMEM; - - command_line[0] =3D ""; - command_line[1] =3D script; - for (i =3D 2; i < argc + 2; i++) - command_line[i] =3D argv[i - 2]; - - my_perl =3D perl_alloc(); - perl_construct(my_perl); - - if (perl_parse(my_perl, xs_init, argc + 2, (char **)command_line, - (char **)NULL)) { - err =3D -1; - goto error; - } - - if (perl_run(my_perl)) { - err =3D -1; - goto error; - } - - if (SvTRUE(ERRSV)) { - err =3D -1; - goto error; - } - - run_start_sub(); - - free(command_line); - return 0; -error: - perl_free(my_perl); - free(command_line); - - return err; -} - -static int perl_flush_script(void) -{ - return 0; -} - -/* - * Stop trace script - */ -static int perl_stop_script(void) -{ - dSP; /* access to Perl stack */ - PUSHMARK(SP); - - if (get_cv("main::trace_end", 0)) - call_pv("main::trace_end", G_DISCARD | G_NOARGS); - - perl_destruct(my_perl); - perl_free(my_perl); - - return 0; -} - -static int perl_generate_script(struct tep_handle *pevent, const char *out= file) -{ - int i, not_first, count, nr_events; - struct tep_event **all_events; - struct tep_event *event =3D NULL; - struct tep_format_field *f; - char fname[PATH_MAX]; - FILE *ofp; - - sprintf(fname, "%s.pl", outfile); - ofp =3D fopen(fname, "w"); - if (ofp =3D=3D NULL) { - fprintf(stderr, "couldn't open %s\n", fname); - return -1; - } - - fprintf(ofp, "# perf script event handlers, " - "generated by perf script -g perl\n"); - - fprintf(ofp, "# Licensed under the terms of the GNU GPL" - " License version 2\n\n"); - - fprintf(ofp, "# The common_* event handler fields are the most useful " - "fields common to\n"); - - fprintf(ofp, "# all events. They don't necessarily correspond to " - "the 'common_*' fields\n"); - - fprintf(ofp, "# in the format files. Those fields not available as " - "handler params can\n"); - - fprintf(ofp, "# be retrieved using Perl functions of the form " - "common_*($context).\n"); - - fprintf(ofp, "# See Context.pm for the list of available " - "functions.\n\n"); - - fprintf(ofp, "use lib \"$ENV{'PERF_EXEC_PATH'}/scripts/perl/" - "Perf-Trace-Util/lib\";\n"); - - fprintf(ofp, "use lib \"./Perf-Trace-Util/lib\";\n"); - fprintf(ofp, "use Perf::Trace::Core;\n"); - fprintf(ofp, "use Perf::Trace::Context;\n"); - fprintf(ofp, "use Perf::Trace::Util;\n\n"); - - fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n"); - fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n"); - - - fprintf(ofp, "\n\ -sub print_backtrace\n\ -{\n\ - my $callchain =3D shift;\n\ - for my $node (@$callchain)\n\ - {\n\ - if(exists $node->{sym})\n\ - {\n\ - printf( \"\\t[\\%%x] \\%%s\\n\", $node->{ip}, $node->{sym}{name});\n\ - }\n\ - else\n\ - {\n\ - printf( \"\\t[\\%%x]\\n\", $node{ip});\n\ - }\n\ - }\n\ -}\n\n\ -"); - - nr_events =3D tep_get_events_count(pevent); - all_events =3D tep_list_events(pevent, TEP_EVENT_SORT_ID); - - for (i =3D 0; all_events && i < nr_events; i++) { - event =3D all_events[i]; - fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name); - fprintf(ofp, "\tmy ("); - - fprintf(ofp, "$event_name, "); - fprintf(ofp, "$context, "); - fprintf(ofp, "$common_cpu, "); - fprintf(ofp, "$common_secs, "); - fprintf(ofp, "$common_nsecs,\n"); - fprintf(ofp, "\t $common_pid, "); - fprintf(ofp, "$common_comm, "); - fprintf(ofp, "$common_callchain,\n\t "); - - not_first =3D 0; - count =3D 0; - - for (f =3D event->format.fields; f; f =3D f->next) { - if (not_first++) - fprintf(ofp, ", "); - if (++count % 5 =3D=3D 0) - fprintf(ofp, "\n\t "); - - fprintf(ofp, "$%s", f->name); - } - fprintf(ofp, ") =3D @_;\n\n"); - - fprintf(ofp, "\tprint_header($event_name, $common_cpu, " - "$common_secs, $common_nsecs,\n\t " - "$common_pid, $common_comm, $common_callchain);\n\n"); - - fprintf(ofp, "\tprintf(\""); - - not_first =3D 0; - count =3D 0; - - for (f =3D event->format.fields; f; f =3D f->next) { - if (not_first++) - fprintf(ofp, ", "); - if (count && count % 4 =3D=3D 0) { - fprintf(ofp, "\".\n\t \""); - } - count++; - - fprintf(ofp, "%s=3D", f->name); - if (f->flags & TEP_FIELD_IS_STRING || - f->flags & TEP_FIELD_IS_FLAG || - f->flags & TEP_FIELD_IS_SYMBOLIC) - fprintf(ofp, "%%s"); - else if (f->flags & TEP_FIELD_IS_SIGNED) - fprintf(ofp, "%%d"); - else - fprintf(ofp, "%%u"); - } - - fprintf(ofp, "\\n\",\n\t "); - - not_first =3D 0; - count =3D 0; - - for (f =3D event->format.fields; f; f =3D f->next) { - if (not_first++) - fprintf(ofp, ", "); - - if (++count % 5 =3D=3D 0) - fprintf(ofp, "\n\t "); - - if (f->flags & TEP_FIELD_IS_FLAG) { - if ((count - 1) % 5 !=3D 0) { - fprintf(ofp, "\n\t "); - count =3D 4; - } - fprintf(ofp, "flag_str(\""); - fprintf(ofp, "%s::%s\", ", event->system, - event->name); - fprintf(ofp, "\"%s\", $%s)", f->name, - f->name); - } else if (f->flags & TEP_FIELD_IS_SYMBOLIC) { - if ((count - 1) % 5 !=3D 0) { - fprintf(ofp, "\n\t "); - count =3D 4; - } - fprintf(ofp, "symbol_str(\""); - fprintf(ofp, "%s::%s\", ", event->system, - event->name); - fprintf(ofp, "\"%s\", $%s)", f->name, - f->name); - } else - fprintf(ofp, "$%s", f->name); - } - - fprintf(ofp, ");\n\n"); - - fprintf(ofp, "\tprint_backtrace($common_callchain);\n"); - - fprintf(ofp, "}\n\n"); - } - - fprintf(ofp, "sub trace_unhandled\n{\n\tmy ($event_name, $context, " - "$common_cpu, $common_secs, $common_nsecs,\n\t " - "$common_pid, $common_comm, $common_callchain) =3D @_;\n\n"); - - fprintf(ofp, "\tprint_header($event_name, $common_cpu, " - "$common_secs, $common_nsecs,\n\t $common_pid, " - "$common_comm, $common_callchain);\n"); - fprintf(ofp, "\tprint_backtrace($common_callchain);\n"); - fprintf(ofp, "}\n\n"); - - fprintf(ofp, "sub print_header\n{\n" - "\tmy ($event_name, $cpu, $secs, $nsecs, $pid, $comm) =3D @_;\n\n" - "\tprintf(\"%%-20s %%5u %%05u.%%09u %%8u %%-20s \",\n\t " - "$event_name, $cpu, $secs, $nsecs, $pid, $comm);\n}\n"); - - fprintf(ofp, - "\n# Packed byte string args of process_event():\n" - "#\n" - "# $event:\tunion perf_event\tutil/event.h\n" - "# $attr:\tstruct perf_event_attr\tlinux/perf_event.h\n" - "# $sample:\tstruct perf_sample\tutil/event.h\n" - "# $raw_data:\tperf_sample->raw_data\tutil/event.h\n" - "\n" - "sub process_event\n" - "{\n" - "\tmy ($event, $attr, $sample, $raw_data) =3D @_;\n" - "\n" - "\tmy @event\t=3D unpack(\"LSS\", $event);\n" - "\tmy @attr\t=3D unpack(\"LLQQQQQLLQQ\", $attr);\n" - "\tmy @sample\t=3D unpack(\"QLLQQQQQLL\", $sample);\n" - "\tmy @raw_data\t=3D unpack(\"C*\", $raw_data);\n" - "\n" - "\tuse Data::Dumper;\n" - "\tprint Dumper \\@event, \\@attr, \\@sample, \\@raw_data;\n" - "}\n"); - - fclose(ofp); - - fprintf(stderr, "generated Perl script: %s\n", fname); - - return 0; -} - -struct scripting_ops perl_scripting_ops =3D { - .name =3D "Perl", - .dirname =3D "perl", - .start_script =3D perl_start_script, - .flush_script =3D perl_flush_script, - .stop_script =3D perl_stop_script, - .process_event =3D perl_process_event, - .generate_script =3D perl_generate_script, -}; diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trac= e-event-scripting.c index fa850e44cb46..a82472419611 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c @@ -206,72 +206,7 @@ void setup_python_scripting(void) } #endif =20 -#ifdef HAVE_LIBTRACEEVENT -static void print_perl_unsupported_msg(void) -{ - fprintf(stderr, "Perl scripting not supported." - " Install libperl and rebuild perf to enable it.\n" - "For example:\n # apt-get install libperl-dev (ubuntu)" - "\n # yum install 'perl(ExtUtils::Embed)' (Fedora)" - "\n etc.\n"); -} - -static int perl_start_script_unsupported(const char *script __maybe_unused, - int argc __maybe_unused, - const char **argv __maybe_unused, - struct perf_session *session __maybe_unused) -{ - print_perl_unsupported_msg(); - - return -1; -} - -static int perl_generate_script_unsupported(struct tep_handle *pevent - __maybe_unused, - const char *outfile __maybe_unused) -{ - print_perl_unsupported_msg(); - - return -1; -} - -struct scripting_ops perl_scripting_unsupported_ops =3D { - .name =3D "Perl", - .dirname =3D "perl", - .start_script =3D perl_start_script_unsupported, - .flush_script =3D flush_script_unsupported, - .stop_script =3D stop_script_unsupported, - .process_event =3D process_event_unsupported, - .generate_script =3D perl_generate_script_unsupported, -}; - -static void register_perl_scripting(struct scripting_ops *scripting_ops) -{ - if (scripting_context =3D=3D NULL) - scripting_context =3D malloc(sizeof(*scripting_context)); - - if (scripting_context =3D=3D NULL || - script_spec_register("Perl", scripting_ops) || - script_spec_register("pl", scripting_ops)) { - pr_err("Error registering Perl script extension: disabling it\n"); - zfree(&scripting_context); - } -} - -#ifndef HAVE_LIBPERL_SUPPORT -void setup_perl_scripting(void) -{ - register_perl_scripting(&perl_scripting_unsupported_ops); -} -#else -extern struct scripting_ops perl_scripting_ops; =20 -void setup_perl_scripting(void) -{ - register_perl_scripting(&perl_scripting_ops); -} -#endif -#endif =20 static const struct { u32 flags; diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 914d9b69ed62..7bdf44403e3a 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -116,7 +116,7 @@ extern unsigned int scripting_max_stack; struct scripting_ops *script_spec__lookup(const char *spec); int script_spec__for_each(int (*cb)(struct scripting_ops *ops, const char = *spec)); =20 -void setup_perl_scripting(void); + void setup_python_scripting(void); =20 struct scripting_context { --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 543D33B38AF for ; Sat, 25 Apr 2026 22:52:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157549; cv=none; b=UCawIUZ4zwOBGRh99jICQs+hIFTh83Jq3fIV2seoUk4/6y6mpgta/+n9nQwiFGCBC68ncXykcq4PFnTR8W+OfI9qt6byuzKivHv7ZrzlBm6sC7BG0L3+LiqTk9XapIWuTvlWKjYbw0eGTQKpPp+TeTob6I2vDeWqIp+sphkYQp4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157549; c=relaxed/simple; bh=suhKH/X6rHSnzfPbYVCym/ED9Hcx6beqB9unaRKDwmk=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=JBFUGyFb/dkdlnamGyxWBJuJDI2opcu+uwSuUbw5m0TXidjg7V31HvW6UYlMKyWM0rNIixIsS4eNZQHQ564JactEXyjdXInoKpjjYZML1zwiFl/fhR/UjNVkVdmn0eVcvTRC4yGXYDrR+Ujgdjw0Xf7D2qUVw6jlGk0mQqvqDQs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=F+X/9nDp; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="F+X/9nDp" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2bdf6fe90a9so13692277eec.1 for ; Sat, 25 Apr 2026 15:52:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157527; x=1777762327; darn=vger.kernel.org; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:from:to:cc:subject:date:message-id :reply-to; bh=9852UW3HJiKdSu5O/COuiyZzvq9ct+oPaCPAjtl8xn4=; b=F+X/9nDpQ4AAPQM7GQ5HbFxAc5wo5mSpeMKBEueYDPF+TF8eaoKz9aYw0HMg7iIOKx S/XRGaCm6IP2vO4gFxtfFqyoZd5Vn/95TA9usOb3FumEZk5NeBekjEpvDVKk+NJQbjU3 VwV6aRvODCznRVEdrsu+JPUvD3LSgqJZdlVgSYcecA0Ijy8pK6oeBWtCHN4dEAPuws1N /Kg2OkR8bYfr0qVApje2VSOE2+pemL9VYVlzH1kOyChWwC6yrvIq+eTzt37k+3+CVsND bXrIQGVd+WYBnZTo3AuKCi9g4ZNkOx2gfCLRUtc7aISksrKOj5wzd6OZIN9V2U6XyV0N ZRMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157527; x=1777762327; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to; bh=9852UW3HJiKdSu5O/COuiyZzvq9ct+oPaCPAjtl8xn4=; b=qVPZkjEFwhzgQrZig3vGL3Wt/eAHk/vA88UhQVYxKgJ5DjuOyT9741vQ10doBQRwbN EhUqqXNQp3RArH5BVemJ2cGX92GVar9CX6nLqk9WFQWd53PyixonBThKAIJ+v6q/qLDz rrf1G/T2vWpA2vppfgYvBeIhUTZmI61YkEUQoCZO9HWhKYgr9f9qe3l+Nd6v3rMDIVRE 6DhW9JafGUA0jUwLvg4/w9BGJXZU0vOqABA/9Fiff1s1RTj8WDfdMfOnPNPxGw1HAqic LDf1P+FX+pknjt0TdPgmFPUyLrmpy9ZWu7hnoXZpIoNCphhBnupJsk4jQsm5XOKNiwNr hB3A== X-Forwarded-Encrypted: i=1; AFNElJ8pdXmrpAMrTq5nH/FmU/s3Ia29uFTALz4yz0glU+nhaoKv6v4FoqlymYy10sZ1xq+UA53qaTFLlch/z9k=@vger.kernel.org X-Gm-Message-State: AOJu0YwlCrEKOe+OQvw9S78S941/fpyZN2FRsVyzlxWQHKkO+TPedEiU lLRhG/zxS8xZPiDk6lEY5FFZEXSOlLH8ibNbYw/Sfviyz4cGOWdilvNzK5mMB+VqBipipZ/5Pls HM9bonPyaog== X-Received: from dled19-n1.prod.google.com ([2002:a05:701b:42d3:10b0:12c:3dfa:518e]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:e10:b0:12c:4931:c2c1 with SMTP id a92af1059eb24-12c73f9988amr20114635c88.17.1777157526202; Sat, 25 Apr 2026 15:52:06 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:46 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-55-irogers@google.com> Subject: [PATCH v7 54/59] perf: Remove libpython support and legacy Python scripts From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This commit removes embedded Python interpreter support from perf, as all legacy Python scripts have been ported to standalone Python scripts or are no longer needed. Changes include: - Removal of libpython detection and flags from Makefile.config. - Removal of Python script installation rules from Makefile.perf. - Deletion of tools/perf/util/scripting-engines/trace-event-python.c. - Removal of Python scripting operations and setup from trace-event-scripting.c. - Removal of setup_python_scripting() call from builtin-script.c and declaration from trace-event.h. - Removal of Python checks in the script browser (scripts.c). - Removal of libpython from the supported features list in builtin-check.c and Documentation/perf-check.txt. - Deletion of the legacy Python scripts in tools/perf/scripts/python. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- tools/perf/Documentation/perf-check.txt | 1 - tools/perf/Makefile.config | 4 - tools/perf/Makefile.perf | 7 +- tools/perf/builtin-check.c | 1 - tools/perf/scripts/Build | 2 - .../perf/scripts/python/Perf-Trace-Util/Build | 4 - .../scripts/python/Perf-Trace-Util/Context.c | 225 -- .../Perf-Trace-Util/lib/Perf/Trace/Core.py | 116 - .../lib/Perf/Trace/EventClass.py | 97 - .../lib/Perf/Trace/SchedGui.py | 184 -- .../Perf-Trace-Util/lib/Perf/Trace/Util.py | 92 - .../scripts/python/arm-cs-trace-disasm.py | 355 --- .../python/bin/compaction-times-record | 2 - .../python/bin/compaction-times-report | 4 - .../python/bin/event_analyzing_sample-record | 8 - .../python/bin/event_analyzing_sample-report | 3 - .../python/bin/export-to-postgresql-record | 8 - .../python/bin/export-to-postgresql-report | 29 - .../python/bin/export-to-sqlite-record | 8 - .../python/bin/export-to-sqlite-report | 29 - .../python/bin/failed-syscalls-by-pid-record | 3 - .../python/bin/failed-syscalls-by-pid-report | 10 - .../perf/scripts/python/bin/flamegraph-record | 2 - .../perf/scripts/python/bin/flamegraph-report | 3 - .../python/bin/futex-contention-record | 2 - .../python/bin/futex-contention-report | 4 - tools/perf/scripts/python/bin/gecko-record | 2 - tools/perf/scripts/python/bin/gecko-report | 7 - .../scripts/python/bin/intel-pt-events-record | 13 - .../scripts/python/bin/intel-pt-events-report | 3 - .../scripts/python/bin/mem-phys-addr-record | 19 - .../scripts/python/bin/mem-phys-addr-report | 3 - .../scripts/python/bin/net_dropmonitor-record | 2 - .../scripts/python/bin/net_dropmonitor-report | 4 - .../scripts/python/bin/netdev-times-record | 8 - .../scripts/python/bin/netdev-times-report | 5 - .../scripts/python/bin/powerpc-hcalls-record | 2 - .../scripts/python/bin/powerpc-hcalls-report | 2 - .../scripts/python/bin/sched-migration-record | 2 - .../scripts/python/bin/sched-migration-report | 3 - tools/perf/scripts/python/bin/sctop-record | 3 - tools/perf/scripts/python/bin/sctop-report | 24 - .../scripts/python/bin/stackcollapse-record | 8 - .../scripts/python/bin/stackcollapse-report | 3 - .../python/bin/syscall-counts-by-pid-record | 3 - .../python/bin/syscall-counts-by-pid-report | 10 - .../scripts/python/bin/syscall-counts-record | 3 - .../scripts/python/bin/syscall-counts-report | 10 - .../scripts/python/bin/task-analyzer-record | 2 - .../scripts/python/bin/task-analyzer-report | 3 - tools/perf/scripts/python/check-perf-trace.py | 84 - tools/perf/scripts/python/compaction-times.py | 311 --- .../scripts/python/event_analyzing_sample.py | 192 -- .../scripts/python/export-to-postgresql.py | 1114 --------- tools/perf/scripts/python/export-to-sqlite.py | 799 ------ .../scripts/python/failed-syscalls-by-pid.py | 79 - tools/perf/scripts/python/flamegraph.py | 267 -- tools/perf/scripts/python/futex-contention.py | 57 - tools/perf/scripts/python/gecko.py | 395 --- tools/perf/scripts/python/intel-pt-events.py | 494 ---- tools/perf/scripts/python/libxed.py | 107 - tools/perf/scripts/python/mem-phys-addr.py | 127 - tools/perf/scripts/python/net_dropmonitor.py | 78 - tools/perf/scripts/python/netdev-times.py | 473 ---- tools/perf/scripts/python/powerpc-hcalls.py | 202 -- tools/perf/scripts/python/sched-migration.py | 462 ---- tools/perf/scripts/python/sctop.py | 89 - tools/perf/scripts/python/stackcollapse.py | 127 - tools/perf/scripts/python/stat-cpi.py | 79 - .../scripts/python/syscall-counts-by-pid.py | 75 - tools/perf/scripts/python/syscall-counts.py | 65 - tools/perf/scripts/python/task-analyzer.py | 934 ------- tools/perf/tests/shell/script_python.sh | 113 - tools/perf/util/scripting-engines/Build | 8 +- .../scripting-engines/trace-event-python.c | 2209 ----------------- tools/perf/util/trace-event-scripting.c | 14 +- 76 files changed, 4 insertions(+), 10297 deletions(-) delete mode 100644 tools/perf/scripts/python/Perf-Trace-Util/Build delete mode 100644 tools/perf/scripts/python/Perf-Trace-Util/Context.c delete mode 100644 tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trac= e/Core.py delete mode 100755 tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trac= e/EventClass.py delete mode 100644 tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trac= e/SchedGui.py delete mode 100644 tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trac= e/Util.py delete mode 100755 tools/perf/scripts/python/arm-cs-trace-disasm.py delete mode 100644 tools/perf/scripts/python/bin/compaction-times-record delete mode 100644 tools/perf/scripts/python/bin/compaction-times-report delete mode 100644 tools/perf/scripts/python/bin/event_analyzing_sample-re= cord delete mode 100644 tools/perf/scripts/python/bin/event_analyzing_sample-re= port delete mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-reco= rd delete mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-repo= rt delete mode 100644 tools/perf/scripts/python/bin/export-to-sqlite-record delete mode 100644 tools/perf/scripts/python/bin/export-to-sqlite-report delete mode 100644 tools/perf/scripts/python/bin/failed-syscalls-by-pid-re= cord delete mode 100644 tools/perf/scripts/python/bin/failed-syscalls-by-pid-re= port delete mode 100755 tools/perf/scripts/python/bin/flamegraph-record delete mode 100755 tools/perf/scripts/python/bin/flamegraph-report delete mode 100644 tools/perf/scripts/python/bin/futex-contention-record delete mode 100644 tools/perf/scripts/python/bin/futex-contention-report delete mode 100644 tools/perf/scripts/python/bin/gecko-record delete mode 100755 tools/perf/scripts/python/bin/gecko-report delete mode 100644 tools/perf/scripts/python/bin/intel-pt-events-record delete mode 100644 tools/perf/scripts/python/bin/intel-pt-events-report delete mode 100644 tools/perf/scripts/python/bin/mem-phys-addr-record delete mode 100644 tools/perf/scripts/python/bin/mem-phys-addr-report delete mode 100755 tools/perf/scripts/python/bin/net_dropmonitor-record delete mode 100755 tools/perf/scripts/python/bin/net_dropmonitor-report delete mode 100644 tools/perf/scripts/python/bin/netdev-times-record delete mode 100644 tools/perf/scripts/python/bin/netdev-times-report delete mode 100644 tools/perf/scripts/python/bin/powerpc-hcalls-record delete mode 100644 tools/perf/scripts/python/bin/powerpc-hcalls-report delete mode 100644 tools/perf/scripts/python/bin/sched-migration-record delete mode 100644 tools/perf/scripts/python/bin/sched-migration-report delete mode 100644 tools/perf/scripts/python/bin/sctop-record delete mode 100644 tools/perf/scripts/python/bin/sctop-report delete mode 100755 tools/perf/scripts/python/bin/stackcollapse-record delete mode 100755 tools/perf/scripts/python/bin/stackcollapse-report delete mode 100644 tools/perf/scripts/python/bin/syscall-counts-by-pid-rec= ord delete mode 100644 tools/perf/scripts/python/bin/syscall-counts-by-pid-rep= ort delete mode 100644 tools/perf/scripts/python/bin/syscall-counts-record delete mode 100644 tools/perf/scripts/python/bin/syscall-counts-report delete mode 100755 tools/perf/scripts/python/bin/task-analyzer-record delete mode 100755 tools/perf/scripts/python/bin/task-analyzer-report delete mode 100644 tools/perf/scripts/python/check-perf-trace.py delete mode 100644 tools/perf/scripts/python/compaction-times.py delete mode 100644 tools/perf/scripts/python/event_analyzing_sample.py delete mode 100644 tools/perf/scripts/python/export-to-postgresql.py delete mode 100644 tools/perf/scripts/python/export-to-sqlite.py delete mode 100644 tools/perf/scripts/python/failed-syscalls-by-pid.py delete mode 100755 tools/perf/scripts/python/flamegraph.py delete mode 100644 tools/perf/scripts/python/futex-contention.py delete mode 100644 tools/perf/scripts/python/gecko.py delete mode 100644 tools/perf/scripts/python/intel-pt-events.py delete mode 100644 tools/perf/scripts/python/libxed.py delete mode 100644 tools/perf/scripts/python/mem-phys-addr.py delete mode 100755 tools/perf/scripts/python/net_dropmonitor.py delete mode 100644 tools/perf/scripts/python/netdev-times.py delete mode 100644 tools/perf/scripts/python/powerpc-hcalls.py delete mode 100644 tools/perf/scripts/python/sched-migration.py delete mode 100644 tools/perf/scripts/python/sctop.py delete mode 100755 tools/perf/scripts/python/stackcollapse.py delete mode 100644 tools/perf/scripts/python/stat-cpi.py delete mode 100644 tools/perf/scripts/python/syscall-counts-by-pid.py delete mode 100644 tools/perf/scripts/python/syscall-counts.py delete mode 100755 tools/perf/scripts/python/task-analyzer.py delete mode 100755 tools/perf/tests/shell/script_python.sh delete mode 100644 tools/perf/util/scripting-engines/trace-event-python.c diff --git a/tools/perf/Documentation/perf-check.txt b/tools/perf/Documenta= tion/perf-check.txt index 60fa9ea43a58..32d6200e7b20 100644 --- a/tools/perf/Documentation/perf-check.txt +++ b/tools/perf/Documentation/perf-check.txt @@ -59,7 +59,6 @@ feature:: libnuma / HAVE_LIBNUMA_SUPPORT libopencsd / HAVE_CSTRACE_SUPPORT libpfm4 / HAVE_LIBPFM - libpython / HAVE_LIBPYTHON_SUPPORT libslang / HAVE_SLANG_SUPPORT libtraceevent / HAVE_LIBTRACEEVENT libunwind / HAVE_LIBUNWIND_SUPPORT diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index db30e73c5efc..ecddd91229c8 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -852,8 +852,6 @@ else ifneq ($(feature-libpython), 1) $(call disable-python,No 'Python.h' was found: disables Python sup= port - please install python-devel/python-dev) else - LDFLAGS +=3D $(PYTHON_EMBED_LDFLAGS) - EXTLIBS +=3D $(PYTHON_EMBED_LIBADD) PYTHON_SETUPTOOLS_INSTALLED :=3D $(shell $(PYTHON) -c 'import set= uptools;' 2> /dev/null && echo "yes" || echo "no") ifeq ($(PYTHON_SETUPTOOLS_INSTALLED), yes) PYTHON_EXTENSION_SUFFIX :=3D $(shell $(PYTHON) -c 'from importl= ib import machinery; print(machinery.EXTENSION_SUFFIXES[0])') @@ -864,8 +862,6 @@ else else $(warning Missing python setuptools, the python binding won't b= e built, please install python3-setuptools or equivalent) endif - CFLAGS +=3D -DHAVE_LIBPYTHON_SUPPORT - $(call detected,CONFIG_LIBPYTHON) ifeq ($(filter -fPIC,$(CFLAGS)),) # Building a shared library requires position independent code. CFLAGS +=3D -fPIC diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 7bf349198622..2020532bab9c 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -1101,11 +1101,8 @@ endif =20 ifndef NO_LIBPYTHON $(call QUIET_INSTALL, python-scripts) \ - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python= /Perf-Trace-Util/lib/Perf/Trace'; \ - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python= /bin'; \ - $(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -m 644 -t '$(= DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/T= race'; \ - $(INSTALL) scripts/python/*.py -m 644 -t '$(DESTDIR_SQ)$(perfexec_instdi= r_SQ)/scripts/python'; \ - $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/= scripts/python/bin' + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python= '; \ + $(INSTALL) python/*.py -m 644 -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/sc= ripts/python' endif $(call QUIET_INSTALL, dlfilters) \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/dlfilters'; \ diff --git a/tools/perf/builtin-check.c b/tools/perf/builtin-check.c index 944038814d62..73391d182039 100644 --- a/tools/perf/builtin-check.c +++ b/tools/perf/builtin-check.c @@ -53,7 +53,6 @@ struct feature_status supported_features[] =3D { FEATURE_STATUS("libopencsd", HAVE_CSTRACE_SUPPORT), =20 FEATURE_STATUS("libpfm4", HAVE_LIBPFM), - FEATURE_STATUS("libpython", HAVE_LIBPYTHON_SUPPORT), FEATURE_STATUS("libslang", HAVE_SLANG_SUPPORT), FEATURE_STATUS("libtraceevent", HAVE_LIBTRACEEVENT), FEATURE_STATUS_TIP("libunwind", HAVE_LIBUNWIND_SUPPORT, "Deprecated, use = LIBUNWIND=3D1 and install libunwind-dev[el] to build with it"), diff --git a/tools/perf/scripts/Build b/tools/perf/scripts/Build index d72cf9ad45fe..d066864369ed 100644 --- a/tools/perf/scripts/Build +++ b/tools/perf/scripts/Build @@ -1,6 +1,4 @@ =20 -perf-util-$(CONFIG_LIBPYTHON) +=3D python/Perf-Trace-Util/ - ifdef MYPY PY_TESTS :=3D $(shell find python -type f -name '*.py') MYPY_TEST_LOGS :=3D $(PY_TESTS:python/%=3Dpython/%.mypy_log) diff --git a/tools/perf/scripts/python/Perf-Trace-Util/Build b/tools/perf/s= cripts/python/Perf-Trace-Util/Build deleted file mode 100644 index be3710c61320..000000000000 --- a/tools/perf/scripts/python/Perf-Trace-Util/Build +++ /dev/null @@ -1,4 +0,0 @@ -perf-util-y +=3D Context.o - -# -Wno-declaration-after-statement: The python headers have mixed code wit= h declarations (decls after asserts, for instance) -CFLAGS_Context.o +=3D $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-str= ict-prototypes -Wno-unused-parameter -Wno-nested-externs -Wno-declaration-a= fter-statement diff --git a/tools/perf/scripts/python/Perf-Trace-Util/Context.c b/tools/pe= rf/scripts/python/Perf-Trace-Util/Context.c deleted file mode 100644 index c19f44610983..000000000000 --- a/tools/perf/scripts/python/Perf-Trace-Util/Context.c +++ /dev/null @@ -1,225 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Context.c. Python interfaces for perf script. - * - * Copyright (C) 2010 Tom Zanussi - */ - -/* - * Use Py_ssize_t for '#' formats to avoid DeprecationWarning: PY_SSIZE_T_= CLEAN - * will be required for '#' formats. - */ -#define PY_SSIZE_T_CLEAN - -#include -#include "../../../util/config.h" -#include "../../../util/trace-event.h" -#include "../../../util/event.h" -#include "../../../util/symbol.h" -#include "../../../util/thread.h" -#include "../../../util/map.h" -#include "../../../util/maps.h" -#include "../../../util/auxtrace.h" -#include "../../../util/session.h" -#include "../../../util/srcline.h" -#include "../../../util/srccode.h" - -#define _PyCapsule_GetPointer(arg1, arg2) \ - PyCapsule_GetPointer((arg1), (arg2)) -#define _PyBytes_FromStringAndSize(arg1, arg2) \ - PyBytes_FromStringAndSize((arg1), (arg2)) -#define _PyUnicode_AsUTF8(arg) \ - PyUnicode_AsUTF8(arg) - -PyMODINIT_FUNC PyInit_perf_trace_context(void); - -static struct scripting_context *get_args(PyObject *args, const char *name= , PyObject **arg2) -{ - int cnt =3D 1 + !!arg2; - PyObject *context; - - if (!PyArg_UnpackTuple(args, name, 1, cnt, &context, arg2)) - return NULL; - - return _PyCapsule_GetPointer(context, NULL); -} - -static struct scripting_context *get_scripting_context(PyObject *args) -{ - return get_args(args, "context", NULL); -} - -#ifdef HAVE_LIBTRACEEVENT -static PyObject *perf_trace_context_common_pc(PyObject *obj, PyObject *arg= s) -{ - struct scripting_context *c =3D get_scripting_context(args); - - if (!c) - return NULL; - - return Py_BuildValue("i", common_pc(c)); -} - -static PyObject *perf_trace_context_common_flags(PyObject *obj, - PyObject *args) -{ - struct scripting_context *c =3D get_scripting_context(args); - - if (!c) - return NULL; - - return Py_BuildValue("i", common_flags(c)); -} - -static PyObject *perf_trace_context_common_lock_depth(PyObject *obj, - PyObject *args) -{ - struct scripting_context *c =3D get_scripting_context(args); - - if (!c) - return NULL; - - return Py_BuildValue("i", common_lock_depth(c)); -} -#endif - -static PyObject *perf_sample_insn(PyObject *obj, PyObject *args) -{ - struct scripting_context *c =3D get_scripting_context(args); - - if (!c) - return NULL; - - if (c->sample->ip && !c->sample->insn_len && thread__maps(c->al->thread))= { - struct machine *machine =3D maps__machine(thread__maps(c->al->thread)); - - perf_sample__fetch_insn(c->sample, c->al->thread, machine); - } - if (!c->sample->insn_len) - Py_RETURN_NONE; /* N.B. This is a return statement */ - - return _PyBytes_FromStringAndSize(c->sample->insn, c->sample->insn_len); -} - -static PyObject *perf_set_itrace_options(PyObject *obj, PyObject *args) -{ - struct scripting_context *c; - const char *itrace_options; - int retval =3D -1; - PyObject *str; - - c =3D get_args(args, "itrace_options", &str); - if (!c) - return NULL; - - if (!c->session || !c->session->itrace_synth_opts) - goto out; - - if (c->session->itrace_synth_opts->set) { - retval =3D 1; - goto out; - } - - itrace_options =3D _PyUnicode_AsUTF8(str); - - retval =3D itrace_do_parse_synth_opts(c->session->itrace_synth_opts, itra= ce_options, 0); -out: - return Py_BuildValue("i", retval); -} - -static PyObject *perf_sample_src(PyObject *obj, PyObject *args, bool get_s= rccode) -{ - struct scripting_context *c =3D get_scripting_context(args); - unsigned int line =3D 0; - char *srcfile =3D NULL; - char *srccode =3D NULL; - PyObject *result; - struct map *map; - struct dso *dso; - int len =3D 0; - u64 addr; - - if (!c) - return NULL; - - map =3D c->al->map; - addr =3D c->al->addr; - dso =3D map ? map__dso(map) : NULL; - - if (dso) - srcfile =3D get_srcline_split(dso, map__rip_2objdump(map, addr), &line); - - if (get_srccode) { - if (srcfile) - srccode =3D find_sourceline(srcfile, line, &len); - result =3D Py_BuildValue("(sIs#)", srcfile, line, srccode, (Py_ssize_t)l= en); - } else { - result =3D Py_BuildValue("(sI)", srcfile, line); - } - - free(srcfile); - - return result; -} - -static PyObject *perf_sample_srcline(PyObject *obj, PyObject *args) -{ - return perf_sample_src(obj, args, false); -} - -static PyObject *perf_sample_srccode(PyObject *obj, PyObject *args) -{ - return perf_sample_src(obj, args, true); -} - -static PyObject *__perf_config_get(PyObject *obj, PyObject *args) -{ - const char *config_name; - - if (!PyArg_ParseTuple(args, "s", &config_name)) - return NULL; - return Py_BuildValue("s", perf_config_get(config_name)); -} - -static PyMethodDef ContextMethods[] =3D { -#ifdef HAVE_LIBTRACEEVENT - { "common_pc", perf_trace_context_common_pc, METH_VARARGS, - "Get the common preempt count event field value."}, - { "common_flags", perf_trace_context_common_flags, METH_VARARGS, - "Get the common flags event field value."}, - { "common_lock_depth", perf_trace_context_common_lock_depth, - METH_VARARGS, "Get the common lock depth event field value."}, -#endif - { "perf_sample_insn", perf_sample_insn, - METH_VARARGS, "Get the machine code instruction."}, - { "perf_set_itrace_options", perf_set_itrace_options, - METH_VARARGS, "Set --itrace options."}, - { "perf_sample_srcline", perf_sample_srcline, - METH_VARARGS, "Get source file name and line number."}, - { "perf_sample_srccode", perf_sample_srccode, - METH_VARARGS, "Get source file name, line number and line."}, - { "perf_config_get", __perf_config_get, METH_VARARGS, "Get perf config en= try"}, - { NULL, NULL, 0, NULL} -}; - -PyMODINIT_FUNC PyInit_perf_trace_context(void) -{ - static struct PyModuleDef moduledef =3D { - PyModuleDef_HEAD_INIT, - "perf_trace_context", /* m_name */ - "", /* m_doc */ - -1, /* m_size */ - ContextMethods, /* m_methods */ - NULL, /* m_reload */ - NULL, /* m_traverse */ - NULL, /* m_clear */ - NULL, /* m_free */ - }; - PyObject *mod; - - mod =3D PyModule_Create(&moduledef); - /* Add perf_script_context to the module so it can be imported */ - PyObject_SetAttrString(mod, "perf_script_context", Py_None); - - return mod; -} diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.= py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py deleted file mode 100644 index 54ace2f6bc36..000000000000 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py +++ /dev/null @@ -1,116 +0,0 @@ -# Core.py - Python extension for perf script, core functions -# -# Copyright (C) 2010 by Tom Zanussi -# -# This software may be distributed under the terms of the GNU General -# Public License ("GPL") version 2 as published by the Free Software -# Foundation. - -from collections import defaultdict - -def autodict(): - return defaultdict(autodict) - -flag_fields =3D autodict() -symbolic_fields =3D autodict() - -def define_flag_field(event_name, field_name, delim): - flag_fields[event_name][field_name]['delim'] =3D delim - -def define_flag_value(event_name, field_name, value, field_str): - flag_fields[event_name][field_name]['values'][value] =3D field_str - -def define_symbolic_field(event_name, field_name): - # nothing to do, really - pass - -def define_symbolic_value(event_name, field_name, value, field_str): - symbolic_fields[event_name][field_name]['values'][value] =3D field_str - -def flag_str(event_name, field_name, value): - string =3D "" - - if flag_fields[event_name][field_name]: - print_delim =3D 0 - for idx in sorted(flag_fields[event_name][field_name]['values']): - if not value and not idx: - string +=3D flag_fields[event_name][field_name]['values'][= idx] - break - if idx and (value & idx) =3D=3D idx: - if print_delim and flag_fields[event_name][field_name]['de= lim']: - string +=3D " " + flag_fields[event_name][field_name][= 'delim'] + " " - string +=3D flag_fields[event_name][field_name]['values'][= idx] - print_delim =3D 1 - value &=3D ~idx - - return string - -def symbol_str(event_name, field_name, value): - string =3D "" - - if symbolic_fields[event_name][field_name]: - for idx in sorted(symbolic_fields[event_name][field_name]['values'= ]): - if not value and not idx: - string =3D symbolic_fields[event_name][field_name]['values= '][idx] - break - if (value =3D=3D idx): - string =3D symbolic_fields[event_name][field_name]['values= '][idx] - break - - return string - -trace_flags =3D { 0x00: "NONE", \ - 0x01: "IRQS_OFF", \ - 0x02: "IRQS_NOSUPPORT", \ - 0x04: "NEED_RESCHED", \ - 0x08: "HARDIRQ", \ - 0x10: "SOFTIRQ" } - -def trace_flag_str(value): - string =3D "" - print_delim =3D 0 - - for idx in trace_flags: - if not value and not idx: - string +=3D "NONE" - break - - if idx and (value & idx) =3D=3D idx: - if print_delim: - string +=3D " | "; - string +=3D trace_flags[idx] - print_delim =3D 1 - value &=3D ~idx - - return string - - -def taskState(state): - states =3D { - 0 : "R", - 1 : "S", - 2 : "D", - 64: "DEAD" - } - - if state not in states: - return "Unknown" - - return states[state] - - -class EventHeaders: - def __init__(self, common_cpu, common_secs, common_nsecs, - common_pid, common_comm, common_callchain): - self.cpu =3D common_cpu - self.secs =3D common_secs - self.nsecs =3D common_nsecs - self.pid =3D common_pid - self.comm =3D common_comm - self.callchain =3D common_callchain - - def ts(self): - return (self.secs * (10 ** 9)) + self.nsecs - - def ts_format(self): - return "%d.%d" % (self.secs, int(self.nsecs / 1000)) diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Event= Class.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventCl= ass.py deleted file mode 100755 index 21a7a1298094..000000000000 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventClass.py +++ /dev/null @@ -1,97 +0,0 @@ -# EventClass.py -# SPDX-License-Identifier: GPL-2.0 -# -# This is a library defining some events types classes, which could -# be used by other scripts to analyzing the perf samples. -# -# Currently there are just a few classes defined for examples, -# PerfEvent is the base class for all perf event sample, PebsEvent -# is a HW base Intel x86 PEBS event, and user could add more SW/HW -# event classes based on requirements. -from __future__ import print_function - -import struct - -# Event types, user could add more here -EVTYPE_GENERIC =3D 0 -EVTYPE_PEBS =3D 1 # Basic PEBS event -EVTYPE_PEBS_LL =3D 2 # PEBS event with load latency info -EVTYPE_IBS =3D 3 - -# -# Currently we don't have good way to tell the event type, but by -# the size of raw buffer, raw PEBS event with load latency data's -# size is 176 bytes, while the pure PEBS event's size is 144 bytes. -# -def create_event(name, comm, dso, symbol, raw_buf): - if (len(raw_buf) =3D=3D 144): - event =3D PebsEvent(name, comm, dso, symbol, raw_buf) - elif (len(raw_buf) =3D=3D 176): - event =3D PebsNHM(name, comm, dso, symbol, raw_buf) - else: - event =3D PerfEvent(name, comm, dso, symbol, raw_buf) - - return event - -class PerfEvent(object): - event_num =3D 0 - def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=3DEVT= YPE_GENERIC): - self.name =3D name - self.comm =3D comm - self.dso =3D dso - self.symbol =3D symbol - self.raw_buf =3D raw_buf - self.ev_type =3D ev_type - PerfEvent.event_num +=3D 1 - - def show(self): - print("PMU event: name=3D%12s, symbol=3D%24s, comm=3D%8s, = dso=3D%12s" % - (self.name, self.symbol, self.comm, self.dso)) - -# -# Basic Intel PEBS (Precise Event-based Sampling) event, whose raw buffer -# contains the context info when that event happened: the EFLAGS and -# linear IP info, as well as all the registers. -# -class PebsEvent(PerfEvent): - pebs_num =3D 0 - def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=3DEVT= YPE_PEBS): - tmp_buf=3Draw_buf[0:80] - flags, ip, ax, bx, cx, dx, si, di, bp, sp =3D struct.unpac= k('QQQQQQQQQQ', tmp_buf) - self.flags =3D flags - self.ip =3D ip - self.ax =3D ax - self.bx =3D bx - self.cx =3D cx - self.dx =3D dx - self.si =3D si - self.di =3D di - self.bp =3D bp - self.sp =3D sp - - PerfEvent.__init__(self, name, comm, dso, symbol, raw_buf,= ev_type) - PebsEvent.pebs_num +=3D 1 - del tmp_buf - -# -# Intel Nehalem and Westmere support PEBS plus Load Latency info which lie -# in the four 64 bit words write after the PEBS data: -# Status: records the IA32_PERF_GLOBAL_STATUS register value -# DLA: Data Linear Address (EIP) -# DSE: Data Source Encoding, where the latency happens, hit or mi= ss -# in L1/L2/L3 or IO operations -# LAT: the actual latency in cycles -# -class PebsNHM(PebsEvent): - pebs_nhm_num =3D 0 - def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=3DEVT= YPE_PEBS_LL): - tmp_buf=3Draw_buf[144:176] - status, dla, dse, lat =3D struct.unpack('QQQQ', tmp_buf) - self.status =3D status - self.dla =3D dla - self.dse =3D dse - self.lat =3D lat - - PebsEvent.__init__(self, name, comm, dso, symbol, raw_buf,= ev_type) - PebsNHM.pebs_nhm_num +=3D 1 - del tmp_buf diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Sched= Gui.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.= py deleted file mode 100644 index cac7b2542ee8..000000000000 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py +++ /dev/null @@ -1,184 +0,0 @@ -# SchedGui.py - Python extension for perf script, basic GUI code for -# traces drawing and overview. -# -# Copyright (C) 2010 by Frederic Weisbecker -# -# This software is distributed under the terms of the GNU General -# Public License ("GPL") version 2 as published by the Free Software -# Foundation. - - -try: - import wx -except ImportError: - raise ImportError("You need to install the wxpython lib for this script") - - -class RootFrame(wx.Frame): - Y_OFFSET =3D 100 - RECT_HEIGHT =3D 100 - RECT_SPACE =3D 50 - EVENT_MARKING_WIDTH =3D 5 - - def __init__(self, sched_tracer, title, parent =3D None, id =3D -1): - wx.Frame.__init__(self, parent, id, title) - - (self.screen_width, self.screen_height) =3D wx.GetDisplaySize() - self.screen_width -=3D 10 - self.screen_height -=3D 10 - self.zoom =3D 0.5 - self.scroll_scale =3D 20 - self.sched_tracer =3D sched_tracer - self.sched_tracer.set_root_win(self) - (self.ts_start, self.ts_end) =3D sched_tracer.interval() - self.update_width_virtual() - self.nr_rects =3D sched_tracer.nr_rectangles() + 1 - self.height_virtual =3D RootFrame.Y_OFFSET + (self.nr_rects * (RootFrame= .RECT_HEIGHT + RootFrame.RECT_SPACE)) - - # whole window panel - self.panel =3D wx.Panel(self, size=3D(self.screen_width, self.screen_hei= ght)) - - # scrollable container - self.scroll =3D wx.ScrolledWindow(self.panel) - self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.wid= th_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale) - self.scroll.EnableScrolling(True, True) - self.scroll.SetFocus() - - # scrollable drawing area - self.scroll_panel =3D wx.Panel(self.scroll, size=3D(self.screen_width - = 15, self.screen_height / 2)) - self.scroll_panel.Bind(wx.EVT_PAINT, self.on_paint) - self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press) - self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) - self.scroll.Bind(wx.EVT_PAINT, self.on_paint) - self.scroll.Bind(wx.EVT_KEY_DOWN, self.on_key_press) - self.scroll.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) - - self.scroll.Fit() - self.Fit() - - self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, self.height_= virtual, wx.SIZE_USE_EXISTING) - - self.txt =3D None - - self.Show(True) - - def us_to_px(self, val): - return val / (10 ** 3) * self.zoom - - def px_to_us(self, val): - return (val / self.zoom) * (10 ** 3) - - def scroll_start(self): - (x, y) =3D self.scroll.GetViewStart() - return (x * self.scroll_scale, y * self.scroll_scale) - - def scroll_start_us(self): - (x, y) =3D self.scroll_start() - return self.px_to_us(x) - - def paint_rectangle_zone(self, nr, color, top_color, start, end): - offset_px =3D self.us_to_px(start - self.ts_start) - width_px =3D self.us_to_px(end - self.ts_start) - - offset_py =3D RootFrame.Y_OFFSET + (nr * (RootFrame.RECT_HEIGHT + RootFr= ame.RECT_SPACE)) - width_py =3D RootFrame.RECT_HEIGHT - - dc =3D self.dc - - if top_color is not None: - (r, g, b) =3D top_color - top_color =3D wx.Colour(r, g, b) - brush =3D wx.Brush(top_color, wx.SOLID) - dc.SetBrush(brush) - dc.DrawRectangle(offset_px, offset_py, width_px, RootFrame.EVENT_MARKIN= G_WIDTH) - width_py -=3D RootFrame.EVENT_MARKING_WIDTH - offset_py +=3D RootFrame.EVENT_MARKING_WIDTH - - (r ,g, b) =3D color - color =3D wx.Colour(r, g, b) - brush =3D wx.Brush(color, wx.SOLID) - dc.SetBrush(brush) - dc.DrawRectangle(offset_px, offset_py, width_px, width_py) - - def update_rectangles(self, dc, start, end): - start +=3D self.ts_start - end +=3D self.ts_start - self.sched_tracer.fill_zone(start, end) - - def on_paint(self, event): - dc =3D wx.PaintDC(self.scroll_panel) - self.dc =3D dc - - width =3D min(self.width_virtual, self.screen_width) - (x, y) =3D self.scroll_start() - start =3D self.px_to_us(x) - end =3D self.px_to_us(x + width) - self.update_rectangles(dc, start, end) - - def rect_from_ypixel(self, y): - y -=3D RootFrame.Y_OFFSET - rect =3D y / (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) - height =3D y % (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) - - if rect < 0 or rect > self.nr_rects - 1 or height > RootFrame.RECT_HEIGH= T: - return -1 - - return rect - - def update_summary(self, txt): - if self.txt: - self.txt.Destroy() - self.txt =3D wx.StaticText(self.panel, -1, txt, (0, (self.screen_height = / 2) + 50)) - - - def on_mouse_down(self, event): - (x, y) =3D event.GetPositionTuple() - rect =3D self.rect_from_ypixel(y) - if rect =3D=3D -1: - return - - t =3D self.px_to_us(x) + self.ts_start - - self.sched_tracer.mouse_down(rect, t) - - - def update_width_virtual(self): - self.width_virtual =3D self.us_to_px(self.ts_end - self.ts_start) - - def __zoom(self, x): - self.update_width_virtual() - (xpos, ypos) =3D self.scroll.GetViewStart() - xpos =3D self.us_to_px(x) / self.scroll_scale - self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.wid= th_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale, xp= os, ypos) - self.Refresh() - - def zoom_in(self): - x =3D self.scroll_start_us() - self.zoom *=3D 2 - self.__zoom(x) - - def zoom_out(self): - x =3D self.scroll_start_us() - self.zoom /=3D 2 - self.__zoom(x) - - - def on_key_press(self, event): - key =3D event.GetRawKeyCode() - if key =3D=3D ord("+"): - self.zoom_in() - return - if key =3D=3D ord("-"): - self.zoom_out() - return - - key =3D event.GetKeyCode() - (x, y) =3D self.scroll.GetViewStart() - if key =3D=3D wx.WXK_RIGHT: - self.scroll.Scroll(x + 1, y) - elif key =3D=3D wx.WXK_LEFT: - self.scroll.Scroll(x - 1, y) - elif key =3D=3D wx.WXK_DOWN: - self.scroll.Scroll(x, y + 1) - elif key =3D=3D wx.WXK_UP: - self.scroll.Scroll(x, y - 1) diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.= py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py deleted file mode 100644 index b75d31858e54..000000000000 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py +++ /dev/null @@ -1,92 +0,0 @@ -# Util.py - Python extension for perf script, miscellaneous utility code -# -# Copyright (C) 2010 by Tom Zanussi -# -# This software may be distributed under the terms of the GNU General -# Public License ("GPL") version 2 as published by the Free Software -# Foundation. -from __future__ import print_function - -import errno, os - -FUTEX_WAIT =3D 0 -FUTEX_WAKE =3D 1 -FUTEX_PRIVATE_FLAG =3D 128 -FUTEX_CLOCK_REALTIME =3D 256 -FUTEX_CMD_MASK =3D ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME) - -NSECS_PER_SEC =3D 1000000000 - -def avg(total, n): - return total / n - -def nsecs(secs, nsecs): - return secs * NSECS_PER_SEC + nsecs - -def nsecs_secs(nsecs): - return nsecs / NSECS_PER_SEC - -def nsecs_nsecs(nsecs): - return nsecs % NSECS_PER_SEC - -def nsecs_str(nsecs): - str =3D "%5u.%09u" % (nsecs_secs(nsecs), nsecs_nsecs(nsecs)), - return str - -def add_stats(dict, key, value): - if key not in dict: - dict[key] =3D (value, value, value, 1) - else: - min, max, avg, count =3D dict[key] - if value < min: - min =3D value - if value > max: - max =3D value - avg =3D (avg + value) / 2 - dict[key] =3D (min, max, avg, count + 1) - -def clear_term(): - print("\x1b[H\x1b[2J") - -audit_package_warned =3D False - -try: - import audit - machine_to_id =3D { - 'x86_64': audit.MACH_86_64, - 'aarch64': audit.MACH_AARCH64, - 'alpha' : audit.MACH_ALPHA, - 'ia64' : audit.MACH_IA64, - 'ppc' : audit.MACH_PPC, - 'ppc64' : audit.MACH_PPC64, - 'ppc64le' : audit.MACH_PPC64LE, - 's390' : audit.MACH_S390, - 's390x' : audit.MACH_S390X, - 'i386' : audit.MACH_X86, - 'i586' : audit.MACH_X86, - 'i686' : audit.MACH_X86, - } - try: - machine_to_id['armeb'] =3D audit.MACH_ARMEB - except: - pass - machine_id =3D machine_to_id[os.uname()[4]] -except: - if not audit_package_warned: - audit_package_warned =3D True - print("Install the python-audit package to get syscall names.\n" - "For example:\n # apt-get install python3-audit (Ubun= tu)" - "\n # yum install python3-audit (Fedora)" - "\n etc.\n") - -def syscall_name(id): - try: - return audit.audit_syscall_to_name(id, machine_id) - except: - return str(id) - -def strerror(nr): - try: - return errno.errorcode[abs(nr)] - except: - return "Unknown %d errno" % nr diff --git a/tools/perf/scripts/python/arm-cs-trace-disasm.py b/tools/perf/= scripts/python/arm-cs-trace-disasm.py deleted file mode 100755 index ba208c90d631..000000000000 --- a/tools/perf/scripts/python/arm-cs-trace-disasm.py +++ /dev/null @@ -1,355 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# arm-cs-trace-disasm.py: ARM CoreSight Trace Dump With Disassember -# -# Author: Tor Jeremiassen -# Mathieu Poirier -# Leo Yan -# Al Grant - -from __future__ import print_function -import os -from os import path -import re -from subprocess import * -import argparse -import platform - -from perf_trace_context import perf_sample_srccode, perf_config_get - -# Below are some example commands for using this script. -# Note a --kcore recording is required for accurate decode -# due to the alternatives patching mechanism. However this -# script only supports reading vmlinux for disassembly dump, -# meaning that any patched instructions will appear -# as unpatched, but the instruction ranges themselves will -# be correct. In addition to this, source line info comes -# from Perf, and when using kcore there is no debug info. The -# following lists the supported features in each mode: -# -# +-----------+-----------------+------------------+------------------+ -# | Recording | Accurate decode | Source line dump | Disassembly dump | -# +-----------+-----------------+------------------+------------------+ -# | --kcore | yes | no | yes | -# | normal | no | yes | yes | -# +-----------+-----------------+------------------+------------------+ -# -# Output disassembly with objdump and auto detect vmlinux -# (when running on same machine.) -# perf script -s scripts/python/arm-cs-trace-disasm.py -d -# -# Output disassembly with llvm-objdump: -# perf script -s scripts/python/arm-cs-trace-disasm.py \ -# -- -d llvm-objdump-11 -k path/to/vmlinux -# -# Output only source line and symbols: -# perf script -s scripts/python/arm-cs-trace-disasm.py - -def default_objdump(): - config =3D perf_config_get("annotate.objdump") - return config if config else "objdump" - -# Command line parsing. -def int_arg(v): - v =3D int(v) - if v < 0: - raise argparse.ArgumentTypeError("Argument must be a positive integer") - return v - -args =3D argparse.ArgumentParser() -args.add_argument("-k", "--vmlinux", - help=3D"Set path to vmlinux file. Omit to autodetect if running on sam= e machine") -args.add_argument("-d", "--objdump", nargs=3D"?", const=3Ddefault_objdump(= ), - help=3D"Show disassembly. Can also be used to change the objdump path"= ), -args.add_argument("-v", "--verbose", action=3D"store_true", help=3D"Enable= debugging log") -args.add_argument("--start-time", type=3Dint_arg, help=3D"Monotonic clock = time of sample to start from. " - "See 'time' field on samples in -v mode.") -args.add_argument("--stop-time", type=3Dint_arg, help=3D"Monotonic clock t= ime of sample to stop at. " - "See 'time' field on samples in -v mode.") -args.add_argument("--start-sample", type=3Dint_arg, help=3D"Index of sampl= e to start from. " - "See 'index' field on samples in -v mode.") -args.add_argument("--stop-sample", type=3Dint_arg, help=3D"Index of sample= to stop at. " - "See 'index' field on samples in -v mode.") - -options =3D args.parse_args() -if (options.start_time and options.stop_time and - options.start_time >=3D options.stop_time): - print("--start-time must less than --stop-time") - exit(2) -if (options.start_sample and options.stop_sample and - options.start_sample >=3D options.stop_sample): - print("--start-sample must less than --stop-sample") - exit(2) - -# Initialize global dicts and regular expression -disasm_cache =3D dict() -cpu_data =3D dict() -disasm_re =3D re.compile(r"^\s*([0-9a-fA-F]+):") -disasm_func_re =3D re.compile(r"^\s*([0-9a-fA-F]+)\s.*:") -cache_size =3D 64*1024 -sample_idx =3D -1 - -glb_source_file_name =3D None -glb_line_number =3D None -glb_dso =3D None - -kver =3D platform.release() -vmlinux_paths =3D [ - f"/usr/lib/debug/boot/vmlinux-{kver}.debug", - f"/usr/lib/debug/lib/modules/{kver}/vmlinux", - f"/lib/modules/{kver}/build/vmlinux", - f"/usr/lib/debug/boot/vmlinux-{kver}", - f"/boot/vmlinux-{kver}", - f"/boot/vmlinux", - f"vmlinux" -] - -def get_optional(perf_dict, field): - if field in perf_dict: - return perf_dict[field] - return "[unknown]" - -def get_offset(perf_dict, field): - if field in perf_dict: - return "+%#x" % perf_dict[field] - return "" - -def find_vmlinux(): - if hasattr(find_vmlinux, "path"): - return find_vmlinux.path - - for v in vmlinux_paths: - if os.access(v, os.R_OK): - find_vmlinux.path =3D v - break - else: - find_vmlinux.path =3D None - - return find_vmlinux.path - -def get_dso_file_path(dso_name, dso_build_id): - if (dso_name =3D=3D "[kernel.kallsyms]" or dso_name =3D=3D "vmlinux"): - if (options.vmlinux): - return options.vmlinux; - else: - return find_vmlinux() if find_vmlinux() else dso_name - - if (dso_name =3D=3D "[vdso]") : - append =3D "/vdso" - else: - append =3D "/elf" - - dso_path =3D os.environ['PERF_BUILDID_DIR'] + "/" + dso_name + "/" + dso_= build_id + append; - # Replace duplicate slash chars to single slash char - dso_path =3D dso_path.replace('//', '/', 1) - return dso_path - -def read_disam(dso_fname, dso_start, start_addr, stop_addr): - addr_range =3D str(start_addr) + ":" + str(stop_addr) + ":" + dso_fname - - # Don't let the cache get too big, clear it when it hits max size - if (len(disasm_cache) > cache_size): - disasm_cache.clear(); - - if addr_range in disasm_cache: - disasm_output =3D disasm_cache[addr_range]; - else: - start_addr =3D start_addr - dso_start; - stop_addr =3D stop_addr - dso_start; - disasm =3D [ options.objdump, "-d", "-z", - "--start-address=3D"+format(start_addr,"#x"), - "--stop-address=3D"+format(stop_addr,"#x") ] - disasm +=3D [ dso_fname ] - disasm_output =3D check_output(disasm).decode('utf-8').split('\n') - disasm_cache[addr_range] =3D disasm_output - - return disasm_output - -def print_disam(dso_fname, dso_start, start_addr, stop_addr): - for line in read_disam(dso_fname, dso_start, start_addr, stop_addr): - m =3D disasm_func_re.search(line) - if m is None: - m =3D disasm_re.search(line) - if m is None: - continue - print("\t" + line) - -def print_sample(sample): - print("Sample =3D { cpu: %04d addr: 0x%016x phys_addr: 0x%016x ip: 0x%016= x " \ - "pid: %d tid: %d period: %d time: %d index: %d}" % \ - (sample['cpu'], sample['addr'], sample['phys_addr'], \ - sample['ip'], sample['pid'], sample['tid'], \ - sample['period'], sample['time'], sample_idx)) - -def trace_begin(): - print('ARM CoreSight Trace Data Assembler Dump') - -def trace_end(): - print('End') - -def trace_unhandled(event_name, context, event_fields_dict): - print(' '.join(['%s=3D%s'%(k,str(v))for k,v in sorted(event_fields_dict.i= tems())])) - -def common_start_str(comm, sample): - sec =3D int(sample["time"] / 1000000000) - ns =3D sample["time"] % 1000000000 - cpu =3D sample["cpu"] - pid =3D sample["pid"] - tid =3D sample["tid"] - return "%16s %5u/%-5u [%04u] %9u.%09u " % (comm, pid, tid, cpu, sec, ns) - -# This code is copied from intel-pt-events.py for printing source code -# line and symbols. -def print_srccode(comm, param_dict, sample, symbol, dso): - ip =3D sample["ip"] - if symbol =3D=3D "[unknown]": - start_str =3D common_start_str(comm, sample) + ("%x" % ip).rjust(16).lju= st(40) - else: - offs =3D get_offset(param_dict, "symoff") - start_str =3D common_start_str(comm, sample) + (symbol + offs).ljust(40) - - global glb_source_file_name - global glb_line_number - global glb_dso - - source_file_name, line_number, source_line =3D perf_sample_srccode(perf_s= cript_context) - if source_file_name: - if glb_line_number =3D=3D line_number and glb_source_file_name =3D=3D so= urce_file_name: - src_str =3D "" - else: - if len(source_file_name) > 40: - src_file =3D ("..." + source_file_name[-37:]) + " " - else: - src_file =3D source_file_name.ljust(41) - - if source_line is None: - src_str =3D src_file + str(line_number).rjust(4) + " " - else: - src_str =3D src_file + str(line_number).rjust(4) + " " + source_line - glb_dso =3D None - elif dso =3D=3D glb_dso: - src_str =3D "" - else: - src_str =3D dso - glb_dso =3D dso - - glb_line_number =3D line_number - glb_source_file_name =3D source_file_name - - print(start_str, src_str) - -def process_event(param_dict): - global cache_size - global options - global sample_idx - - sample =3D param_dict["sample"] - comm =3D param_dict["comm"] - - name =3D param_dict["ev_name"] - dso =3D get_optional(param_dict, "dso") - dso_bid =3D get_optional(param_dict, "dso_bid") - dso_start =3D get_optional(param_dict, "dso_map_start") - dso_end =3D get_optional(param_dict, "dso_map_end") - symbol =3D get_optional(param_dict, "symbol") - map_pgoff =3D get_optional(param_dict, "map_pgoff") - # check for valid map offset - if (str(map_pgoff) =3D=3D '[unknown]'): - map_pgoff =3D 0 - - cpu =3D sample["cpu"] - ip =3D sample["ip"] - addr =3D sample["addr"] - - sample_idx +=3D 1 - - if (options.start_time and sample["time"] < options.start_time): - return - if (options.stop_time and sample["time"] > options.stop_time): - exit(0) - if (options.start_sample and sample_idx < options.start_sample): - return - if (options.stop_sample and sample_idx > options.stop_sample): - exit(0) - - if (options.verbose =3D=3D True): - print("Event type: %s" % name) - print_sample(sample) - - # Initialize CPU data if it's empty, and directly return back - # if this is the first tracing event for this CPU. - if (cpu_data.get(str(cpu) + 'addr') =3D=3D None): - cpu_data[str(cpu) + 'addr'] =3D addr - return - - # If cannot find dso so cannot dump assembler, bail out - if (dso =3D=3D '[unknown]'): - return - - # Validate dso start and end addresses - if ((dso_start =3D=3D '[unknown]') or (dso_end =3D=3D '[unknown]')): - print("Failed to find valid dso map for dso %s" % dso) - return - - if (name[0:12] =3D=3D "instructions"): - print_srccode(comm, param_dict, sample, symbol, dso) - return - - # Don't proceed if this event is not a branch sample, . - if (name[0:8] !=3D "branches"): - return - - # The format for packet is: - # - # +------------+------------+------------+ - # sample_prev: | addr | ip | cpu | - # +------------+------------+------------+ - # sample_next: | addr | ip | cpu | - # +------------+------------+------------+ - # - # We need to combine the two continuous packets to get the instruction - # range for sample_prev::cpu: - # - # [ sample_prev::addr .. sample_next::ip ] - # - # For this purose, sample_prev::addr is stored into cpu_data structure - # and read back for 'start_addr' when the new packet comes, and we need - # to use sample_next::ip to calculate 'stop_addr', plusing extra 4 for - # 'stop_addr' is for the sake of objdump so the final assembler dump can - # include last instruction for sample_next::ip. - start_addr =3D cpu_data[str(cpu) + 'addr'] - stop_addr =3D ip + 4 - - # Record for previous sample packet - cpu_data[str(cpu) + 'addr'] =3D addr - - # Filter out zero start_address. Optionally identify CS_ETM_TRACE_ON pack= et - if (start_addr =3D=3D 0): - if ((stop_addr =3D=3D 4) and (options.verbose =3D=3D True)): - print("CPU%d: CS_ETM_TRACE_ON packet is inserted" % cpu) - return - - if (start_addr < int(dso_start) or start_addr > int(dso_end)): - print("Start address 0x%x is out of range [ 0x%x .. 0x%x ] for dso %s" %= (start_addr, int(dso_start), int(dso_end), dso)) - return - - if (stop_addr < int(dso_start) or stop_addr > int(dso_end)): - print("Stop address 0x%x is out of range [ 0x%x .. 0x%x ] for dso %s" % = (stop_addr, int(dso_start), int(dso_end), dso)) - return - - if (options.objdump !=3D None): - # It doesn't need to decrease virtual memory offset for disassembly - # for kernel dso and executable file dso, so in this case we set - # vm_start to zero. - if (dso =3D=3D "[kernel.kallsyms]" or dso_start =3D=3D 0x400000): - dso_vm_start =3D 0 - map_pgoff =3D 0 - else: - dso_vm_start =3D int(dso_start) - - dso_fname =3D get_dso_file_path(dso, dso_bid) - if path.exists(dso_fname): - print_disam(dso_fname, dso_vm_start, start_addr + map_pgoff, stop_addr = + map_pgoff) - else: - print("Failed to find dso %s for address range [ 0x%x .. 0x%x ]" % (dso= , start_addr + map_pgoff, stop_addr + map_pgoff)) - - print_srccode(comm, param_dict, sample, symbol, dso) diff --git a/tools/perf/scripts/python/bin/compaction-times-record b/tools/= perf/scripts/python/bin/compaction-times-record deleted file mode 100644 index 6edcd40e14e8..000000000000 --- a/tools/perf/scripts/python/bin/compaction-times-record +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -perf record -e compaction:mm_compaction_begin -e compaction:mm_compaction_= end -e compaction:mm_compaction_migratepages -e compaction:mm_compaction_is= olate_migratepages -e compaction:mm_compaction_isolate_freepages $@ diff --git a/tools/perf/scripts/python/bin/compaction-times-report b/tools/= perf/scripts/python/bin/compaction-times-report deleted file mode 100644 index 3dc13897cfde..000000000000 --- a/tools/perf/scripts/python/bin/compaction-times-report +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -#description: display time taken by mm compaction -#args: [-h] [-u] [-p|-pv] [-t | [-m] [-fs] [-ms]] [pid|pid-range|comm-rege= x] -perf script -s "$PERF_EXEC_PATH"/scripts/python/compaction-times.py $@ diff --git a/tools/perf/scripts/python/bin/event_analyzing_sample-record b/= tools/perf/scripts/python/bin/event_analyzing_sample-record deleted file mode 100644 index 5ce652dabd02..000000000000 --- a/tools/perf/scripts/python/bin/event_analyzing_sample-record +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -# -# event_analyzing_sample.py can cover all type of perf samples including -# the tracepoints, so no special record requirements, just record what -# you want to analyze. -# -perf record $@ diff --git a/tools/perf/scripts/python/bin/event_analyzing_sample-report b/= tools/perf/scripts/python/bin/event_analyzing_sample-report deleted file mode 100644 index 0941fc94e158..000000000000 --- a/tools/perf/scripts/python/bin/event_analyzing_sample-report +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -# description: analyze all perf samples -perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/event_analyzing_sample.= py diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-record b/to= ols/perf/scripts/python/bin/export-to-postgresql-record deleted file mode 100644 index 221d66e05713..000000000000 --- a/tools/perf/scripts/python/bin/export-to-postgresql-record +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -# -# export perf data to a postgresql database. Can cover -# perf ip samples (excluding the tracepoints). No special -# record requirements, just record what you want to export. -# -perf record $@ diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-report b/to= ols/perf/scripts/python/bin/export-to-postgresql-report deleted file mode 100644 index cd335b6e2a01..000000000000 --- a/tools/perf/scripts/python/bin/export-to-postgresql-report +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# description: export perf data to a postgresql database -# args: [database name] [columns] [calls] -n_args=3D0 -for i in "$@" -do - if expr match "$i" "-" > /dev/null ; then - break - fi - n_args=3D$(( $n_args + 1 )) -done -if [ "$n_args" -gt 3 ] ; then - echo "usage: export-to-postgresql-report [database name] [columns] [ca= lls]" - exit -fi -if [ "$n_args" -gt 2 ] ; then - dbname=3D$1 - columns=3D$2 - calls=3D$3 - shift 3 -elif [ "$n_args" -gt 1 ] ; then - dbname=3D$1 - columns=3D$2 - shift 2 -elif [ "$n_args" -gt 0 ] ; then - dbname=3D$1 - shift -fi -perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py= $dbname $columns $calls diff --git a/tools/perf/scripts/python/bin/export-to-sqlite-record b/tools/= perf/scripts/python/bin/export-to-sqlite-record deleted file mode 100644 index 070204fd6d00..000000000000 --- a/tools/perf/scripts/python/bin/export-to-sqlite-record +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -# -# export perf data to a sqlite3 database. Can cover -# perf ip samples (excluding the tracepoints). No special -# record requirements, just record what you want to export. -# -perf record $@ diff --git a/tools/perf/scripts/python/bin/export-to-sqlite-report b/tools/= perf/scripts/python/bin/export-to-sqlite-report deleted file mode 100644 index 5ff6033e70ba..000000000000 --- a/tools/perf/scripts/python/bin/export-to-sqlite-report +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# description: export perf data to a sqlite3 database -# args: [database name] [columns] [calls] -n_args=3D0 -for i in "$@" -do - if expr match "$i" "-" > /dev/null ; then - break - fi - n_args=3D$(( $n_args + 1 )) -done -if [ "$n_args" -gt 3 ] ; then - echo "usage: export-to-sqlite-report [database name] [columns] [calls]" - exit -fi -if [ "$n_args" -gt 2 ] ; then - dbname=3D$1 - columns=3D$2 - calls=3D$3 - shift 3 -elif [ "$n_args" -gt 1 ] ; then - dbname=3D$1 - columns=3D$2 - shift 2 -elif [ "$n_args" -gt 0 ] ; then - dbname=3D$1 - shift -fi -perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-sqlite.py $db= name $columns $calls diff --git a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record b/= tools/perf/scripts/python/bin/failed-syscalls-by-pid-record deleted file mode 100644 index 74685f318379..000000000000 --- a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -(perf record -e raw_syscalls:sys_exit $@ || \ - perf record -e syscalls:sys_exit $@) 2> /dev/null diff --git a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report b/= tools/perf/scripts/python/bin/failed-syscalls-by-pid-report deleted file mode 100644 index fda5096d0cbf..000000000000 --- a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# description: system-wide failed syscalls, by pid -# args: [comm] -if [ $# -gt 0 ] ; then - if ! expr match "$1" "-" > /dev/null ; then - comm=3D$1 - shift - fi -fi -perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/failed-syscalls-by-pid.= py $comm diff --git a/tools/perf/scripts/python/bin/flamegraph-record b/tools/perf/s= cripts/python/bin/flamegraph-record deleted file mode 100755 index 7df5a19c0163..000000000000 --- a/tools/perf/scripts/python/bin/flamegraph-record +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -perf record -g "$@" diff --git a/tools/perf/scripts/python/bin/flamegraph-report b/tools/perf/s= cripts/python/bin/flamegraph-report deleted file mode 100755 index 453a6918afbe..000000000000 --- a/tools/perf/scripts/python/bin/flamegraph-report +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -# description: create flame graphs -perf script -s "$PERF_EXEC_PATH"/scripts/python/flamegraph.py "$@" diff --git a/tools/perf/scripts/python/bin/futex-contention-record b/tools/= perf/scripts/python/bin/futex-contention-record deleted file mode 100644 index b1495c9a9b20..000000000000 --- a/tools/perf/scripts/python/bin/futex-contention-record +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -perf record -e syscalls:sys_enter_futex -e syscalls:sys_exit_futex $@ diff --git a/tools/perf/scripts/python/bin/futex-contention-report b/tools/= perf/scripts/python/bin/futex-contention-report deleted file mode 100644 index 6c44271091ab..000000000000 --- a/tools/perf/scripts/python/bin/futex-contention-report +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -# description: futext contention measurement - -perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/futex-contention.py diff --git a/tools/perf/scripts/python/bin/gecko-record b/tools/perf/script= s/python/bin/gecko-record deleted file mode 100644 index f0d1aa55f171..000000000000 --- a/tools/perf/scripts/python/bin/gecko-record +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -perf record -F 99 -g "$@" diff --git a/tools/perf/scripts/python/bin/gecko-report b/tools/perf/script= s/python/bin/gecko-report deleted file mode 100755 index 1867ec8d9757..000000000000 --- a/tools/perf/scripts/python/bin/gecko-report +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# description: create firefox gecko profile json format from perf.data -if [ "$*" =3D "-i -" ]; then -perf script -s "$PERF_EXEC_PATH"/scripts/python/gecko.py -else -perf script -s "$PERF_EXEC_PATH"/scripts/python/gecko.py -- "$@" -fi diff --git a/tools/perf/scripts/python/bin/intel-pt-events-record b/tools/p= erf/scripts/python/bin/intel-pt-events-record deleted file mode 100644 index 6b9877cfe23e..000000000000 --- a/tools/perf/scripts/python/bin/intel-pt-events-record +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# -# print Intel PT Events including Power Events and PTWRITE. The intel_pt P= MU -# event needs to be specified with appropriate config terms. -# -if ! echo "$@" | grep -q intel_pt ; then - echo "Options must include the Intel PT event e.g. -e intel_pt/pwr_evt,pt= w/" - echo "and for power events it probably needs to be system wide i.e. -a op= tion" - echo "For example: -a -e intel_pt/pwr_evt,branch=3D0/ sleep 1" - exit 1 -fi -perf record $@ diff --git a/tools/perf/scripts/python/bin/intel-pt-events-report b/tools/p= erf/scripts/python/bin/intel-pt-events-report deleted file mode 100644 index beeac3fde9db..000000000000 --- a/tools/perf/scripts/python/bin/intel-pt-events-report +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -# description: print Intel PT Events including Power Events and PTWRITE -perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/intel-pt-events.py diff --git a/tools/perf/scripts/python/bin/mem-phys-addr-record b/tools/per= f/scripts/python/bin/mem-phys-addr-record deleted file mode 100644 index 5a875122a904..000000000000 --- a/tools/perf/scripts/python/bin/mem-phys-addr-record +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# -# Profiling physical memory by all retired load instructions/uops event -# MEM_INST_RETIRED.ALL_LOADS or MEM_UOPS_RETIRED.ALL_LOADS -# - -load=3D`perf list | grep mem_inst_retired.all_loads` -if [ -z "$load" ]; then - load=3D`perf list | grep mem_uops_retired.all_loads` -fi -if [ -z "$load" ]; then - echo "There is no event to count all retired load instructions/uops." - exit 1 -fi - -arg=3D$(echo $load | tr -d ' ') -arg=3D"$arg:P" -perf record --phys-data -e $arg $@ diff --git a/tools/perf/scripts/python/bin/mem-phys-addr-report b/tools/per= f/scripts/python/bin/mem-phys-addr-report deleted file mode 100644 index 3f2b847e2eab..000000000000 --- a/tools/perf/scripts/python/bin/mem-phys-addr-report +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -# description: resolve physical address samples -perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/mem-phys-addr.py diff --git a/tools/perf/scripts/python/bin/net_dropmonitor-record b/tools/p= erf/scripts/python/bin/net_dropmonitor-record deleted file mode 100755 index 423fb81dadae..000000000000 --- a/tools/perf/scripts/python/bin/net_dropmonitor-record +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -perf record -e skb:kfree_skb $@ diff --git a/tools/perf/scripts/python/bin/net_dropmonitor-report b/tools/p= erf/scripts/python/bin/net_dropmonitor-report deleted file mode 100755 index 8d698f5a06aa..000000000000 --- a/tools/perf/scripts/python/bin/net_dropmonitor-report +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -# description: display a table of dropped frames - -perf script -s "$PERF_EXEC_PATH"/scripts/python/net_dropmonitor.py $@ diff --git a/tools/perf/scripts/python/bin/netdev-times-record b/tools/perf= /scripts/python/bin/netdev-times-record deleted file mode 100644 index 558754b840a9..000000000000 --- a/tools/perf/scripts/python/bin/netdev-times-record +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -perf record -e net:net_dev_xmit -e net:net_dev_queue \ - -e net:netif_receive_skb -e net:netif_rx \ - -e skb:consume_skb -e skb:kfree_skb \ - -e skb:skb_copy_datagram_iovec -e napi:napi_poll \ - -e irq:irq_handler_entry -e irq:irq_handler_exit \ - -e irq:softirq_entry -e irq:softirq_exit \ - -e irq:softirq_raise $@ diff --git a/tools/perf/scripts/python/bin/netdev-times-report b/tools/perf= /scripts/python/bin/netdev-times-report deleted file mode 100644 index 8f759291da86..000000000000 --- a/tools/perf/scripts/python/bin/netdev-times-report +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -# description: display a process of packet and processing time -# args: [tx] [rx] [dev=3D] [debug] - -perf script -s "$PERF_EXEC_PATH"/scripts/python/netdev-times.py $@ diff --git a/tools/perf/scripts/python/bin/powerpc-hcalls-record b/tools/pe= rf/scripts/python/bin/powerpc-hcalls-record deleted file mode 100644 index b7402aa9147d..000000000000 --- a/tools/perf/scripts/python/bin/powerpc-hcalls-record +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -perf record -e "{powerpc:hcall_entry,powerpc:hcall_exit}" $@ diff --git a/tools/perf/scripts/python/bin/powerpc-hcalls-report b/tools/pe= rf/scripts/python/bin/powerpc-hcalls-report deleted file mode 100644 index dd32ad7465f6..000000000000 --- a/tools/perf/scripts/python/bin/powerpc-hcalls-report +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/powerpc-hcalls.py diff --git a/tools/perf/scripts/python/bin/sched-migration-record b/tools/p= erf/scripts/python/bin/sched-migration-record deleted file mode 100644 index 7493fddbe995..000000000000 --- a/tools/perf/scripts/python/bin/sched-migration-record +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -perf record -m 16384 -e sched:sched_wakeup -e sched:sched_wakeup_new -e sc= hed:sched_switch -e sched:sched_migrate_task $@ diff --git a/tools/perf/scripts/python/bin/sched-migration-report b/tools/p= erf/scripts/python/bin/sched-migration-report deleted file mode 100644 index 68b037a1849b..000000000000 --- a/tools/perf/scripts/python/bin/sched-migration-report +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -# description: sched migration overview -perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/sched-migration.py diff --git a/tools/perf/scripts/python/bin/sctop-record b/tools/perf/script= s/python/bin/sctop-record deleted file mode 100644 index d6940841e54f..000000000000 --- a/tools/perf/scripts/python/bin/sctop-record +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -(perf record -e raw_syscalls:sys_enter $@ || \ - perf record -e syscalls:sys_enter $@) 2> /dev/null diff --git a/tools/perf/scripts/python/bin/sctop-report b/tools/perf/script= s/python/bin/sctop-report deleted file mode 100644 index c32db294124d..000000000000 --- a/tools/perf/scripts/python/bin/sctop-report +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -# description: syscall top -# args: [comm] [interval] -n_args=3D0 -for i in "$@" -do - if expr match "$i" "-" > /dev/null ; then - break - fi - n_args=3D$(( $n_args + 1 )) -done -if [ "$n_args" -gt 2 ] ; then - echo "usage: sctop-report [comm] [interval]" - exit -fi -if [ "$n_args" -gt 1 ] ; then - comm=3D$1 - interval=3D$2 - shift 2 -elif [ "$n_args" -gt 0 ] ; then - interval=3D$1 - shift -fi -perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/sctop.py $comm $interval diff --git a/tools/perf/scripts/python/bin/stackcollapse-record b/tools/per= f/scripts/python/bin/stackcollapse-record deleted file mode 100755 index 9d8f9f0f3a17..000000000000 --- a/tools/perf/scripts/python/bin/stackcollapse-record +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -# -# stackcollapse.py can cover all type of perf samples including -# the tracepoints, so no special record requirements, just record what -# you want to analyze. -# -perf record "$@" diff --git a/tools/perf/scripts/python/bin/stackcollapse-report b/tools/per= f/scripts/python/bin/stackcollapse-report deleted file mode 100755 index 21a356bd27f6..000000000000 --- a/tools/perf/scripts/python/bin/stackcollapse-report +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -# description: produce callgraphs in short form for scripting use -perf script -s "$PERF_EXEC_PATH"/scripts/python/stackcollapse.py "$@" diff --git a/tools/perf/scripts/python/bin/syscall-counts-by-pid-record b/t= ools/perf/scripts/python/bin/syscall-counts-by-pid-record deleted file mode 100644 index d6940841e54f..000000000000 --- a/tools/perf/scripts/python/bin/syscall-counts-by-pid-record +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -(perf record -e raw_syscalls:sys_enter $@ || \ - perf record -e syscalls:sys_enter $@) 2> /dev/null diff --git a/tools/perf/scripts/python/bin/syscall-counts-by-pid-report b/t= ools/perf/scripts/python/bin/syscall-counts-by-pid-report deleted file mode 100644 index 16eb8d65c543..000000000000 --- a/tools/perf/scripts/python/bin/syscall-counts-by-pid-report +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# description: system-wide syscall counts, by pid -# args: [comm] -if [ $# -gt 0 ] ; then - if ! expr match "$1" "-" > /dev/null ; then - comm=3D$1 - shift - fi -fi -perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/syscall-counts-by-pid.p= y $comm diff --git a/tools/perf/scripts/python/bin/syscall-counts-record b/tools/pe= rf/scripts/python/bin/syscall-counts-record deleted file mode 100644 index d6940841e54f..000000000000 --- a/tools/perf/scripts/python/bin/syscall-counts-record +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -(perf record -e raw_syscalls:sys_enter $@ || \ - perf record -e syscalls:sys_enter $@) 2> /dev/null diff --git a/tools/perf/scripts/python/bin/syscall-counts-report b/tools/pe= rf/scripts/python/bin/syscall-counts-report deleted file mode 100644 index 0f0e9d453bb4..000000000000 --- a/tools/perf/scripts/python/bin/syscall-counts-report +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# description: system-wide syscall counts -# args: [comm] -if [ $# -gt 0 ] ; then - if ! expr match "$1" "-" > /dev/null ; then - comm=3D$1 - shift - fi -fi -perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/syscall-counts.py $comm diff --git a/tools/perf/scripts/python/bin/task-analyzer-record b/tools/per= f/scripts/python/bin/task-analyzer-record deleted file mode 100755 index 0f6b51bb2767..000000000000 --- a/tools/perf/scripts/python/bin/task-analyzer-record +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -perf record -e sched:sched_switch -e sched:sched_migrate_task "$@" diff --git a/tools/perf/scripts/python/bin/task-analyzer-report b/tools/per= f/scripts/python/bin/task-analyzer-report deleted file mode 100755 index 4b16a8cc40a0..000000000000 --- a/tools/perf/scripts/python/bin/task-analyzer-report +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -# description: analyze timings of tasks -perf script -s "$PERF_EXEC_PATH"/scripts/python/task-analyzer.py -- "$@" diff --git a/tools/perf/scripts/python/check-perf-trace.py b/tools/perf/scr= ipts/python/check-perf-trace.py deleted file mode 100644 index d2c22954800d..000000000000 --- a/tools/perf/scripts/python/check-perf-trace.py +++ /dev/null @@ -1,84 +0,0 @@ -# perf script event handlers, generated by perf script -g python -# (c) 2010, Tom Zanussi -# Licensed under the terms of the GNU GPL License version 2 -# -# This script tests basic functionality such as flag and symbol -# strings, common_xxx() calls back into perf, begin, end, unhandled -# events, etc. Basically, if this script runs successfully and -# displays expected results, Python scripting support should be ok. - -from __future__ import print_function - -import os -import sys - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -from Core import * -from perf_trace_context import * - -unhandled =3D autodict() - -def trace_begin(): - print("trace_begin") - pass - -def trace_end(): - print_unhandled() - -def irq__softirq_entry(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, vec): - print_header(event_name, common_cpu, common_secs, common_nsecs, - common_pid, common_comm) - - print_uncommon(context) - - print("vec=3D%s" % (symbol_str("irq__softirq_entry", "vec", vec))) - -def kmem__kmalloc(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, call_site, ptr, bytes_req, bytes_alloc, - gfp_flags): - print_header(event_name, common_cpu, common_secs, common_nsecs, - common_pid, common_comm) - - print_uncommon(context) - - print("call_site=3D%u, ptr=3D%u, bytes_req=3D%u, " - "bytes_alloc=3D%u, gfp_flags=3D%s" % - (call_site, ptr, bytes_req, bytes_alloc, - flag_str("kmem__kmalloc", "gfp_flags", gfp_flags))) - -def trace_unhandled(event_name, context, event_fields_dict): - try: - unhandled[event_name] +=3D 1 - except TypeError: - unhandled[event_name] =3D 1 - -def print_header(event_name, cpu, secs, nsecs, pid, comm): - print("%-20s %5u %05u.%09u %8u %-20s " % - (event_name, cpu, secs, nsecs, pid, comm), - end=3D' ') - -# print trace fields not included in handler args -def print_uncommon(context): - print("common_preempt_count=3D%d, common_flags=3D%s, " - "common_lock_depth=3D%d, " % - (common_pc(context), trace_flag_str(common_flags(context)), - common_lock_depth(context))) - -def print_unhandled(): - keys =3D unhandled.keys() - if not keys: - return - - print("\nunhandled events:\n") - - print("%-40s %10s" % ("event", "count")) - print("%-40s %10s" % ("----------------------------------------", - "-----------")) - - for event_name in keys: - print("%-40s %10d\n" % (event_name, unhandled[event_name])) diff --git a/tools/perf/scripts/python/compaction-times.py b/tools/perf/scr= ipts/python/compaction-times.py deleted file mode 100644 index 9401f7c14747..000000000000 --- a/tools/perf/scripts/python/compaction-times.py +++ /dev/null @@ -1,311 +0,0 @@ -# report time spent in compaction -# Licensed under the terms of the GNU GPL License version 2 - -# testing: -# 'echo 1 > /proc/sys/vm/compact_memory' to force compaction of all zones - -import os -import sys -import re - -import signal -signal.signal(signal.SIGPIPE, signal.SIG_DFL) - -usage =3D "usage: perf script report compaction-times.py -- [-h] [-u] [-p|= -pv] [-t | [-m] [-fs] [-ms]] [pid|pid-range|comm-regex]\n" - -class popt: - DISP_DFL =3D 0 - DISP_PROC =3D 1 - DISP_PROC_VERBOSE=3D2 - -class topt: - DISP_TIME =3D 0 - DISP_MIG =3D 1 - DISP_ISOLFREE =3D 2 - DISP_ISOLMIG =3D 4 - DISP_ALL =3D 7 - -class comm_filter: - def __init__(self, re): - self.re =3D re - - def filter(self, pid, comm): - m =3D self.re.search(comm) - return m =3D=3D None or m.group() =3D=3D "" - -class pid_filter: - def __init__(self, low, high): - self.low =3D (0 if low =3D=3D "" else int(low)) - self.high =3D (0 if high =3D=3D "" else int(high)) - - def filter(self, pid, comm): - return not (pid >=3D self.low and (self.high =3D=3D 0 or pid <=3D self.h= igh)) - -def set_type(t): - global opt_disp - opt_disp =3D (t if opt_disp =3D=3D topt.DISP_ALL else opt_disp|t) - -def ns(sec, nsec): - return (sec * 1000000000) + nsec - -def time(ns): - return "%dns" % ns if opt_ns else "%dus" % (round(ns, -3) / 1000) - -class pair: - def __init__(self, aval, bval, alabel =3D None, blabel =3D None): - self.alabel =3D alabel - self.blabel =3D blabel - self.aval =3D aval - self.bval =3D bval - - def __add__(self, rhs): - self.aval +=3D rhs.aval - self.bval +=3D rhs.bval - return self - - def __str__(self): - return "%s=3D%d %s=3D%d" % (self.alabel, self.aval, self.blabel, self.bv= al) - -class cnode: - def __init__(self, ns): - self.ns =3D ns - self.migrated =3D pair(0, 0, "moved", "failed") - self.fscan =3D pair(0,0, "scanned", "isolated") - self.mscan =3D pair(0,0, "scanned", "isolated") - - def __add__(self, rhs): - self.ns +=3D rhs.ns - self.migrated +=3D rhs.migrated - self.fscan +=3D rhs.fscan - self.mscan +=3D rhs.mscan - return self - - def __str__(self): - prev =3D 0 - s =3D "%s " % time(self.ns) - if (opt_disp & topt.DISP_MIG): - s +=3D "migration: %s" % self.migrated - prev =3D 1 - if (opt_disp & topt.DISP_ISOLFREE): - s +=3D "%sfree_scanner: %s" % (" " if prev else "", self.fscan) - prev =3D 1 - if (opt_disp & topt.DISP_ISOLMIG): - s +=3D "%smigration_scanner: %s" % (" " if prev else "", self.mscan) - return s - - def complete(self, secs, nsecs): - self.ns =3D ns(secs, nsecs) - self.ns - - def increment(self, migrated, fscan, mscan): - if (migrated !=3D None): - self.migrated +=3D migrated - if (fscan !=3D None): - self.fscan +=3D fscan - if (mscan !=3D None): - self.mscan +=3D mscan - - -class chead: - heads =3D {} - val =3D cnode(0); - fobj =3D None - - @classmethod - def add_filter(cls, filter): - cls.fobj =3D filter - - @classmethod - def create_pending(cls, pid, comm, start_secs, start_nsecs): - filtered =3D 0 - try: - head =3D cls.heads[pid] - filtered =3D head.is_filtered() - except KeyError: - if cls.fobj !=3D None: - filtered =3D cls.fobj.filter(pid, comm) - head =3D cls.heads[pid] =3D chead(comm, pid, filtered) - - if not filtered: - head.mark_pending(start_secs, start_nsecs) - - @classmethod - def increment_pending(cls, pid, migrated, fscan, mscan): - head =3D cls.heads[pid] - if not head.is_filtered(): - if head.is_pending(): - head.do_increment(migrated, fscan, mscan) - else: - sys.stderr.write("missing start compaction event for pid %d\n" % pid) - - @classmethod - def complete_pending(cls, pid, secs, nsecs): - head =3D cls.heads[pid] - if not head.is_filtered(): - if head.is_pending(): - head.make_complete(secs, nsecs) - else: - sys.stderr.write("missing start compaction event for pid %d\n" % pid) - - @classmethod - def gen(cls): - if opt_proc !=3D popt.DISP_DFL: - for i in cls.heads: - yield cls.heads[i] - - @classmethod - def str(cls): - return cls.val - - def __init__(self, comm, pid, filtered): - self.comm =3D comm - self.pid =3D pid - self.val =3D cnode(0) - self.pending =3D None - self.filtered =3D filtered - self.list =3D [] - - def __add__(self, rhs): - self.ns +=3D rhs.ns - self.val +=3D rhs.val - return self - - def mark_pending(self, secs, nsecs): - self.pending =3D cnode(ns(secs, nsecs)) - - def do_increment(self, migrated, fscan, mscan): - self.pending.increment(migrated, fscan, mscan) - - def make_complete(self, secs, nsecs): - self.pending.complete(secs, nsecs) - chead.val +=3D self.pending - - if opt_proc !=3D popt.DISP_DFL: - self.val +=3D self.pending - - if opt_proc =3D=3D popt.DISP_PROC_VERBOSE: - self.list.append(self.pending) - self.pending =3D None - - def enumerate(self): - if opt_proc =3D=3D popt.DISP_PROC_VERBOSE and not self.is_filtered(): - for i, pelem in enumerate(self.list): - sys.stdout.write("%d[%s].%d: %s\n" % (self.pid, self.comm, i+1, pelem)) - - def is_pending(self): - return self.pending !=3D None - - def is_filtered(self): - return self.filtered - - def display(self): - if not self.is_filtered(): - sys.stdout.write("%d[%s]: %s\n" % (self.pid, self.comm, self.val)) - - -def trace_end(): - sys.stdout.write("total: %s\n" % chead.str()) - for i in chead.gen(): - i.display(), - i.enumerate() - -def compaction__mm_compaction_migratepages(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, nr_migrated, nr_failed): - - chead.increment_pending(common_pid, - pair(nr_migrated, nr_failed), None, None) - -def compaction__mm_compaction_isolate_freepages(event_name, context, commo= n_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, start_pfn, end_pfn, nr_scanned, nr_taken): - - chead.increment_pending(common_pid, - None, pair(nr_scanned, nr_taken), None) - -def compaction__mm_compaction_isolate_migratepages(event_name, context, co= mmon_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, start_pfn, end_pfn, nr_scanned, nr_taken): - - chead.increment_pending(common_pid, - None, None, pair(nr_scanned, nr_taken)) - -def compaction__mm_compaction_end(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, zone_start, migrate_start, free_start, zone_end, - sync, status): - - chead.complete_pending(common_pid, common_secs, common_nsecs) - -def compaction__mm_compaction_begin(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, zone_start, migrate_start, free_start, zone_end, - sync): - - chead.create_pending(common_pid, common_comm, common_secs, common_nsecs) - -def pr_help(): - global usage - - sys.stdout.write(usage) - sys.stdout.write("\n") - sys.stdout.write("-h display this help\n") - sys.stdout.write("-p display by process\n") - sys.stdout.write("-pv display by process (verbose)\n") - sys.stdout.write("-t display stall times only\n") - sys.stdout.write("-m display stats for migration\n") - sys.stdout.write("-fs display stats for free scanner\n") - sys.stdout.write("-ms display stats for migration scanner\n") - sys.stdout.write("-u display results in microseconds (default nanoseconds= )\n") - - -comm_re =3D None -pid_re =3D None -pid_regex =3D r"^(\d*)-(\d*)$|^(\d*)$" - -opt_proc =3D popt.DISP_DFL -opt_disp =3D topt.DISP_ALL - -opt_ns =3D True - -argc =3D len(sys.argv) - 1 -if argc >=3D 1: - pid_re =3D re.compile(pid_regex) - - for i, opt in enumerate(sys.argv[1:]): - if opt[0] =3D=3D "-": - if opt =3D=3D "-h": - pr_help() - exit(0); - elif opt =3D=3D "-p": - opt_proc =3D popt.DISP_PROC - elif opt =3D=3D "-pv": - opt_proc =3D popt.DISP_PROC_VERBOSE - elif opt =3D=3D '-u': - opt_ns =3D False - elif opt =3D=3D "-t": - set_type(topt.DISP_TIME) - elif opt =3D=3D "-m": - set_type(topt.DISP_MIG) - elif opt =3D=3D "-fs": - set_type(topt.DISP_ISOLFREE) - elif opt =3D=3D "-ms": - set_type(topt.DISP_ISOLMIG) - else: - sys.exit(usage) - - elif i =3D=3D argc - 1: - m =3D pid_re.search(opt) - if m !=3D None and m.group() !=3D "": - if m.group(3) !=3D None: - f =3D pid_filter(m.group(3), m.group(3)) - else: - f =3D pid_filter(m.group(1), m.group(2)) - else: - try: - comm_re=3Dre.compile(opt) - except: - sys.stderr.write("invalid regex '%s'" % opt) - sys.exit(usage) - f =3D comm_filter(comm_re) - - chead.add_filter(f) diff --git a/tools/perf/scripts/python/event_analyzing_sample.py b/tools/pe= rf/scripts/python/event_analyzing_sample.py deleted file mode 100644 index aa1e2cfa26a6..000000000000 --- a/tools/perf/scripts/python/event_analyzing_sample.py +++ /dev/null @@ -1,192 +0,0 @@ -# event_analyzing_sample.py: general event handler in python -# SPDX-License-Identifier: GPL-2.0 -# -# Current perf report is already very powerful with the annotation integra= ted, -# and this script is not trying to be as powerful as perf report, but -# providing end user/developer a flexible way to analyze the events other -# than trace points. -# -# The 2 database related functions in this script just show how to gather -# the basic information, and users can modify and write their own functions -# according to their specific requirement. -# -# The first function "show_general_events" just does a basic grouping for = all -# generic events with the help of sqlite, and the 2nd one "show_pebs_ll" is -# for a x86 HW PMU event: PEBS with load latency data. -# - -from __future__ import print_function - -import os -import sys -import math -import struct -import sqlite3 - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -from perf_trace_context import * -from EventClass import * - -# -# If the perf.data has a big number of samples, then the insert operation -# will be very time consuming (about 10+ minutes for 10000 samples) if the -# .db database is on disk. Move the .db file to RAM based FS to speedup -# the handling, which will cut the time down to several seconds. -# -con =3D sqlite3.connect("/dev/shm/perf.db") -con.isolation_level =3D None - -def trace_begin(): - print("In trace_begin:\n") - - # - # Will create several tables at the start, pebs_ll is for PEBS dat= a with - # load latency info, while gen_events is for general event. - # - con.execute(""" - create table if not exists gen_events ( - name text, - symbol text, - comm text, - dso text - );""") - con.execute(""" - create table if not exists pebs_ll ( - name text, - symbol text, - comm text, - dso text, - flags integer, - ip integer, - status integer, - dse integer, - dla integer, - lat integer - );""") - -# -# Create and insert event object to a database so that user could -# do more analysis with simple database commands. -# -def process_event(param_dict): - event_attr =3D param_dict["attr"] - sample =3D param_dict["sample"] - raw_buf =3D param_dict["raw_buf"] - comm =3D param_dict["comm"] - name =3D param_dict["ev_name"] - - # Symbol and dso info are not always resolved - if ("dso" in param_dict): - dso =3D param_dict["dso"] - else: - dso =3D "Unknown_dso" - - if ("symbol" in param_dict): - symbol =3D param_dict["symbol"] - else: - symbol =3D "Unknown_symbol" - - # Create the event object and insert it to the right table in data= base - event =3D create_event(name, comm, dso, symbol, raw_buf) - insert_db(event) - -def insert_db(event): - if event.ev_type =3D=3D EVTYPE_GENERIC: - con.execute("insert into gen_events values(?, ?, ?, ?)", - (event.name, event.symbol, event.comm, eve= nt.dso)) - elif event.ev_type =3D=3D EVTYPE_PEBS_LL: - event.ip &=3D 0x7fffffffffffffff - event.dla &=3D 0x7fffffffffffffff - con.execute("insert into pebs_ll values (?, ?, ?, ?, ?, ?,= ?, ?, ?, ?)", - (event.name, event.symbol, event.comm, event.dso, = event.flags, - event.ip, event.status, event.dse, event.d= la, event.lat)) - -def trace_end(): - print("In trace_end:\n") - # We show the basic info for the 2 type of event classes - show_general_events() - show_pebs_ll() - con.close() - -# -# As the event number may be very big, so we can't use linear way -# to show the histogram in real number, but use a log2 algorithm. -# - -def num2sym(num): - # Each number will have at least one '#' - snum =3D '#' * (int)(math.log(num, 2) + 1) - return snum - -def show_general_events(): - - # Check the total record number in the table - count =3D con.execute("select count(*) from gen_events") - for t in count: - print("There is %d records in gen_events table" % t[0]) - if t[0] =3D=3D 0: - return - - print("Statistics about the general events grouped by thread/symbo= l/dso: \n") - - # Group by thread - commq =3D con.execute("select comm, count(comm) from gen_events gr= oup by comm order by -count(comm)") - print("\n%16s %8s %16s\n%s" % ("comm", "number", "histogram", "=3D= "*42)) - for row in commq: - print("%16s %8d %s" % (row[0], row[1], num2sym(row[1]))) - - # Group by symbol - print("\n%32s %8s %16s\n%s" % ("symbol", "number", "histogram", "= =3D"*58)) - symbolq =3D con.execute("select symbol, count(symbol) from gen_eve= nts group by symbol order by -count(symbol)") - for row in symbolq: - print("%32s %8d %s" % (row[0], row[1], num2sym(row[1]))) - - # Group by dso - print("\n%40s %8s %16s\n%s" % ("dso", "number", "histogram", "=3D"= *74)) - dsoq =3D con.execute("select dso, count(dso) from gen_events group= by dso order by -count(dso)") - for row in dsoq: - print("%40s %8d %s" % (row[0], row[1], num2sym(row[1]))) - -# -# This function just shows the basic info, and we could do more with the -# data in the tables, like checking the function parameters when some -# big latency events happen. -# -def show_pebs_ll(): - - count =3D con.execute("select count(*) from pebs_ll") - for t in count: - print("There is %d records in pebs_ll table" % t[0]) - if t[0] =3D=3D 0: - return - - print("Statistics about the PEBS Load Latency events grouped by th= read/symbol/dse/latency: \n") - - # Group by thread - commq =3D con.execute("select comm, count(comm) from pebs_ll group= by comm order by -count(comm)") - print("\n%16s %8s %16s\n%s" % ("comm", "number", "histogram", "=3D= "*42)) - for row in commq: - print("%16s %8d %s" % (row[0], row[1], num2sym(row[1]))) - - # Group by symbol - print("\n%32s %8s %16s\n%s" % ("symbol", "number", "histogram", "= =3D"*58)) - symbolq =3D con.execute("select symbol, count(symbol) from pebs_ll= group by symbol order by -count(symbol)") - for row in symbolq: - print("%32s %8d %s" % (row[0], row[1], num2sym(row[1]))) - - # Group by dse - dseq =3D con.execute("select dse, count(dse) from pebs_ll group by= dse order by -count(dse)") - print("\n%32s %8s %16s\n%s" % ("dse", "number", "histogram", "=3D"= *58)) - for row in dseq: - print("%32s %8d %s" % (row[0], row[1], num2sym(row[1]))) - - # Group by latency - latq =3D con.execute("select lat, count(lat) from pebs_ll group by= lat order by lat") - print("\n%32s %8s %16s\n%s" % ("latency", "number", "histogram", "= =3D"*58)) - for row in latq: - print("%32s %8d %s" % (row[0], row[1], num2sym(row[1]))) - -def trace_unhandled(event_name, context, event_fields_dict): - print (' '.join(['%s=3D%s'%(k,str(v))for k,v in sorted(event_field= s_dict.items())])) diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf= /scripts/python/export-to-postgresql.py deleted file mode 100644 index 3a6bdcd74e60..000000000000 --- a/tools/perf/scripts/python/export-to-postgresql.py +++ /dev/null @@ -1,1114 +0,0 @@ -# export-to-postgresql.py: export perf data to a postgresql database -# Copyright (c) 2014, Intel Corporation. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms and conditions of the GNU General Public License, -# version 2, as published by the Free Software Foundation. -# -# This program is distributed in the hope it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. - -from __future__ import print_function - -import os -import sys -import struct -import datetime - -# To use this script you will need to have installed package python-pyside= which -# provides LGPL-licensed Python bindings for Qt. You will also need the p= ackage -# libqt4-sql-psql for Qt postgresql support. -# -# The script assumes postgresql is running on the local machine and that t= he -# user has postgresql permissions to create databases. Examples of install= ing -# postgresql and adding such a user are: -# -# fedora: -# -# $ sudo yum install postgresql postgresql-server qt-postgresql -# $ sudo su - postgres -c initdb -# $ sudo service postgresql start -# $ sudo su - postgres -# $ createuser -s # Older versions may not support = -s, in which case answer the prompt below: -# Shall the new role be a superuser? (y/n) y -# $ sudo yum install python-pyside -# -# Alternately, to use Python3 and/or pyside 2, one of the following: -# $ sudo yum install python3-pyside -# $ pip install --user PySide2 -# $ pip3 install --user PySide2 -# -# ubuntu: -# -# $ sudo apt-get install postgresql -# $ sudo su - postgres -# $ createuser -s -# $ sudo apt-get install python-pyside.qtsql libqt4-sql-psql -# -# Alternately, to use Python3 and/or pyside 2, one of the following: -# -# $ sudo apt-get install python3-pyside.qtsql libqt4-sql-psql -# $ sudo apt-get install python-pyside2.qtsql libqt5sql5-psql -# $ sudo apt-get install python3-pyside2.qtsql libqt5sql5-psql -# -# An example of using this script with Intel PT: -# -# $ perf record -e intel_pt//u ls -# $ perf script -s ~/libexec/perf-core/scripts/python/export-to-postgresql= .py pt_example branches calls -# 2015-05-29 12:49:23.464364 Creating database... -# 2015-05-29 12:49:26.281717 Writing to intermediate files... -# 2015-05-29 12:49:27.190383 Copying to database... -# 2015-05-29 12:49:28.140451 Removing intermediate files... -# 2015-05-29 12:49:28.147451 Adding primary keys -# 2015-05-29 12:49:28.655683 Adding foreign keys -# 2015-05-29 12:49:29.365350 Done -# -# To browse the database, psql can be used e.g. -# -# $ psql pt_example -# pt_example=3D# select * from samples_view where id < 100; -# pt_example=3D# \d+ -# pt_example=3D# \d+ samples_view -# pt_example=3D# \q -# -# An example of using the database is provided by the script -# exported-sql-viewer.py. Refer to that script for details. -# -# Tables: -# -# The tables largely correspond to perf tools' data structures. They are = largely self-explanatory. -# -# samples -# -# 'samples' is the main table. It represents what instruction was executi= ng at a point in time -# when something (a selected event) happened. The memory address is the = instruction pointer or 'ip'. -# -# calls -# -# 'calls' represents function calls and is related to 'samples' by 'call_= id' and 'return_id'. -# 'calls' is only created when the 'calls' option to this script is speci= fied. -# -# call_paths -# -# 'call_paths' represents all the call stacks. Each 'call' has an associ= ated record in 'call_paths'. -# 'calls_paths' is only created when the 'calls' option to this script is= specified. -# -# branch_types -# -# 'branch_types' provides descriptions for each type of branch. -# -# comm_threads -# -# 'comm_threads' shows how 'comms' relates to 'threads'. -# -# comms -# -# 'comms' contains a record for each 'comm' - the name given to the execu= table that is running. -# -# dsos -# -# 'dsos' contains a record for each executable file or library. -# -# machines -# -# 'machines' can be used to distinguish virtual machines if virtualizatio= n is supported. -# -# selected_events -# -# 'selected_events' contains a record for each kind of event that has bee= n sampled. -# -# symbols -# -# 'symbols' contains a record for each symbol. Only symbols that have sa= mples are present. -# -# threads -# -# 'threads' contains a record for each thread. -# -# Views: -# -# Most of the tables have views for more friendly display. The views are: -# -# calls_view -# call_paths_view -# comm_threads_view -# dsos_view -# machines_view -# samples_view -# symbols_view -# threads_view -# -# More examples of browsing the database with psql: -# Note that some of the examples are not the most optimal SQL query. -# Note that call information is only available if the script's 'calls' o= ption has been used. -# -# Top 10 function calls (not aggregated by symbol): -# -# SELECT * FROM calls_view ORDER BY elapsed_time DESC LIMIT 10; -# -# Top 10 function calls (aggregated by symbol): -# -# SELECT symbol_id,(SELECT name FROM symbols WHERE id =3D symbol_id) AS s= ymbol, -# SUM(elapsed_time) AS tot_elapsed_time,SUM(branch_count) AS tot_branch_= count -# FROM calls_view GROUP BY symbol_id ORDER BY tot_elapsed_time DESC LIMI= T 10; -# -# Note that the branch count gives a rough estimation of cpu usage, so fu= nctions -# that took a long time but have a relatively low branch count must have = spent time -# waiting. -# -# Find symbols by pattern matching on part of the name (e.g. names contain= ing 'alloc'): -# -# SELECT * FROM symbols_view WHERE name LIKE '%alloc%'; -# -# Top 10 function calls for a specific symbol (e.g. whose symbol_id is 187= ): -# -# SELECT * FROM calls_view WHERE symbol_id =3D 187 ORDER BY elapsed_time = DESC LIMIT 10; -# -# Show function calls made by function in the same context (i.e. same call= path) (e.g. one with call_path_id 254): -# -# SELECT * FROM calls_view WHERE parent_call_path_id =3D 254; -# -# Show branches made during a function call (e.g. where call_id is 29357 a= nd return_id is 29370 and tid is 29670) -# -# SELECT * FROM samples_view WHERE id >=3D 29357 AND id <=3D 29370 AND ti= d =3D 29670 AND event LIKE 'branches%'; -# -# Show transactions: -# -# SELECT * FROM samples_view WHERE event =3D 'transactions'; -# -# Note transaction start has 'in_tx' true whereas, transaction end has 'i= n_tx' false. -# Transaction aborts have branch_type_name 'transaction abort' -# -# Show transaction aborts: -# -# SELECT * FROM samples_view WHERE event =3D 'transactions' AND branch_ty= pe_name =3D 'transaction abort'; -# -# To print a call stack requires walking the call_paths table. For exampl= e this python script: -# #!/usr/bin/python2 -# -# import sys -# from PySide.QtSql import * -# -# if __name__ =3D=3D '__main__': -# if (len(sys.argv) < 3): -# print >> sys.stderr, "Usage is: printcallstack.py " -# raise Exception("Too few arguments") -# dbname =3D sys.argv[1] -# call_path_id =3D sys.argv[2] -# db =3D QSqlDatabase.addDatabase('QPSQL') -# db.setDatabaseName(dbname) -# if not db.open(): -# raise Exception("Failed to open database " + dbname + = " error: " + db.lastError().text()) -# query =3D QSqlQuery(db) -# print " id ip symbol_id symbol = dso_id dso_short_name" -# while call_path_id !=3D 0 and call_path_id !=3D 1: -# ret =3D query.exec_('SELECT * FROM call_paths_view WHE= RE id =3D ' + str(call_path_id)) -# if not ret: -# raise Exception("Query failed: " + query.lastE= rror().text()) -# if not query.next(): -# raise Exception("Query failed") -# print "{0:>6} {1:>10} {2:>9} {3:<30} {4:>6} {5:<3= 0}".format(query.value(0), query.value(1), query.value(2), query.value(3), = query.value(4), query.value(5)) -# call_path_id =3D query.value(6) - -pyside_version_1 =3D True -if not "pyside-version-1" in sys.argv: - try: - from PySide2.QtSql import * - pyside_version_1 =3D False - except: - pass - -if pyside_version_1: - from PySide.QtSql import * - -if sys.version_info < (3, 0): - def toserverstr(str): - return str - def toclientstr(str): - return str -else: - # Assume UTF-8 server_encoding and client_encoding - def toserverstr(str): - return bytes(str, "UTF_8") - def toclientstr(str): - return bytes(str, "UTF_8") - -# Need to access PostgreSQL C library directly to use COPY FROM STDIN -from ctypes import * -libpq =3D CDLL("libpq.so.5") -PQconnectdb =3D libpq.PQconnectdb -PQconnectdb.restype =3D c_void_p -PQconnectdb.argtypes =3D [ c_char_p ] -PQfinish =3D libpq.PQfinish -PQfinish.argtypes =3D [ c_void_p ] -PQstatus =3D libpq.PQstatus -PQstatus.restype =3D c_int -PQstatus.argtypes =3D [ c_void_p ] -PQexec =3D libpq.PQexec -PQexec.restype =3D c_void_p -PQexec.argtypes =3D [ c_void_p, c_char_p ] -PQresultStatus =3D libpq.PQresultStatus -PQresultStatus.restype =3D c_int -PQresultStatus.argtypes =3D [ c_void_p ] -PQputCopyData =3D libpq.PQputCopyData -PQputCopyData.restype =3D c_int -PQputCopyData.argtypes =3D [ c_void_p, c_void_p, c_int ] -PQputCopyEnd =3D libpq.PQputCopyEnd -PQputCopyEnd.restype =3D c_int -PQputCopyEnd.argtypes =3D [ c_void_p, c_void_p ] - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -# These perf imports are not used at present -#from perf_trace_context import * -#from Core import * - -perf_db_export_mode =3D True -perf_db_export_calls =3D False -perf_db_export_callchains =3D False - -def printerr(*args, **kw_args): - print(*args, file=3Dsys.stderr, **kw_args) - -def printdate(*args, **kw_args): - print(datetime.datetime.today(), *args, sep=3D' ', **kw_args) - -def usage(): - printerr("Usage is: export-to-postgresql.py [] [= ] [] []"); - printerr("where: columns 'all' or 'branches'"); - printerr(" calls 'calls' =3D> create calls and call_p= aths table"); - printerr(" callchains 'callchains' =3D> create call_paths = table"); - printerr(" pyside-version-1 'pyside-version-1' =3D> use pyside v= ersion 1"); - raise Exception("Too few or bad arguments") - -if (len(sys.argv) < 2): - usage() - -dbname =3D sys.argv[1] - -if (len(sys.argv) >=3D 3): - columns =3D sys.argv[2] -else: - columns =3D "all" - -if columns not in ("all", "branches"): - usage() - -branches =3D (columns =3D=3D "branches") - -for i in range(3,len(sys.argv)): - if (sys.argv[i] =3D=3D "calls"): - perf_db_export_calls =3D True - elif (sys.argv[i] =3D=3D "callchains"): - perf_db_export_callchains =3D True - elif (sys.argv[i] =3D=3D "pyside-version-1"): - pass - else: - usage() - -output_dir_name =3D os.getcwd() + "/" + dbname + "-perf-data" -os.mkdir(output_dir_name) - -def do_query(q, s): - if (q.exec_(s)): - return - raise Exception("Query failed: " + q.lastError().text()) - -printdate("Creating database...") - -db =3D QSqlDatabase.addDatabase('QPSQL') -query =3D QSqlQuery(db) -db.setDatabaseName('postgres') -db.open() -try: - do_query(query, 'CREATE DATABASE ' + dbname) -except: - os.rmdir(output_dir_name) - raise -query.finish() -query.clear() -db.close() - -db.setDatabaseName(dbname) -db.open() - -query =3D QSqlQuery(db) -do_query(query, 'SET client_min_messages TO WARNING') - -do_query(query, 'CREATE TABLE selected_events (' - 'id bigint NOT NULL,' - 'name varchar(80))') -do_query(query, 'CREATE TABLE machines (' - 'id bigint NOT NULL,' - 'pid integer,' - 'root_dir varchar(4096))') -do_query(query, 'CREATE TABLE threads (' - 'id bigint NOT NULL,' - 'machine_id bigint,' - 'process_id bigint,' - 'pid integer,' - 'tid integer)') -do_query(query, 'CREATE TABLE comms (' - 'id bigint NOT NULL,' - 'comm varchar(16),' - 'c_thread_id bigint,' - 'c_time bigint,' - 'exec_flag boolean)') -do_query(query, 'CREATE TABLE comm_threads (' - 'id bigint NOT NULL,' - 'comm_id bigint,' - 'thread_id bigint)') -do_query(query, 'CREATE TABLE dsos (' - 'id bigint NOT NULL,' - 'machine_id bigint,' - 'short_name varchar(256),' - 'long_name varchar(4096),' - 'build_id varchar(64))') -do_query(query, 'CREATE TABLE symbols (' - 'id bigint NOT NULL,' - 'dso_id bigint,' - 'sym_start bigint,' - 'sym_end bigint,' - 'binding integer,' - 'name varchar(2048))') -do_query(query, 'CREATE TABLE branch_types (' - 'id integer NOT NULL,' - 'name varchar(80))') - -if branches: - do_query(query, 'CREATE TABLE samples (' - 'id bigint NOT NULL,' - 'evsel_id bigint,' - 'machine_id bigint,' - 'thread_id bigint,' - 'comm_id bigint,' - 'dso_id bigint,' - 'symbol_id bigint,' - 'sym_offset bigint,' - 'ip bigint,' - 'time bigint,' - 'cpu integer,' - 'to_dso_id bigint,' - 'to_symbol_id bigint,' - 'to_sym_offset bigint,' - 'to_ip bigint,' - 'branch_type integer,' - 'in_tx boolean,' - 'call_path_id bigint,' - 'insn_count bigint,' - 'cyc_count bigint,' - 'flags integer)') -else: - do_query(query, 'CREATE TABLE samples (' - 'id bigint NOT NULL,' - 'evsel_id bigint,' - 'machine_id bigint,' - 'thread_id bigint,' - 'comm_id bigint,' - 'dso_id bigint,' - 'symbol_id bigint,' - 'sym_offset bigint,' - 'ip bigint,' - 'time bigint,' - 'cpu integer,' - 'to_dso_id bigint,' - 'to_symbol_id bigint,' - 'to_sym_offset bigint,' - 'to_ip bigint,' - 'period bigint,' - 'weight bigint,' - 'transaction bigint,' - 'data_src bigint,' - 'branch_type integer,' - 'in_tx boolean,' - 'call_path_id bigint,' - 'insn_count bigint,' - 'cyc_count bigint,' - 'flags integer)') - -if perf_db_export_calls or perf_db_export_callchains: - do_query(query, 'CREATE TABLE call_paths (' - 'id bigint NOT NULL,' - 'parent_id bigint,' - 'symbol_id bigint,' - 'ip bigint)') -if perf_db_export_calls: - do_query(query, 'CREATE TABLE calls (' - 'id bigint NOT NULL,' - 'thread_id bigint,' - 'comm_id bigint,' - 'call_path_id bigint,' - 'call_time bigint,' - 'return_time bigint,' - 'branch_count bigint,' - 'call_id bigint,' - 'return_id bigint,' - 'parent_call_path_id bigint,' - 'flags integer,' - 'parent_id bigint,' - 'insn_count bigint,' - 'cyc_count bigint)') - -do_query(query, 'CREATE TABLE ptwrite (' - 'id bigint NOT NULL,' - 'payload bigint,' - 'exact_ip boolean)') - -do_query(query, 'CREATE TABLE cbr (' - 'id bigint NOT NULL,' - 'cbr integer,' - 'mhz integer,' - 'percent integer)') - -do_query(query, 'CREATE TABLE mwait (' - 'id bigint NOT NULL,' - 'hints integer,' - 'extensions integer)') - -do_query(query, 'CREATE TABLE pwre (' - 'id bigint NOT NULL,' - 'cstate integer,' - 'subcstate integer,' - 'hw boolean)') - -do_query(query, 'CREATE TABLE exstop (' - 'id bigint NOT NULL,' - 'exact_ip boolean)') - -do_query(query, 'CREATE TABLE pwrx (' - 'id bigint NOT NULL,' - 'deepest_cstate integer,' - 'last_cstate integer,' - 'wake_reason integer)') - -do_query(query, 'CREATE TABLE context_switches (' - 'id bigint NOT NULL,' - 'machine_id bigint,' - 'time bigint,' - 'cpu integer,' - 'thread_out_id bigint,' - 'comm_out_id bigint,' - 'thread_in_id bigint,' - 'comm_in_id bigint,' - 'flags integer)') - -do_query(query, 'CREATE VIEW machines_view AS ' - 'SELECT ' - 'id,' - 'pid,' - 'root_dir,' - 'CASE WHEN id=3D0 THEN \'unknown\' WHEN pid=3D-1 THEN \'host\' ELSE \'gu= est\' END AS host_or_guest' - ' FROM machines') - -do_query(query, 'CREATE VIEW dsos_view AS ' - 'SELECT ' - 'id,' - 'machine_id,' - '(SELECT host_or_guest FROM machines_view WHERE id =3D machine_id) AS ho= st_or_guest,' - 'short_name,' - 'long_name,' - 'build_id' - ' FROM dsos') - -do_query(query, 'CREATE VIEW symbols_view AS ' - 'SELECT ' - 'id,' - 'name,' - '(SELECT short_name FROM dsos WHERE id=3Ddso_id) AS dso,' - 'dso_id,' - 'sym_start,' - 'sym_end,' - 'CASE WHEN binding=3D0 THEN \'local\' WHEN binding=3D1 THEN \'global\' E= LSE \'weak\' END AS binding' - ' FROM symbols') - -do_query(query, 'CREATE VIEW threads_view AS ' - 'SELECT ' - 'id,' - 'machine_id,' - '(SELECT host_or_guest FROM machines_view WHERE id =3D machine_id) AS ho= st_or_guest,' - 'process_id,' - 'pid,' - 'tid' - ' FROM threads') - -do_query(query, 'CREATE VIEW comm_threads_view AS ' - 'SELECT ' - 'comm_id,' - '(SELECT comm FROM comms WHERE id =3D comm_id) AS command,' - 'thread_id,' - '(SELECT pid FROM threads WHERE id =3D thread_id) AS pid,' - '(SELECT tid FROM threads WHERE id =3D thread_id) AS tid' - ' FROM comm_threads') - -if perf_db_export_calls or perf_db_export_callchains: - do_query(query, 'CREATE VIEW call_paths_view AS ' - 'SELECT ' - 'c.id,' - 'to_hex(c.ip) AS ip,' - 'c.symbol_id,' - '(SELECT name FROM symbols WHERE id =3D c.symbol_id) AS symbol,' - '(SELECT dso_id FROM symbols WHERE id =3D c.symbol_id) AS dso_id,' - '(SELECT dso FROM symbols_view WHERE id =3D c.symbol_id) AS dso_short_= name,' - 'c.parent_id,' - 'to_hex(p.ip) AS parent_ip,' - 'p.symbol_id AS parent_symbol_id,' - '(SELECT name FROM symbols WHERE id =3D p.symbol_id) AS parent_symbol,' - '(SELECT dso_id FROM symbols WHERE id =3D p.symbol_id) AS parent_dso_id= ,' - '(SELECT dso FROM symbols_view WHERE id =3D p.symbol_id) AS parent_dso= _short_name' - ' FROM call_paths c INNER JOIN call_paths p ON p.id =3D c.parent_id') -if perf_db_export_calls: - do_query(query, 'CREATE VIEW calls_view AS ' - 'SELECT ' - 'calls.id,' - 'thread_id,' - '(SELECT pid FROM threads WHERE id =3D thread_id) AS pid,' - '(SELECT tid FROM threads WHERE id =3D thread_id) AS tid,' - '(SELECT comm FROM comms WHERE id =3D comm_id) AS command,' - 'call_path_id,' - 'to_hex(ip) AS ip,' - 'symbol_id,' - '(SELECT name FROM symbols WHERE id =3D symbol_id) AS symbol,' - 'call_time,' - 'return_time,' - 'return_time - call_time AS elapsed_time,' - 'branch_count,' - 'insn_count,' - 'cyc_count,' - 'CASE WHEN cyc_count=3D0 THEN CAST(0 AS NUMERIC(20, 2)) ELSE CAST((CAST= (insn_count AS FLOAT) / cyc_count) AS NUMERIC(20, 2)) END AS IPC,' - 'call_id,' - 'return_id,' - 'CASE WHEN flags=3D0 THEN \'\' WHEN flags=3D1 THEN \'no call\' WHEN fla= gs=3D2 THEN \'no return\' WHEN flags=3D3 THEN \'no call/return\' WHEN flags= =3D6 THEN \'jump\' ELSE CAST ( flags AS VARCHAR(6) ) END AS flags,' - 'parent_call_path_id,' - 'calls.parent_id' - ' FROM calls INNER JOIN call_paths ON call_paths.id =3D call_path_id') - -do_query(query, 'CREATE VIEW samples_view AS ' - 'SELECT ' - 'id,' - 'time,' - 'cpu,' - '(SELECT pid FROM threads WHERE id =3D thread_id) AS pid,' - '(SELECT tid FROM threads WHERE id =3D thread_id) AS tid,' - '(SELECT comm FROM comms WHERE id =3D comm_id) AS command,' - '(SELECT name FROM selected_events WHERE id =3D evsel_id) AS event,' - 'to_hex(ip) AS ip_hex,' - '(SELECT name FROM symbols WHERE id =3D symbol_id) AS symbol,' - 'sym_offset,' - '(SELECT short_name FROM dsos WHERE id =3D dso_id) AS dso_short_name,' - 'to_hex(to_ip) AS to_ip_hex,' - '(SELECT name FROM symbols WHERE id =3D to_symbol_id) AS to_symbol,' - 'to_sym_offset,' - '(SELECT short_name FROM dsos WHERE id =3D to_dso_id) AS to_dso_short_na= me,' - '(SELECT name FROM branch_types WHERE id =3D branch_type) AS branch_type= _name,' - 'in_tx,' - 'insn_count,' - 'cyc_count,' - 'CASE WHEN cyc_count=3D0 THEN CAST(0 AS NUMERIC(20, 2)) ELSE CAST((CAST(= insn_count AS FLOAT) / cyc_count) AS NUMERIC(20, 2)) END AS IPC,' - 'flags' - ' FROM samples') - -do_query(query, 'CREATE VIEW ptwrite_view AS ' - 'SELECT ' - 'ptwrite.id,' - 'time,' - 'cpu,' - 'to_hex(payload) AS payload_hex,' - 'CASE WHEN exact_ip=3DFALSE THEN \'False\' ELSE \'True\' END AS exact_ip' - ' FROM ptwrite' - ' INNER JOIN samples ON samples.id =3D ptwrite.id') - -do_query(query, 'CREATE VIEW cbr_view AS ' - 'SELECT ' - 'cbr.id,' - 'time,' - 'cpu,' - 'cbr,' - 'mhz,' - 'percent' - ' FROM cbr' - ' INNER JOIN samples ON samples.id =3D cbr.id') - -do_query(query, 'CREATE VIEW mwait_view AS ' - 'SELECT ' - 'mwait.id,' - 'time,' - 'cpu,' - 'to_hex(hints) AS hints_hex,' - 'to_hex(extensions) AS extensions_hex' - ' FROM mwait' - ' INNER JOIN samples ON samples.id =3D mwait.id') - -do_query(query, 'CREATE VIEW pwre_view AS ' - 'SELECT ' - 'pwre.id,' - 'time,' - 'cpu,' - 'cstate,' - 'subcstate,' - 'CASE WHEN hw=3DFALSE THEN \'False\' ELSE \'True\' END AS hw' - ' FROM pwre' - ' INNER JOIN samples ON samples.id =3D pwre.id') - -do_query(query, 'CREATE VIEW exstop_view AS ' - 'SELECT ' - 'exstop.id,' - 'time,' - 'cpu,' - 'CASE WHEN exact_ip=3DFALSE THEN \'False\' ELSE \'True\' END AS exact_ip' - ' FROM exstop' - ' INNER JOIN samples ON samples.id =3D exstop.id') - -do_query(query, 'CREATE VIEW pwrx_view AS ' - 'SELECT ' - 'pwrx.id,' - 'time,' - 'cpu,' - 'deepest_cstate,' - 'last_cstate,' - 'CASE WHEN wake_reason=3D1 THEN \'Interrupt\'' - ' WHEN wake_reason=3D2 THEN \'Timer Deadline\'' - ' WHEN wake_reason=3D4 THEN \'Monitored Address\'' - ' WHEN wake_reason=3D8 THEN \'HW\'' - ' ELSE CAST ( wake_reason AS VARCHAR(2) )' - 'END AS wake_reason' - ' FROM pwrx' - ' INNER JOIN samples ON samples.id =3D pwrx.id') - -do_query(query, 'CREATE VIEW power_events_view AS ' - 'SELECT ' - 'samples.id,' - 'samples.time,' - 'samples.cpu,' - 'selected_events.name AS event,' - 'FORMAT(\'%6s\', cbr.cbr) AS cbr,' - 'FORMAT(\'%6s\', cbr.mhz) AS MHz,' - 'FORMAT(\'%5s\', cbr.percent) AS percent,' - 'to_hex(mwait.hints) AS hints_hex,' - 'to_hex(mwait.extensions) AS extensions_hex,' - 'FORMAT(\'%3s\', pwre.cstate) AS cstate,' - 'FORMAT(\'%3s\', pwre.subcstate) AS subcstate,' - 'CASE WHEN pwre.hw=3DFALSE THEN \'False\' WHEN pwre.hw=3DTRUE THEN \'Tru= e\' ELSE NULL END AS hw,' - 'CASE WHEN exstop.exact_ip=3DFALSE THEN \'False\' WHEN exstop.exact_ip= =3DTRUE THEN \'True\' ELSE NULL END AS exact_ip,' - 'FORMAT(\'%3s\', pwrx.deepest_cstate) AS deepest_cstate,' - 'FORMAT(\'%3s\', pwrx.last_cstate) AS last_cstate,' - 'CASE WHEN pwrx.wake_reason=3D1 THEN \'Interrupt\'' - ' WHEN pwrx.wake_reason=3D2 THEN \'Timer Deadline\'' - ' WHEN pwrx.wake_reason=3D4 THEN \'Monitored Address\'' - ' WHEN pwrx.wake_reason=3D8 THEN \'HW\'' - ' ELSE FORMAT(\'%2s\', pwrx.wake_reason)' - 'END AS wake_reason' - ' FROM cbr' - ' FULL JOIN mwait ON mwait.id =3D cbr.id' - ' FULL JOIN pwre ON pwre.id =3D cbr.id' - ' FULL JOIN exstop ON exstop.id =3D cbr.id' - ' FULL JOIN pwrx ON pwrx.id =3D cbr.id' - ' INNER JOIN samples ON samples.id =3D coalesce(cbr.id, mwait.id, pwre.id= , exstop.id, pwrx.id)' - ' INNER JOIN selected_events ON selected_events.id =3D samples.evsel_id' - ' ORDER BY samples.id') - -do_query(query, 'CREATE VIEW context_switches_view AS ' - 'SELECT ' - 'context_switches.id,' - 'context_switches.machine_id,' - 'context_switches.time,' - 'context_switches.cpu,' - 'th_out.pid AS pid_out,' - 'th_out.tid AS tid_out,' - 'comm_out.comm AS comm_out,' - 'th_in.pid AS pid_in,' - 'th_in.tid AS tid_in,' - 'comm_in.comm AS comm_in,' - 'CASE WHEN context_switches.flags =3D 0 THEN \'in\'' - ' WHEN context_switches.flags =3D 1 THEN \'out\'' - ' WHEN context_switches.flags =3D 3 THEN \'out preempt\'' - ' ELSE CAST ( context_switches.flags AS VARCHAR(11) )' - 'END AS flags' - ' FROM context_switches' - ' INNER JOIN threads AS th_out ON th_out.id =3D context_switches.thread= _out_id' - ' INNER JOIN threads AS th_in ON th_in.id =3D context_switches.thread= _in_id' - ' INNER JOIN comms AS comm_out ON comm_out.id =3D context_switches.comm_o= ut_id' - ' INNER JOIN comms AS comm_in ON comm_in.id =3D context_switches.comm_i= n_id') - -file_header =3D struct.pack("!11sii", b"PGCOPY\n\377\r\n\0", 0, 0) -file_trailer =3D b"\377\377" - -def open_output_file(file_name): - path_name =3D output_dir_name + "/" + file_name - file =3D open(path_name, "wb+") - file.write(file_header) - return file - -def close_output_file(file): - file.write(file_trailer) - file.close() - -def copy_output_file_direct(file, table_name): - close_output_file(file) - sql =3D "COPY " + table_name + " FROM '" + file.name + "' (FORMAT 'binary= ')" - do_query(query, sql) - -# Use COPY FROM STDIN because security may prevent postgres from accessing= the files directly -def copy_output_file(file, table_name): - conn =3D PQconnectdb(toclientstr("dbname =3D " + dbname)) - if (PQstatus(conn)): - raise Exception("COPY FROM STDIN PQconnectdb failed") - file.write(file_trailer) - file.seek(0) - sql =3D "COPY " + table_name + " FROM STDIN (FORMAT 'binary')" - res =3D PQexec(conn, toclientstr(sql)) - if (PQresultStatus(res) !=3D 4): - raise Exception("COPY FROM STDIN PQexec failed") - data =3D file.read(65536) - while (len(data)): - ret =3D PQputCopyData(conn, data, len(data)) - if (ret !=3D 1): - raise Exception("COPY FROM STDIN PQputCopyData failed, error " + str(re= t)) - data =3D file.read(65536) - ret =3D PQputCopyEnd(conn, None) - if (ret !=3D 1): - raise Exception("COPY FROM STDIN PQputCopyEnd failed, error " + str(ret)) - PQfinish(conn) - -def remove_output_file(file): - name =3D file.name - file.close() - os.unlink(name) - -evsel_file =3D open_output_file("evsel_table.bin") -machine_file =3D open_output_file("machine_table.bin") -thread_file =3D open_output_file("thread_table.bin") -comm_file =3D open_output_file("comm_table.bin") -comm_thread_file =3D open_output_file("comm_thread_table.bin") -dso_file =3D open_output_file("dso_table.bin") -symbol_file =3D open_output_file("symbol_table.bin") -branch_type_file =3D open_output_file("branch_type_table.bin") -sample_file =3D open_output_file("sample_table.bin") -if perf_db_export_calls or perf_db_export_callchains: - call_path_file =3D open_output_file("call_path_table.bin") -if perf_db_export_calls: - call_file =3D open_output_file("call_table.bin") -ptwrite_file =3D open_output_file("ptwrite_table.bin") -cbr_file =3D open_output_file("cbr_table.bin") -mwait_file =3D open_output_file("mwait_table.bin") -pwre_file =3D open_output_file("pwre_table.bin") -exstop_file =3D open_output_file("exstop_table.bin") -pwrx_file =3D open_output_file("pwrx_table.bin") -context_switches_file =3D open_output_file("context_switches_table.bin") - -def trace_begin(): - printdate("Writing to intermediate files...") - # id =3D=3D 0 means unknown. It is easier to create records for them tha= n replace the zeroes with NULLs - evsel_table(0, "unknown") - machine_table(0, 0, "unknown") - thread_table(0, 0, 0, -1, -1) - comm_table(0, "unknown", 0, 0, 0) - dso_table(0, 0, "unknown", "unknown", "") - symbol_table(0, 0, 0, 0, 0, "unknown") - sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, = 0, 0, 0, 0, 0) - if perf_db_export_calls or perf_db_export_callchains: - call_path_table(0, 0, 0, 0) - call_return_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - -unhandled_count =3D 0 - -def is_table_empty(table_name): - do_query(query, 'SELECT * FROM ' + table_name + ' LIMIT 1'); - if query.next(): - return False - return True - -def drop(table_name): - do_query(query, 'DROP VIEW ' + table_name + '_view'); - do_query(query, 'DROP TABLE ' + table_name); - -def trace_end(): - printdate("Copying to database...") - copy_output_file(evsel_file, "selected_events") - copy_output_file(machine_file, "machines") - copy_output_file(thread_file, "threads") - copy_output_file(comm_file, "comms") - copy_output_file(comm_thread_file, "comm_threads") - copy_output_file(dso_file, "dsos") - copy_output_file(symbol_file, "symbols") - copy_output_file(branch_type_file, "branch_types") - copy_output_file(sample_file, "samples") - if perf_db_export_calls or perf_db_export_callchains: - copy_output_file(call_path_file, "call_paths") - if perf_db_export_calls: - copy_output_file(call_file, "calls") - copy_output_file(ptwrite_file, "ptwrite") - copy_output_file(cbr_file, "cbr") - copy_output_file(mwait_file, "mwait") - copy_output_file(pwre_file, "pwre") - copy_output_file(exstop_file, "exstop") - copy_output_file(pwrx_file, "pwrx") - copy_output_file(context_switches_file, "context_switches") - - printdate("Removing intermediate files...") - remove_output_file(evsel_file) - remove_output_file(machine_file) - remove_output_file(thread_file) - remove_output_file(comm_file) - remove_output_file(comm_thread_file) - remove_output_file(dso_file) - remove_output_file(symbol_file) - remove_output_file(branch_type_file) - remove_output_file(sample_file) - if perf_db_export_calls or perf_db_export_callchains: - remove_output_file(call_path_file) - if perf_db_export_calls: - remove_output_file(call_file) - remove_output_file(ptwrite_file) - remove_output_file(cbr_file) - remove_output_file(mwait_file) - remove_output_file(pwre_file) - remove_output_file(exstop_file) - remove_output_file(pwrx_file) - remove_output_file(context_switches_file) - os.rmdir(output_dir_name) - printdate("Adding primary keys") - do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)') - do_query(query, 'ALTER TABLE machines ADD PRIMARY KEY (id)') - do_query(query, 'ALTER TABLE threads ADD PRIMARY KEY (id)') - do_query(query, 'ALTER TABLE comms ADD PRIMARY KEY (id)') - do_query(query, 'ALTER TABLE comm_threads ADD PRIMARY KEY (id)') - do_query(query, 'ALTER TABLE dsos ADD PRIMARY KEY (id)') - do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)') - do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)') - do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)') - if perf_db_export_calls or perf_db_export_callchains: - do_query(query, 'ALTER TABLE call_paths ADD PRIMARY KEY (id)') - if perf_db_export_calls: - do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)') - do_query(query, 'ALTER TABLE ptwrite ADD PRIMARY KEY (id)') - do_query(query, 'ALTER TABLE cbr ADD PRIMARY KEY (id)') - do_query(query, 'ALTER TABLE mwait ADD PRIMARY KEY (id)') - do_query(query, 'ALTER TABLE pwre ADD PRIMARY KEY (id)') - do_query(query, 'ALTER TABLE exstop ADD PRIMARY KEY (id)') - do_query(query, 'ALTER TABLE pwrx ADD PRIMARY KEY (id)') - do_query(query, 'ALTER TABLE context_switches ADD PRIMARY KEY (id)') - - printdate("Adding foreign keys") - do_query(query, 'ALTER TABLE threads ' - 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES mach= ines (id),' - 'ADD CONSTRAINT processfk FOREIGN KEY (process_id) REFERENCES thre= ads (id)') - do_query(query, 'ALTER TABLE comms ' - 'ADD CONSTRAINT threadfk FOREIGN KEY (c_thread_id) REFERENCES thre= ads (id)') - do_query(query, 'ALTER TABLE comm_threads ' - 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comm= s (id),' - 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES thre= ads (id)') - do_query(query, 'ALTER TABLE dsos ' - 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES mach= ines (id)') - do_query(query, 'ALTER TABLE symbols ' - 'ADD CONSTRAINT dsofk FOREIGN KEY (dso_id) REFERENCES dsos= (id)') - do_query(query, 'ALTER TABLE samples ' - 'ADD CONSTRAINT evselfk FOREIGN KEY (evsel_id) REFERENCES sele= cted_events (id),' - 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES mach= ines (id),' - 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES thre= ads (id),' - 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comm= s (id),' - 'ADD CONSTRAINT dsofk FOREIGN KEY (dso_id) REFERENCES dsos= (id),' - 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symb= ols (id),' - 'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos= (id),' - 'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symb= ols (id)') - if perf_db_export_calls or perf_db_export_callchains: - do_query(query, 'ALTER TABLE call_paths ' - 'ADD CONSTRAINT parentfk FOREIGN KEY (parent_id) REFERENCES cal= l_paths (id),' - 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES sym= bols (id)') - if perf_db_export_calls: - do_query(query, 'ALTER TABLE calls ' - 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES thr= eads (id),' - 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES com= ms (id),' - 'ADD CONSTRAINT call_pathfk FOREIGN KEY (call_path_id) REFERENCES cal= l_paths (id),' - 'ADD CONSTRAINT callfk FOREIGN KEY (call_id) REFERENCES sam= ples (id),' - 'ADD CONSTRAINT returnfk FOREIGN KEY (return_id) REFERENCES sam= ples (id),' - 'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) = REFERENCES call_paths (id)') - do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)') - do_query(query, 'CREATE INDEX pid_idx ON calls (parent_id)') - do_query(query, 'ALTER TABLE comms ADD has_calls boolean') - do_query(query, 'UPDATE comms SET has_calls =3D TRUE WHERE comms.id IN (= SELECT DISTINCT comm_id FROM calls)') - do_query(query, 'ALTER TABLE ptwrite ' - 'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES sam= ples (id)') - do_query(query, 'ALTER TABLE cbr ' - 'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES sam= ples (id)') - do_query(query, 'ALTER TABLE mwait ' - 'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES sam= ples (id)') - do_query(query, 'ALTER TABLE pwre ' - 'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES sam= ples (id)') - do_query(query, 'ALTER TABLE exstop ' - 'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES sam= ples (id)') - do_query(query, 'ALTER TABLE pwrx ' - 'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES sam= ples (id)') - do_query(query, 'ALTER TABLE context_switches ' - 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES ma= chines (id),' - 'ADD CONSTRAINT toutfk FOREIGN KEY (thread_out_id) REFERENCES th= reads (id),' - 'ADD CONSTRAINT tinfk FOREIGN KEY (thread_in_id) REFERENCES th= reads (id),' - 'ADD CONSTRAINT coutfk FOREIGN KEY (comm_out_id) REFERENCES co= mms (id),' - 'ADD CONSTRAINT cinfk FOREIGN KEY (comm_in_id) REFERENCES co= mms (id)') - - printdate("Dropping unused tables") - if is_table_empty("ptwrite"): - drop("ptwrite") - if is_table_empty("mwait") and is_table_empty("pwre") and is_table_empty(= "exstop") and is_table_empty("pwrx"): - do_query(query, 'DROP VIEW power_events_view'); - drop("mwait") - drop("pwre") - drop("exstop") - drop("pwrx") - if is_table_empty("cbr"): - drop("cbr") - if is_table_empty("context_switches"): - drop("context_switches") - - if (unhandled_count): - printdate("Warning: ", unhandled_count, " unhandled events") - printdate("Done") - -def trace_unhandled(event_name, context, event_fields_dict): - global unhandled_count - unhandled_count +=3D 1 - -def sched__sched_switch(*x): - pass - -def evsel_table(evsel_id, evsel_name, *x): - evsel_name =3D toserverstr(evsel_name) - n =3D len(evsel_name) - fmt =3D "!hiqi" + str(n) + "s" - value =3D struct.pack(fmt, 2, 8, evsel_id, n, evsel_name) - evsel_file.write(value) - -def machine_table(machine_id, pid, root_dir, *x): - root_dir =3D toserverstr(root_dir) - n =3D len(root_dir) - fmt =3D "!hiqiii" + str(n) + "s" - value =3D struct.pack(fmt, 3, 8, machine_id, 4, pid, n, root_dir) - machine_file.write(value) - -def thread_table(thread_id, machine_id, process_id, pid, tid, *x): - value =3D struct.pack("!hiqiqiqiiii", 5, 8, thread_id, 8, machine_id, 8, = process_id, 4, pid, 4, tid) - thread_file.write(value) - -def comm_table(comm_id, comm_str, thread_id, time, exec_flag, *x): - comm_str =3D toserverstr(comm_str) - n =3D len(comm_str) - fmt =3D "!hiqi" + str(n) + "s" + "iqiqiB" - value =3D struct.pack(fmt, 5, 8, comm_id, n, comm_str, 8, thread_id, 8, t= ime, 1, exec_flag) - comm_file.write(value) - -def comm_thread_table(comm_thread_id, comm_id, thread_id, *x): - fmt =3D "!hiqiqiq" - value =3D struct.pack(fmt, 3, 8, comm_thread_id, 8, comm_id, 8, thread_id) - comm_thread_file.write(value) - -def dso_table(dso_id, machine_id, short_name, long_name, build_id, *x): - short_name =3D toserverstr(short_name) - long_name =3D toserverstr(long_name) - build_id =3D toserverstr(build_id) - n1 =3D len(short_name) - n2 =3D len(long_name) - n3 =3D len(build_id) - fmt =3D "!hiqiqi" + str(n1) + "si" + str(n2) + "si" + str(n3) + "s" - value =3D struct.pack(fmt, 5, 8, dso_id, 8, machine_id, n1, short_name, n= 2, long_name, n3, build_id) - dso_file.write(value) - -def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_na= me, *x): - symbol_name =3D toserverstr(symbol_name) - n =3D len(symbol_name) - fmt =3D "!hiqiqiqiqiii" + str(n) + "s" - value =3D struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, s= ym_end, 4, binding, n, symbol_name) - symbol_file.write(value) - -def branch_type_table(branch_type, name, *x): - name =3D toserverstr(name) - n =3D len(name) - fmt =3D "!hiii" + str(n) + "s" - value =3D struct.pack(fmt, 2, 4, branch_type, n, name) - branch_type_file.write(value) - -def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_= id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_o= ffset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, ca= ll_path_id, insn_cnt, cyc_cnt, flags, *x): - if branches: - value =3D struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiBiqiqiqii", 21= , 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, ds= o_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8,= to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx, 8, cal= l_path_id, 8, insn_cnt, 8, cyc_cnt, 4, flags) - else: - value =3D struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiBiqiqi= qii", 25, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_i= d, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_ds= o_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8,= transaction, 8, data_src, 4, branch_type, 1, in_tx, 8, call_path_id, 8, in= sn_cnt, 8, cyc_cnt, 4, flags) - sample_file.write(value) - -def call_path_table(cp_id, parent_id, symbol_id, ip, *x): - fmt =3D "!hiqiqiqiq" - value =3D struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip) - call_path_file.write(value) - -def call_return_table(cr_id, thread_id, comm_id, call_path_id, call_time, = return_time, branch_count, call_id, return_id, parent_call_path_id, flags, = parent_id, insn_cnt, cyc_cnt, *x): - fmt =3D "!hiqiqiqiqiqiqiqiqiqiqiiiqiqiq" - value =3D struct.pack(fmt, 14, 8, cr_id, 8, thread_id, 8, comm_id, 8, cal= l_path_id, 8, call_time, 8, return_time, 8, branch_count, 8, call_id, 8, re= turn_id, 8, parent_call_path_id, 4, flags, 8, parent_id, 8, insn_cnt, 8, cy= c_cnt) - call_file.write(value) - -def ptwrite(id, raw_buf): - data =3D struct.unpack_from("> 32) & 0x3 - value =3D struct.pack("!hiqiiii", 3, 8, id, 4, hints, 4, extensions) - mwait_file.write(value) - -def pwre(id, raw_buf): - data =3D struct.unpack_from("> 7) & 1 - cstate =3D (payload >> 12) & 0xf - subcstate =3D (payload >> 8) & 0xf - value =3D struct.pack("!hiqiiiiiB", 4, 8, id, 4, cstate, 4, subcstate, 1,= hw) - pwre_file.write(value) - -def exstop(id, raw_buf): - data =3D struct.unpack_from("> 4) & 0xf - wake_reason =3D (payload >> 8) & 0xf - value =3D struct.pack("!hiqiiiiii", 4, 8, id, 4, deepest_cstate, 4, last_= cstate, 4, wake_reason) - pwrx_file.write(value) - -def synth_data(id, config, raw_buf, *x): - if config =3D=3D 0: - ptwrite(id, raw_buf) - elif config =3D=3D 1: - mwait(id, raw_buf) - elif config =3D=3D 2: - pwre(id, raw_buf) - elif config =3D=3D 3: - exstop(id, raw_buf) - elif config =3D=3D 4: - pwrx(id, raw_buf) - elif config =3D=3D 5: - cbr(id, raw_buf) - -def context_switch_table(id, machine_id, time, cpu, thread_out_id, comm_ou= t_id, thread_in_id, comm_in_id, flags, *x): - fmt =3D "!hiqiqiqiiiqiqiqiqii" - value =3D struct.pack(fmt, 9, 8, id, 8, machine_id, 8, time, 4, cpu, 8, t= hread_out_id, 8, comm_out_id, 8, thread_in_id, 8, comm_in_id, 4, flags) - context_switches_file.write(value) diff --git a/tools/perf/scripts/python/export-to-sqlite.py b/tools/perf/scr= ipts/python/export-to-sqlite.py deleted file mode 100644 index 73c992feb1b9..000000000000 --- a/tools/perf/scripts/python/export-to-sqlite.py +++ /dev/null @@ -1,799 +0,0 @@ -# export-to-sqlite.py: export perf data to a sqlite3 database -# Copyright (c) 2017, Intel Corporation. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms and conditions of the GNU General Public License, -# version 2, as published by the Free Software Foundation. -# -# This program is distributed in the hope it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. - -from __future__ import print_function - -import os -import sys -import struct -import datetime - -# To use this script you will need to have installed package python-pyside= which -# provides LGPL-licensed Python bindings for Qt. You will also need the p= ackage -# libqt4-sql-sqlite for Qt sqlite3 support. -# -# Examples of installing pyside: -# -# ubuntu: -# -# $ sudo apt-get install python-pyside.qtsql libqt4-sql-psql -# -# Alternately, to use Python3 and/or pyside 2, one of the following: -# -# $ sudo apt-get install python3-pyside.qtsql libqt4-sql-psql -# $ sudo apt-get install python-pyside2.qtsql libqt5sql5-psql -# $ sudo apt-get install python3-pyside2.qtsql libqt5sql5-psql -# fedora: -# -# $ sudo yum install python-pyside -# -# Alternately, to use Python3 and/or pyside 2, one of the following: -# $ sudo yum install python3-pyside -# $ pip install --user PySide2 -# $ pip3 install --user PySide2 -# -# An example of using this script with Intel PT: -# -# $ perf record -e intel_pt//u ls -# $ perf script -s ~/libexec/perf-core/scripts/python/export-to-sqlite.py = pt_example branches calls -# 2017-07-31 14:26:07.326913 Creating database... -# 2017-07-31 14:26:07.538097 Writing records... -# 2017-07-31 14:26:09.889292 Adding indexes -# 2017-07-31 14:26:09.958746 Done -# -# To browse the database, sqlite3 can be used e.g. -# -# $ sqlite3 pt_example -# sqlite> .header on -# sqlite> select * from samples_view where id < 10; -# sqlite> .mode column -# sqlite> select * from samples_view where id < 10; -# sqlite> .tables -# sqlite> .schema samples_view -# sqlite> .quit -# -# An example of using the database is provided by the script -# exported-sql-viewer.py. Refer to that script for details. -# -# The database structure is practically the same as created by the script -# export-to-postgresql.py. Refer to that script for details. A notable -# difference is the 'transaction' column of the 'samples' table which is -# renamed 'transaction_' in sqlite because 'transaction' is a reserved wor= d. - -pyside_version_1 =3D True -if not "pyside-version-1" in sys.argv: - try: - from PySide2.QtSql import * - pyside_version_1 =3D False - except: - pass - -if pyside_version_1: - from PySide.QtSql import * - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -# These perf imports are not used at present -#from perf_trace_context import * -#from Core import * - -perf_db_export_mode =3D True -perf_db_export_calls =3D False -perf_db_export_callchains =3D False - -def printerr(*args, **keyword_args): - print(*args, file=3Dsys.stderr, **keyword_args) - -def printdate(*args, **kw_args): - print(datetime.datetime.today(), *args, sep=3D' ', **kw_args) - -def usage(): - printerr("Usage is: export-to-sqlite.py [] [] [] []"); - printerr("where: columns 'all' or 'branches'"); - printerr(" calls 'calls' =3D> create calls and call_p= aths table"); - printerr(" callchains 'callchains' =3D> create call_paths = table"); - printerr(" pyside-version-1 'pyside-version-1' =3D> use pyside v= ersion 1"); - raise Exception("Too few or bad arguments") - -if (len(sys.argv) < 2): - usage() - -dbname =3D sys.argv[1] - -if (len(sys.argv) >=3D 3): - columns =3D sys.argv[2] -else: - columns =3D "all" - -if columns not in ("all", "branches"): - usage() - -branches =3D (columns =3D=3D "branches") - -for i in range(3,len(sys.argv)): - if (sys.argv[i] =3D=3D "calls"): - perf_db_export_calls =3D True - elif (sys.argv[i] =3D=3D "callchains"): - perf_db_export_callchains =3D True - elif (sys.argv[i] =3D=3D "pyside-version-1"): - pass - else: - usage() - -def do_query(q, s): - if (q.exec_(s)): - return - raise Exception("Query failed: " + q.lastError().text()) - -def do_query_(q): - if (q.exec_()): - return - raise Exception("Query failed: " + q.lastError().text()) - -printdate("Creating database ...") - -db_exists =3D False -try: - f =3D open(dbname) - f.close() - db_exists =3D True -except: - pass - -if db_exists: - raise Exception(dbname + " already exists") - -db =3D QSqlDatabase.addDatabase('QSQLITE') -db.setDatabaseName(dbname) -db.open() - -query =3D QSqlQuery(db) - -do_query(query, 'PRAGMA journal_mode =3D OFF') -do_query(query, 'BEGIN TRANSACTION') - -do_query(query, 'CREATE TABLE selected_events (' - 'id integer NOT NULL PRIMARY KEY,' - 'name varchar(80))') -do_query(query, 'CREATE TABLE machines (' - 'id integer NOT NULL PRIMARY KEY,' - 'pid integer,' - 'root_dir varchar(4096))') -do_query(query, 'CREATE TABLE threads (' - 'id integer NOT NULL PRIMARY KEY,' - 'machine_id bigint,' - 'process_id bigint,' - 'pid integer,' - 'tid integer)') -do_query(query, 'CREATE TABLE comms (' - 'id integer NOT NULL PRIMARY KEY,' - 'comm varchar(16),' - 'c_thread_id bigint,' - 'c_time bigint,' - 'exec_flag boolean)') -do_query(query, 'CREATE TABLE comm_threads (' - 'id integer NOT NULL PRIMARY KEY,' - 'comm_id bigint,' - 'thread_id bigint)') -do_query(query, 'CREATE TABLE dsos (' - 'id integer NOT NULL PRIMARY KEY,' - 'machine_id bigint,' - 'short_name varchar(256),' - 'long_name varchar(4096),' - 'build_id varchar(64))') -do_query(query, 'CREATE TABLE symbols (' - 'id integer NOT NULL PRIMARY KEY,' - 'dso_id bigint,' - 'sym_start bigint,' - 'sym_end bigint,' - 'binding integer,' - 'name varchar(2048))') -do_query(query, 'CREATE TABLE branch_types (' - 'id integer NOT NULL PRIMARY KEY,' - 'name varchar(80))') - -if branches: - do_query(query, 'CREATE TABLE samples (' - 'id integer NOT NULL PRIMARY KEY,' - 'evsel_id bigint,' - 'machine_id bigint,' - 'thread_id bigint,' - 'comm_id bigint,' - 'dso_id bigint,' - 'symbol_id bigint,' - 'sym_offset bigint,' - 'ip bigint,' - 'time bigint,' - 'cpu integer,' - 'to_dso_id bigint,' - 'to_symbol_id bigint,' - 'to_sym_offset bigint,' - 'to_ip bigint,' - 'branch_type integer,' - 'in_tx boolean,' - 'call_path_id bigint,' - 'insn_count bigint,' - 'cyc_count bigint,' - 'flags integer)') -else: - do_query(query, 'CREATE TABLE samples (' - 'id integer NOT NULL PRIMARY KEY,' - 'evsel_id bigint,' - 'machine_id bigint,' - 'thread_id bigint,' - 'comm_id bigint,' - 'dso_id bigint,' - 'symbol_id bigint,' - 'sym_offset bigint,' - 'ip bigint,' - 'time bigint,' - 'cpu integer,' - 'to_dso_id bigint,' - 'to_symbol_id bigint,' - 'to_sym_offset bigint,' - 'to_ip bigint,' - 'period bigint,' - 'weight bigint,' - 'transaction_ bigint,' - 'data_src bigint,' - 'branch_type integer,' - 'in_tx boolean,' - 'call_path_id bigint,' - 'insn_count bigint,' - 'cyc_count bigint,' - 'flags integer)') - -if perf_db_export_calls or perf_db_export_callchains: - do_query(query, 'CREATE TABLE call_paths (' - 'id integer NOT NULL PRIMARY KEY,' - 'parent_id bigint,' - 'symbol_id bigint,' - 'ip bigint)') -if perf_db_export_calls: - do_query(query, 'CREATE TABLE calls (' - 'id integer NOT NULL PRIMARY KEY,' - 'thread_id bigint,' - 'comm_id bigint,' - 'call_path_id bigint,' - 'call_time bigint,' - 'return_time bigint,' - 'branch_count bigint,' - 'call_id bigint,' - 'return_id bigint,' - 'parent_call_path_id bigint,' - 'flags integer,' - 'parent_id bigint,' - 'insn_count bigint,' - 'cyc_count bigint)') - -do_query(query, 'CREATE TABLE ptwrite (' - 'id integer NOT NULL PRIMARY KEY,' - 'payload bigint,' - 'exact_ip integer)') - -do_query(query, 'CREATE TABLE cbr (' - 'id integer NOT NULL PRIMARY KEY,' - 'cbr integer,' - 'mhz integer,' - 'percent integer)') - -do_query(query, 'CREATE TABLE mwait (' - 'id integer NOT NULL PRIMARY KEY,' - 'hints integer,' - 'extensions integer)') - -do_query(query, 'CREATE TABLE pwre (' - 'id integer NOT NULL PRIMARY KEY,' - 'cstate integer,' - 'subcstate integer,' - 'hw integer)') - -do_query(query, 'CREATE TABLE exstop (' - 'id integer NOT NULL PRIMARY KEY,' - 'exact_ip integer)') - -do_query(query, 'CREATE TABLE pwrx (' - 'id integer NOT NULL PRIMARY KEY,' - 'deepest_cstate integer,' - 'last_cstate integer,' - 'wake_reason integer)') - -do_query(query, 'CREATE TABLE context_switches (' - 'id integer NOT NULL PRIMARY KEY,' - 'machine_id bigint,' - 'time bigint,' - 'cpu integer,' - 'thread_out_id bigint,' - 'comm_out_id bigint,' - 'thread_in_id bigint,' - 'comm_in_id bigint,' - 'flags integer)') - -# printf was added to sqlite in version 3.8.3 -sqlite_has_printf =3D False -try: - do_query(query, 'SELECT printf("") FROM machines') - sqlite_has_printf =3D True -except: - pass - -def emit_to_hex(x): - if sqlite_has_printf: - return 'printf("%x", ' + x + ')' - else: - return x - -do_query(query, 'CREATE VIEW machines_view AS ' - 'SELECT ' - 'id,' - 'pid,' - 'root_dir,' - 'CASE WHEN id=3D0 THEN \'unknown\' WHEN pid=3D-1 THEN \'host\' ELSE \'gu= est\' END AS host_or_guest' - ' FROM machines') - -do_query(query, 'CREATE VIEW dsos_view AS ' - 'SELECT ' - 'id,' - 'machine_id,' - '(SELECT host_or_guest FROM machines_view WHERE id =3D machine_id) AS ho= st_or_guest,' - 'short_name,' - 'long_name,' - 'build_id' - ' FROM dsos') - -do_query(query, 'CREATE VIEW symbols_view AS ' - 'SELECT ' - 'id,' - 'name,' - '(SELECT short_name FROM dsos WHERE id=3Ddso_id) AS dso,' - 'dso_id,' - 'sym_start,' - 'sym_end,' - 'CASE WHEN binding=3D0 THEN \'local\' WHEN binding=3D1 THEN \'global\' E= LSE \'weak\' END AS binding' - ' FROM symbols') - -do_query(query, 'CREATE VIEW threads_view AS ' - 'SELECT ' - 'id,' - 'machine_id,' - '(SELECT host_or_guest FROM machines_view WHERE id =3D machine_id) AS ho= st_or_guest,' - 'process_id,' - 'pid,' - 'tid' - ' FROM threads') - -do_query(query, 'CREATE VIEW comm_threads_view AS ' - 'SELECT ' - 'comm_id,' - '(SELECT comm FROM comms WHERE id =3D comm_id) AS command,' - 'thread_id,' - '(SELECT pid FROM threads WHERE id =3D thread_id) AS pid,' - '(SELECT tid FROM threads WHERE id =3D thread_id) AS tid' - ' FROM comm_threads') - -if perf_db_export_calls or perf_db_export_callchains: - do_query(query, 'CREATE VIEW call_paths_view AS ' - 'SELECT ' - 'c.id,' - + emit_to_hex('c.ip') + ' AS ip,' - 'c.symbol_id,' - '(SELECT name FROM symbols WHERE id =3D c.symbol_id) AS symbol,' - '(SELECT dso_id FROM symbols WHERE id =3D c.symbol_id) AS dso_id,' - '(SELECT dso FROM symbols_view WHERE id =3D c.symbol_id) AS dso_short_= name,' - 'c.parent_id,' - + emit_to_hex('p.ip') + ' AS parent_ip,' - 'p.symbol_id AS parent_symbol_id,' - '(SELECT name FROM symbols WHERE id =3D p.symbol_id) AS parent_symbol,' - '(SELECT dso_id FROM symbols WHERE id =3D p.symbol_id) AS parent_dso_id= ,' - '(SELECT dso FROM symbols_view WHERE id =3D p.symbol_id) AS parent_dso= _short_name' - ' FROM call_paths c INNER JOIN call_paths p ON p.id =3D c.parent_id') -if perf_db_export_calls: - do_query(query, 'CREATE VIEW calls_view AS ' - 'SELECT ' - 'calls.id,' - 'thread_id,' - '(SELECT pid FROM threads WHERE id =3D thread_id) AS pid,' - '(SELECT tid FROM threads WHERE id =3D thread_id) AS tid,' - '(SELECT comm FROM comms WHERE id =3D comm_id) AS command,' - 'call_path_id,' - + emit_to_hex('ip') + ' AS ip,' - 'symbol_id,' - '(SELECT name FROM symbols WHERE id =3D symbol_id) AS symbol,' - 'call_time,' - 'return_time,' - 'return_time - call_time AS elapsed_time,' - 'branch_count,' - 'insn_count,' - 'cyc_count,' - 'CASE WHEN cyc_count=3D0 THEN CAST(0 AS FLOAT) ELSE ROUND(CAST(insn_cou= nt AS FLOAT) / cyc_count, 2) END AS IPC,' - 'call_id,' - 'return_id,' - 'CASE WHEN flags=3D0 THEN \'\' WHEN flags=3D1 THEN \'no call\' WHEN fla= gs=3D2 THEN \'no return\' WHEN flags=3D3 THEN \'no call/return\' WHEN flags= =3D6 THEN \'jump\' ELSE flags END AS flags,' - 'parent_call_path_id,' - 'calls.parent_id' - ' FROM calls INNER JOIN call_paths ON call_paths.id =3D call_path_id') - -do_query(query, 'CREATE VIEW samples_view AS ' - 'SELECT ' - 'id,' - 'time,' - 'cpu,' - '(SELECT pid FROM threads WHERE id =3D thread_id) AS pid,' - '(SELECT tid FROM threads WHERE id =3D thread_id) AS tid,' - '(SELECT comm FROM comms WHERE id =3D comm_id) AS command,' - '(SELECT name FROM selected_events WHERE id =3D evsel_id) AS event,' - + emit_to_hex('ip') + ' AS ip_hex,' - '(SELECT name FROM symbols WHERE id =3D symbol_id) AS symbol,' - 'sym_offset,' - '(SELECT short_name FROM dsos WHERE id =3D dso_id) AS dso_short_name,' - + emit_to_hex('to_ip') + ' AS to_ip_hex,' - '(SELECT name FROM symbols WHERE id =3D to_symbol_id) AS to_symbol,' - 'to_sym_offset,' - '(SELECT short_name FROM dsos WHERE id =3D to_dso_id) AS to_dso_short_na= me,' - '(SELECT name FROM branch_types WHERE id =3D branch_type) AS branch_type= _name,' - 'in_tx,' - 'insn_count,' - 'cyc_count,' - 'CASE WHEN cyc_count=3D0 THEN CAST(0 AS FLOAT) ELSE ROUND(CAST(insn_coun= t AS FLOAT) / cyc_count, 2) END AS IPC,' - 'flags' - ' FROM samples') - -do_query(query, 'CREATE VIEW ptwrite_view AS ' - 'SELECT ' - 'ptwrite.id,' - 'time,' - 'cpu,' - + emit_to_hex('payload') + ' AS payload_hex,' - 'CASE WHEN exact_ip=3D0 THEN \'False\' ELSE \'True\' END AS exact_ip' - ' FROM ptwrite' - ' INNER JOIN samples ON samples.id =3D ptwrite.id') - -do_query(query, 'CREATE VIEW cbr_view AS ' - 'SELECT ' - 'cbr.id,' - 'time,' - 'cpu,' - 'cbr,' - 'mhz,' - 'percent' - ' FROM cbr' - ' INNER JOIN samples ON samples.id =3D cbr.id') - -do_query(query, 'CREATE VIEW mwait_view AS ' - 'SELECT ' - 'mwait.id,' - 'time,' - 'cpu,' - + emit_to_hex('hints') + ' AS hints_hex,' - + emit_to_hex('extensions') + ' AS extensions_hex' - ' FROM mwait' - ' INNER JOIN samples ON samples.id =3D mwait.id') - -do_query(query, 'CREATE VIEW pwre_view AS ' - 'SELECT ' - 'pwre.id,' - 'time,' - 'cpu,' - 'cstate,' - 'subcstate,' - 'CASE WHEN hw=3D0 THEN \'False\' ELSE \'True\' END AS hw' - ' FROM pwre' - ' INNER JOIN samples ON samples.id =3D pwre.id') - -do_query(query, 'CREATE VIEW exstop_view AS ' - 'SELECT ' - 'exstop.id,' - 'time,' - 'cpu,' - 'CASE WHEN exact_ip=3D0 THEN \'False\' ELSE \'True\' END AS exact_ip' - ' FROM exstop' - ' INNER JOIN samples ON samples.id =3D exstop.id') - -do_query(query, 'CREATE VIEW pwrx_view AS ' - 'SELECT ' - 'pwrx.id,' - 'time,' - 'cpu,' - 'deepest_cstate,' - 'last_cstate,' - 'CASE WHEN wake_reason=3D1 THEN \'Interrupt\'' - ' WHEN wake_reason=3D2 THEN \'Timer Deadline\'' - ' WHEN wake_reason=3D4 THEN \'Monitored Address\'' - ' WHEN wake_reason=3D8 THEN \'HW\'' - ' ELSE wake_reason ' - 'END AS wake_reason' - ' FROM pwrx' - ' INNER JOIN samples ON samples.id =3D pwrx.id') - -do_query(query, 'CREATE VIEW power_events_view AS ' - 'SELECT ' - 'samples.id,' - 'time,' - 'cpu,' - 'selected_events.name AS event,' - 'CASE WHEN selected_events.name=3D\'cbr\' THEN (SELECT cbr FROM cbr WHER= E cbr.id =3D samples.id) ELSE "" END AS cbr,' - 'CASE WHEN selected_events.name=3D\'cbr\' THEN (SELECT mhz FROM cbr WHER= E cbr.id =3D samples.id) ELSE "" END AS mhz,' - 'CASE WHEN selected_events.name=3D\'cbr\' THEN (SELECT percent FROM cbr = WHERE cbr.id =3D samples.id) ELSE "" END AS percent,' - 'CASE WHEN selected_events.name=3D\'mwait\' THEN (SELECT ' + emit_to_hex= ('hints') + ' FROM mwait WHERE mwait.id =3D samples.id) ELSE "" END AS hint= s_hex,' - 'CASE WHEN selected_events.name=3D\'mwait\' THEN (SELECT ' + emit_to_hex= ('extensions') + ' FROM mwait WHERE mwait.id =3D samples.id) ELSE "" END AS= extensions_hex,' - 'CASE WHEN selected_events.name=3D\'pwre\' THEN (SELECT cstate FROM pwre= WHERE pwre.id =3D samples.id) ELSE "" END AS cstate,' - 'CASE WHEN selected_events.name=3D\'pwre\' THEN (SELECT subcstate FROM p= wre WHERE pwre.id =3D samples.id) ELSE "" END AS subcstate,' - 'CASE WHEN selected_events.name=3D\'pwre\' THEN (SELECT hw FROM pwre WHE= RE pwre.id =3D samples.id) ELSE "" END AS hw,' - 'CASE WHEN selected_events.name=3D\'exstop\' THEN (SELECT exact_ip FROM = exstop WHERE exstop.id =3D samples.id) ELSE "" END AS exact_ip,' - 'CASE WHEN selected_events.name=3D\'pwrx\' THEN (SELECT deepest_cstate F= ROM pwrx WHERE pwrx.id =3D samples.id) ELSE "" END AS deepest_cstate,' - 'CASE WHEN selected_events.name=3D\'pwrx\' THEN (SELECT last_cstate FROM= pwrx WHERE pwrx.id =3D samples.id) ELSE "" END AS last_cstate,' - 'CASE WHEN selected_events.name=3D\'pwrx\' THEN (SELECT ' - 'CASE WHEN wake_reason=3D1 THEN \'Interrupt\'' - ' WHEN wake_reason=3D2 THEN \'Timer Deadline\'' - ' WHEN wake_reason=3D4 THEN \'Monitored Address\'' - ' WHEN wake_reason=3D8 THEN \'HW\'' - ' ELSE wake_reason ' - 'END' - ' FROM pwrx WHERE pwrx.id =3D samples.id) ELSE "" END AS wake_reason' - ' FROM samples' - ' INNER JOIN selected_events ON selected_events.id =3D evsel_id' - ' WHERE selected_events.name IN (\'cbr\',\'mwait\',\'exstop\',\'pwre\',\'= pwrx\')') - -do_query(query, 'CREATE VIEW context_switches_view AS ' - 'SELECT ' - 'context_switches.id,' - 'context_switches.machine_id,' - 'context_switches.time,' - 'context_switches.cpu,' - 'th_out.pid AS pid_out,' - 'th_out.tid AS tid_out,' - 'comm_out.comm AS comm_out,' - 'th_in.pid AS pid_in,' - 'th_in.tid AS tid_in,' - 'comm_in.comm AS comm_in,' - 'CASE WHEN context_switches.flags =3D 0 THEN \'in\'' - ' WHEN context_switches.flags =3D 1 THEN \'out\'' - ' WHEN context_switches.flags =3D 3 THEN \'out preempt\'' - ' ELSE context_switches.flags ' - 'END AS flags' - ' FROM context_switches' - ' INNER JOIN threads AS th_out ON th_out.id =3D context_switches.thread= _out_id' - ' INNER JOIN threads AS th_in ON th_in.id =3D context_switches.thread= _in_id' - ' INNER JOIN comms AS comm_out ON comm_out.id =3D context_switches.comm_o= ut_id' - ' INNER JOIN comms AS comm_in ON comm_in.id =3D context_switches.comm_i= n_id') - -do_query(query, 'END TRANSACTION') - -evsel_query =3D QSqlQuery(db) -evsel_query.prepare("INSERT INTO selected_events VALUES (?, ?)") -machine_query =3D QSqlQuery(db) -machine_query.prepare("INSERT INTO machines VALUES (?, ?, ?)") -thread_query =3D QSqlQuery(db) -thread_query.prepare("INSERT INTO threads VALUES (?, ?, ?, ?, ?)") -comm_query =3D QSqlQuery(db) -comm_query.prepare("INSERT INTO comms VALUES (?, ?, ?, ?, ?)") -comm_thread_query =3D QSqlQuery(db) -comm_thread_query.prepare("INSERT INTO comm_threads VALUES (?, ?, ?)") -dso_query =3D QSqlQuery(db) -dso_query.prepare("INSERT INTO dsos VALUES (?, ?, ?, ?, ?)") -symbol_query =3D QSqlQuery(db) -symbol_query.prepare("INSERT INTO symbols VALUES (?, ?, ?, ?, ?, ?)") -branch_type_query =3D QSqlQuery(db) -branch_type_query.prepare("INSERT INTO branch_types VALUES (?, ?)") -sample_query =3D QSqlQuery(db) -if branches: - sample_query.prepare("INSERT INTO samples VALUES (?, ?, ?, ?, ?, ?, ?, ?,= ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") -else: - sample_query.prepare("INSERT INTO samples VALUES (?, ?, ?, ?, ?, ?, ?, ?,= ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") -if perf_db_export_calls or perf_db_export_callchains: - call_path_query =3D QSqlQuery(db) - call_path_query.prepare("INSERT INTO call_paths VALUES (?, ?, ?, ?)") -if perf_db_export_calls: - call_query =3D QSqlQuery(db) - call_query.prepare("INSERT INTO calls VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, = ?, ?, ?, ?, ?)") -ptwrite_query =3D QSqlQuery(db) -ptwrite_query.prepare("INSERT INTO ptwrite VALUES (?, ?, ?)") -cbr_query =3D QSqlQuery(db) -cbr_query.prepare("INSERT INTO cbr VALUES (?, ?, ?, ?)") -mwait_query =3D QSqlQuery(db) -mwait_query.prepare("INSERT INTO mwait VALUES (?, ?, ?)") -pwre_query =3D QSqlQuery(db) -pwre_query.prepare("INSERT INTO pwre VALUES (?, ?, ?, ?)") -exstop_query =3D QSqlQuery(db) -exstop_query.prepare("INSERT INTO exstop VALUES (?, ?)") -pwrx_query =3D QSqlQuery(db) -pwrx_query.prepare("INSERT INTO pwrx VALUES (?, ?, ?, ?)") -context_switch_query =3D QSqlQuery(db) -context_switch_query.prepare("INSERT INTO context_switches VALUES (?, ?, ?= , ?, ?, ?, ?, ?, ?)") - -def trace_begin(): - printdate("Writing records...") - do_query(query, 'BEGIN TRANSACTION') - # id =3D=3D 0 means unknown. It is easier to create records for them tha= n replace the zeroes with NULLs - evsel_table(0, "unknown") - machine_table(0, 0, "unknown") - thread_table(0, 0, 0, -1, -1) - comm_table(0, "unknown", 0, 0, 0) - dso_table(0, 0, "unknown", "unknown", "") - symbol_table(0, 0, 0, 0, 0, "unknown") - sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, = 0, 0, 0, 0, 0) - if perf_db_export_calls or perf_db_export_callchains: - call_path_table(0, 0, 0, 0) - call_return_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - -unhandled_count =3D 0 - -def is_table_empty(table_name): - do_query(query, 'SELECT * FROM ' + table_name + ' LIMIT 1'); - if query.next(): - return False - return True - -def drop(table_name): - do_query(query, 'DROP VIEW ' + table_name + '_view'); - do_query(query, 'DROP TABLE ' + table_name); - -def trace_end(): - do_query(query, 'END TRANSACTION') - - printdate("Adding indexes") - if perf_db_export_calls: - do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)') - do_query(query, 'CREATE INDEX pid_idx ON calls (parent_id)') - do_query(query, 'ALTER TABLE comms ADD has_calls boolean') - do_query(query, 'UPDATE comms SET has_calls =3D 1 WHERE comms.id IN (SEL= ECT DISTINCT comm_id FROM calls)') - - printdate("Dropping unused tables") - if is_table_empty("ptwrite"): - drop("ptwrite") - if is_table_empty("mwait") and is_table_empty("pwre") and is_table_empty(= "exstop") and is_table_empty("pwrx"): - do_query(query, 'DROP VIEW power_events_view'); - drop("mwait") - drop("pwre") - drop("exstop") - drop("pwrx") - if is_table_empty("cbr"): - drop("cbr") - if is_table_empty("context_switches"): - drop("context_switches") - - if (unhandled_count): - printdate("Warning: ", unhandled_count, " unhandled events") - printdate("Done") - -def trace_unhandled(event_name, context, event_fields_dict): - global unhandled_count - unhandled_count +=3D 1 - -def sched__sched_switch(*x): - pass - -def bind_exec(q, n, x): - for xx in x[0:n]: - q.addBindValue(str(xx)) - do_query_(q) - -def evsel_table(*x): - bind_exec(evsel_query, 2, x) - -def machine_table(*x): - bind_exec(machine_query, 3, x) - -def thread_table(*x): - bind_exec(thread_query, 5, x) - -def comm_table(*x): - bind_exec(comm_query, 5, x) - -def comm_thread_table(*x): - bind_exec(comm_thread_query, 3, x) - -def dso_table(*x): - bind_exec(dso_query, 5, x) - -def symbol_table(*x): - bind_exec(symbol_query, 6, x) - -def branch_type_table(*x): - bind_exec(branch_type_query, 2, x) - -def sample_table(*x): - if branches: - for xx in x[0:15]: - sample_query.addBindValue(str(xx)) - for xx in x[19:25]: - sample_query.addBindValue(str(xx)) - do_query_(sample_query) - else: - bind_exec(sample_query, 25, x) - -def call_path_table(*x): - bind_exec(call_path_query, 4, x) - -def call_return_table(*x): - bind_exec(call_query, 14, x) - -def ptwrite(id, raw_buf): - data =3D struct.unpack_from("> 32) & 0x3 - mwait_query.addBindValue(str(id)) - mwait_query.addBindValue(str(hints)) - mwait_query.addBindValue(str(extensions)) - do_query_(mwait_query) - -def pwre(id, raw_buf): - data =3D struct.unpack_from("> 7) & 1 - cstate =3D (payload >> 12) & 0xf - subcstate =3D (payload >> 8) & 0xf - pwre_query.addBindValue(str(id)) - pwre_query.addBindValue(str(cstate)) - pwre_query.addBindValue(str(subcstate)) - pwre_query.addBindValue(str(hw)) - do_query_(pwre_query) - -def exstop(id, raw_buf): - data =3D struct.unpack_from("> 4) & 0xf - wake_reason =3D (payload >> 8) & 0xf - pwrx_query.addBindValue(str(id)) - pwrx_query.addBindValue(str(deepest_cstate)) - pwrx_query.addBindValue(str(last_cstate)) - pwrx_query.addBindValue(str(wake_reason)) - do_query_(pwrx_query) - -def synth_data(id, config, raw_buf, *x): - if config =3D=3D 0: - ptwrite(id, raw_buf) - elif config =3D=3D 1: - mwait(id, raw_buf) - elif config =3D=3D 2: - pwre(id, raw_buf) - elif config =3D=3D 3: - exstop(id, raw_buf) - elif config =3D=3D 4: - pwrx(id, raw_buf) - elif config =3D=3D 5: - cbr(id, raw_buf) - -def context_switch_table(*x): - bind_exec(context_switch_query, 9, x) diff --git a/tools/perf/scripts/python/failed-syscalls-by-pid.py b/tools/pe= rf/scripts/python/failed-syscalls-by-pid.py deleted file mode 100644 index 310efe5e7e23..000000000000 --- a/tools/perf/scripts/python/failed-syscalls-by-pid.py +++ /dev/null @@ -1,79 +0,0 @@ -# failed system call counts, by pid -# (c) 2010, Tom Zanussi -# Licensed under the terms of the GNU GPL License version 2 -# -# Displays system-wide failed system call totals, broken down by pid. -# If a [comm] arg is specified, only syscalls called by [comm] are display= ed. - -from __future__ import print_function - -import os -import sys - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -from perf_trace_context import * -from Core import * -from Util import * - -usage =3D "perf script -s syscall-counts-by-pid.py [comm|pid]\n"; - -for_comm =3D None -for_pid =3D None - -if len(sys.argv) > 2: - sys.exit(usage) - -if len(sys.argv) > 1: - try: - for_pid =3D int(sys.argv[1]) - except: - for_comm =3D sys.argv[1] - -syscalls =3D autodict() - -def trace_begin(): - print("Press control+C to stop and show the summary") - -def trace_end(): - print_error_totals() - -def raw_syscalls__sys_exit(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, id, ret): - if (for_comm and common_comm !=3D for_comm) or \ - (for_pid and common_pid !=3D for_pid ): - return - - if ret < 0: - try: - syscalls[common_comm][common_pid][id][ret] +=3D 1 - except TypeError: - syscalls[common_comm][common_pid][id][ret] =3D 1 - -def syscalls__sys_exit(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - id, ret): - raw_syscalls__sys_exit(**locals()) - -def print_error_totals(): - if for_comm is not None: - print("\nsyscall errors for %s:\n" % (for_comm)) - else: - print("\nsyscall errors:\n") - - print("%-30s %10s" % ("comm [pid]", "count")) - print("%-30s %10s" % ("------------------------------", "----------")) - - comm_keys =3D syscalls.keys() - for comm in comm_keys: - pid_keys =3D syscalls[comm].keys() - for pid in pid_keys: - print("\n%s [%d]" % (comm, pid)) - id_keys =3D syscalls[comm][pid].keys() - for id in id_keys: - print(" syscall: %-16s" % syscall_name(id)) - ret_keys =3D syscalls[comm][pid][id].keys() - for ret, val in sorted(syscalls[comm][pid][id].items(), key =3D lambda= kv: (kv[1], kv[0]), reverse =3D True): - print(" err =3D %-20s %10d" % (strerror(ret), val)) diff --git a/tools/perf/scripts/python/flamegraph.py b/tools/perf/scripts/p= ython/flamegraph.py deleted file mode 100755 index ad735990c5be..000000000000 --- a/tools/perf/scripts/python/flamegraph.py +++ /dev/null @@ -1,267 +0,0 @@ -# flamegraph.py - create flame graphs from perf samples -# SPDX-License-Identifier: GPL-2.0 -# -# Usage: -# -# perf record -a -g -F 99 sleep 60 -# perf script report flamegraph -# -# Combined: -# -# perf script flamegraph -a -F 99 sleep 60 -# -# Written by Andreas Gerstmayr -# Flame Graphs invented by Brendan Gregg -# Works in tandem with d3-flame-graph by Martin Spier -# -# pylint: disable=3Dmissing-module-docstring -# pylint: disable=3Dmissing-class-docstring -# pylint: disable=3Dmissing-function-docstring - -import argparse -import hashlib -import io -import json -import os -import subprocess -import sys -from typing import Dict, Optional, Union -import urllib.request - -MINIMAL_HTML =3D """ - - - -
- - - -""" - -# pylint: disable=3Dtoo-few-public-methods -class Node: - def __init__(self, name: str, libtype: str): - self.name =3D name - # "root" | "kernel" | "" - # "" indicates user space - self.libtype =3D libtype - self.value: int =3D 0 - self.children: list[Node] =3D [] - - def to_json(self) -> Dict[str, Union[str, int, list[Dict]]]: - return { - "n": self.name, - "l": self.libtype, - "v": self.value, - "c": [x.to_json() for x in self.children] - } - - -class FlameGraphCLI: - def __init__(self, args): - self.args =3D args - self.stack =3D Node("all", "root") - - @staticmethod - def get_libtype_from_dso(dso: Optional[str]) -> str: - """ - when kernel-debuginfo is installed, - dso points to /usr/lib/debug/lib/modules/*/vmlinux - """ - if dso and (dso =3D=3D "[kernel.kallsyms]" or dso.endswith("/vmlin= ux")): - return "kernel" - - return "" - - @staticmethod - def find_or_create_node(node: Node, name: str, libtype: str) -> Node: - for child in node.children: - if child.name =3D=3D name: - return child - - child =3D Node(name, libtype) - node.children.append(child) - return child - - def process_event(self, event) -> None: - # ignore events where the event name does not match - # the one specified by the user - if self.args.event_name and event.get("ev_name") !=3D self.args.ev= ent_name: - return - - pid =3D event.get("sample", {}).get("pid", 0) - # event["dso"] sometimes contains /usr/lib/debug/lib/modules/*/vml= inux - # for user-space processes; let's use pid for kernel or user-space= distinction - if pid =3D=3D 0: - comm =3D event["comm"] - libtype =3D "kernel" - else: - comm =3D f"{event['comm']} ({pid})" - libtype =3D "" - node =3D self.find_or_create_node(self.stack, comm, libtype) - - if "callchain" in event: - for entry in reversed(event["callchain"]): - name =3D entry.get("sym", {}).get("name", "[unknown]") - libtype =3D self.get_libtype_from_dso(entry.get("dso")) - node =3D self.find_or_create_node(node, name, libtype) - else: - name =3D event.get("symbol", "[unknown]") - libtype =3D self.get_libtype_from_dso(event.get("dso")) - node =3D self.find_or_create_node(node, name, libtype) - node.value +=3D 1 - - def get_report_header(self) -> str: - if self.args.input =3D=3D "-": - # when this script is invoked with "perf script flamegraph", - # no perf.data is created and we cannot read the header of it - return "" - - try: - # if the file name other than perf.data is given, - # we read the header of that file - if self.args.input: - output =3D subprocess.check_output(["perf", "report", "--h= eader-only", - "-i", self.args.input]) - else: - output =3D subprocess.check_output(["perf", "report", "--h= eader-only"]) - - result =3D output.decode("utf-8") - if self.args.event_name: - result +=3D "\nFocused event: " + self.args.event_name - return result - except Exception as err: # pylint: disable=3Dbroad-except - print(f"Error reading report header: {err}", file=3Dsys.stderr) - return "" - - def trace_end(self) -> None: - stacks_json =3D json.dumps(self.stack, default=3Dlambda x: x.to_js= on()) - - if self.args.format =3D=3D "html": - report_header =3D self.get_report_header() - options =3D { - "colorscheme": self.args.colorscheme, - "context": report_header - } - options_json =3D json.dumps(options) - - template_md5sum =3D None - if self.args.format =3D=3D "html": - if os.path.isfile(self.args.template): - template =3D f"file://{self.args.template}" - else: - if not self.args.allow_download: - print(f"""Warning: Flame Graph template '{self.arg= s.template}' -does not exist. To avoid this please install a package such as the -js-d3-flame-graph or libjs-d3-flame-graph, specify an existing flame -graph template (--template PATH) or use another output format (--format -FORMAT).""", - file=3Dsys.stderr) - if self.args.input =3D=3D "-": - print( -"""Not attempting to download Flame Graph template as script command line -input is disabled due to using live mode. If you want to download the -template retry without live mode. For example, use 'perf record -a -g --F 99 sleep 60' and 'perf script report flamegraph'. Alternatively, -download the template from: -https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/d3-flameg= raph-base.html -and place it at: -/usr/share/d3-flame-graph/d3-flamegraph-base.html""", - file=3Dsys.stderr) - sys.exit(1) - s =3D None - while s not in ["y", "n"]: - s =3D input("Do you wish to download a templat= e from cdn.jsdelivr.net?" + - "(this warning can be suppressed wit= h --allow-download) [yn] " - ).lower() - if s =3D=3D "n": - sys.exit(1) - template =3D ("https://cdn.jsdelivr.net/npm/d3-flame-g= raph@4.1.3/dist/templates/" - "d3-flamegraph-base.html") - template_md5sum =3D "143e0d06ba69b8370b9848dcd6ae3f36" - - try: - with urllib.request.urlopen(template) as url_template: - output_str =3D "".join([ - l.decode("utf-8") for l in url_template.readlines() - ]) - except Exception as err: - print(f"Error reading template {template}: {err}\n" - "a minimal flame graph will be generated", file=3Dsy= s.stderr) - output_str =3D MINIMAL_HTML - template_md5sum =3D None - - if template_md5sum: - download_md5sum =3D hashlib.md5(output_str.encode("utf-8")= ).hexdigest() - if download_md5sum !=3D template_md5sum: - s =3D None - while s not in ["y", "n"]: - s =3D input(f"""Unexpected template md5sum. -{download_md5sum} !=3D {template_md5sum}, for: -{output_str} -continue?[yn] """).lower() - if s =3D=3D "n": - sys.exit(1) - - output_str =3D output_str.replace("/** @options_json **/", opt= ions_json) - output_str =3D output_str.replace("/** @flamegraph_json **/", = stacks_json) - - output_fn =3D self.args.output or "flamegraph.html" - else: - output_str =3D stacks_json - output_fn =3D self.args.output or "stacks.json" - - if output_fn =3D=3D "-": - with io.open(sys.stdout.fileno(), "w", encoding=3D"utf-8", clo= sefd=3DFalse) as out: - out.write(output_str) - else: - print(f"dumping data to {output_fn}") - try: - with io.open(output_fn, "w", encoding=3D"utf-8") as out: - out.write(output_str) - except IOError as err: - print(f"Error writing output file: {err}", file=3Dsys.stde= rr) - sys.exit(1) - - -if __name__ =3D=3D "__main__": - parser =3D argparse.ArgumentParser(description=3D"Create flame graphs.= ") - parser.add_argument("-f", "--format", - default=3D"html", choices=3D["json", "html"], - help=3D"output file format") - parser.add_argument("-o", "--output", - help=3D"output file name") - parser.add_argument("--template", - default=3D"/usr/share/d3-flame-graph/d3-flamegraph= -base.html", - help=3D"path to flame graph HTML template") - parser.add_argument("--colorscheme", - default=3D"blue-green", - help=3D"flame graph color scheme", - choices=3D["blue-green", "orange"]) - parser.add_argument("-i", "--input", - help=3Dargparse.SUPPRESS) - parser.add_argument("--allow-download", - default=3DFalse, - action=3D"store_true", - help=3D"allow unprompted downloading of HTML templ= ate") - parser.add_argument("-e", "--event", - default=3D"", - dest=3D"event_name", - type=3Dstr, - help=3D"specify the event to generate flamegraph f= or") - - cli_args =3D parser.parse_args() - cli =3D FlameGraphCLI(cli_args) - - process_event =3D cli.process_event - trace_end =3D cli.trace_end diff --git a/tools/perf/scripts/python/futex-contention.py b/tools/perf/scr= ipts/python/futex-contention.py deleted file mode 100644 index 7e884d46f920..000000000000 --- a/tools/perf/scripts/python/futex-contention.py +++ /dev/null @@ -1,57 +0,0 @@ -# futex contention -# (c) 2010, Arnaldo Carvalho de Melo -# Licensed under the terms of the GNU GPL License version 2 -# -# Translation of: -# -# http://sourceware.org/systemtap/wiki/WSFutexContention -# -# to perf python scripting. -# -# Measures futex contention - -from __future__ import print_function - -import os -import sys -sys.path.append(os.environ['PERF_EXEC_PATH'] + - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') -from Util import * - -process_names =3D {} -thread_thislock =3D {} -thread_blocktime =3D {} - -lock_waits =3D {} # long-lived stats on (tid,lock) blockage elapsed time -process_names =3D {} # long-lived pid-to-execname mapping - - -def syscalls__sys_enter_futex(event, ctxt, cpu, s, ns, tid, comm, callchai= n, - nr, uaddr, op, val, utime, uaddr2, val3): - cmd =3D op & FUTEX_CMD_MASK - if cmd !=3D FUTEX_WAIT: - return # we don't care about originators of WAKE events - - process_names[tid] =3D comm - thread_thislock[tid] =3D uaddr - thread_blocktime[tid] =3D nsecs(s, ns) - - -def syscalls__sys_exit_futex(event, ctxt, cpu, s, ns, tid, comm, callchain, - nr, ret): - if tid in thread_blocktime: - elapsed =3D nsecs(s, ns) - thread_blocktime[tid] - add_stats(lock_waits, (tid, thread_thislock[tid]), elapsed) - del thread_blocktime[tid] - del thread_thislock[tid] - - -def trace_begin(): - print("Press control+C to stop and show the summary") - - -def trace_end(): - for (tid, lock) in lock_waits: - min, max, avg, count =3D lock_waits[tid, lock] - print("%s[%d] lock %x contended %d times, %d avg ns [max: %d ns, m= in %d ns]" % - (process_names[tid], tid, lock, count, avg, max, min)) diff --git a/tools/perf/scripts/python/gecko.py b/tools/perf/scripts/python= /gecko.py deleted file mode 100644 index bc5a72f94bfa..000000000000 --- a/tools/perf/scripts/python/gecko.py +++ /dev/null @@ -1,395 +0,0 @@ -# gecko.py - Convert perf record output to Firefox's gecko profile format -# SPDX-License-Identifier: GPL-2.0 -# -# The script converts perf.data to Gecko Profile Format, -# which can be read by https://profiler.firefox.com/. -# -# Usage: -# -# perf record -a -g -F 99 sleep 60 -# perf script report gecko -# -# Combined: -# -# perf script gecko -F 99 -a sleep 60 - -import os -import sys -import time -import json -import string -import random -import argparse -import threading -import webbrowser -import urllib.parse -from os import system -from functools import reduce -from dataclasses import dataclass, field -from http.server import HTTPServer, SimpleHTTPRequestHandler, test -from typing import List, Dict, Optional, NamedTuple, Set, Tuple, Any - -# Add the Perf-Trace-Util library to the Python path -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -from perf_trace_context import * -from Core import * - -StringID =3D int -StackID =3D int -FrameID =3D int -CategoryID =3D int -Milliseconds =3D float - -# start_time is intialiazed only once for the all event traces. -start_time =3D None - -# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7= 457fee1d66cd4e2737/src/types/profile.js#L425 -# Follow Brendan Gregg's Flamegraph convention: orange for kernel and yell= ow for user space by default. -CATEGORIES =3D None - -# The product name is used by the profiler UI to show the Operating system= and Processor. -PRODUCT =3D os.popen('uname -op').read().strip() - -# store the output file -output_file =3D None - -# Here key =3D tid, value =3D Thread -tid_to_thread =3D dict() - -# The HTTP server is used to serve the profile to the profiler UI. -http_server_thread =3D None - -# The category index is used by the profiler UI to show the color of the f= lame graph. -USER_CATEGORY_INDEX =3D 0 -KERNEL_CATEGORY_INDEX =3D 1 - -# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7= 457fee1d66cd4e2737/src/types/gecko-profile.js#L156 -class Frame(NamedTuple): - string_id: StringID - relevantForJS: bool - innerWindowID: int - implementation: None - optimizations: None - line: None - column: None - category: CategoryID - subcategory: int - -# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7= 457fee1d66cd4e2737/src/types/gecko-profile.js#L216 -class Stack(NamedTuple): - prefix_id: Optional[StackID] - frame_id: FrameID - -# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7= 457fee1d66cd4e2737/src/types/gecko-profile.js#L90 -class Sample(NamedTuple): - stack_id: Optional[StackID] - time_ms: Milliseconds - responsiveness: int - -@dataclass -class Thread: - """A builder for a profile of the thread. - - Attributes: - comm: Thread command-line (name). - pid: process ID of containing process. - tid: thread ID. - samples: Timeline of profile samples. - frameTable: interned stack frame ID -> stack frame. - stringTable: interned string ID -> string. - stringMap: interned string -> string ID. - stackTable: interned stack ID -> stack. - stackMap: (stack prefix ID, leaf stack frame ID) -> interned Stack ID. - frameMap: Stack Frame string -> interned Frame ID. - comm: str - pid: int - tid: int - samples: List[Sample] =3D field(default_factory=3Dlist) - frameTable: List[Frame] =3D field(default_factory=3Dlist) - stringTable: List[str] =3D field(default_factory=3Dlist) - stringMap: Dict[str, int] =3D field(default_factory=3Ddict) - stackTable: List[Stack] =3D field(default_factory=3Dlist) - stackMap: Dict[Tuple[Optional[int], int], int] =3D field(default_factory= =3Ddict) - frameMap: Dict[str, int] =3D field(default_factory=3Ddict) - """ - comm: str - pid: int - tid: int - samples: List[Sample] =3D field(default_factory=3Dlist) - frameTable: List[Frame] =3D field(default_factory=3Dlist) - stringTable: List[str] =3D field(default_factory=3Dlist) - stringMap: Dict[str, int] =3D field(default_factory=3Ddict) - stackTable: List[Stack] =3D field(default_factory=3Dlist) - stackMap: Dict[Tuple[Optional[int], int], int] =3D field(default_factory= =3Ddict) - frameMap: Dict[str, int] =3D field(default_factory=3Ddict) - - def _intern_stack(self, frame_id: int, prefix_id: Optional[int]) -> int: - """Gets a matching stack, or saves the new stack. Returns a Stack ID.""" - key =3D f"{frame_id}" if prefix_id is None else f"{frame_id},{prefix_id}" - # key =3D (prefix_id, frame_id) - stack_id =3D self.stackMap.get(key) - if stack_id is None: - # return stack_id - stack_id =3D len(self.stackTable) - self.stackTable.append(Stack(prefix_id=3Dprefix_id, frame_id=3Dframe_id= )) - self.stackMap[key] =3D stack_id - return stack_id - - def _intern_string(self, string: str) -> int: - """Gets a matching string, or saves the new string. Returns a String ID.= """ - string_id =3D self.stringMap.get(string) - if string_id is not None: - return string_id - string_id =3D len(self.stringTable) - self.stringTable.append(string) - self.stringMap[string] =3D string_id - return string_id - - def _intern_frame(self, frame_str: str) -> int: - """Gets a matching stack frame, or saves the new frame. Returns a Frame = ID.""" - frame_id =3D self.frameMap.get(frame_str) - if frame_id is not None: - return frame_id - frame_id =3D len(self.frameTable) - self.frameMap[frame_str] =3D frame_id - string_id =3D self._intern_string(frame_str) - - symbol_name_to_category =3D KERNEL_CATEGORY_INDEX if frame_str.find('kal= lsyms') !=3D -1 \ - or frame_str.find('/vmlinux') !=3D -1 \ - or frame_str.endswith('.ko)') \ - else USER_CATEGORY_INDEX - - self.frameTable.append(Frame( - string_id=3Dstring_id, - relevantForJS=3DFalse, - innerWindowID=3D0, - implementation=3DNone, - optimizations=3DNone, - line=3DNone, - column=3DNone, - category=3Dsymbol_name_to_category, - subcategory=3DNone, - )) - return frame_id - - def _add_sample(self, comm: str, stack: List[str], time_ms: Milliseconds)= -> None: - """Add a timestamped stack trace sample to the thread builder. - Args: - comm: command-line (name) of the thread at this sample - stack: sampled stack frames. Root first, leaf last. - time_ms: timestamp of sample in milliseconds. - """ - # Ihreads may not set their names right after they are created. - # Instead, they might do it later. In such situations, to use the latest= name they have set. - if self.comm !=3D comm: - self.comm =3D comm - - prefix_stack_id =3D reduce(lambda prefix_id, frame: self._intern_stack - (self._intern_frame(frame), prefix_id), stack, None) - if prefix_stack_id is not None: - self.samples.append(Sample(stack_id=3Dprefix_stack_id, - time_ms=3Dtime_ms, - responsiveness=3D0)) - - def _to_json_dict(self) -> Dict: - """Converts current Thread to GeckoThread JSON format.""" - # Gecko profile format is row-oriented data as List[List], - # And a schema for interpreting each index. - # Schema: - # https://github.com/firefox-devtools/profiler/blob/main/docs-developer/= gecko-profile-format.md - # https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26= d7457fee1d66cd4e2737/src/types/gecko-profile.js#L230 - return { - "tid": self.tid, - "pid": self.pid, - "name": self.comm, - # https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e2= 6d7457fee1d66cd4e2737/src/types/gecko-profile.js#L51 - "markers": { - "schema": { - "name": 0, - "startTime": 1, - "endTime": 2, - "phase": 3, - "category": 4, - "data": 5, - }, - "data": [], - }, - - # https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e2= 6d7457fee1d66cd4e2737/src/types/gecko-profile.js#L90 - "samples": { - "schema": { - "stack": 0, - "time": 1, - "responsiveness": 2, - }, - "data": self.samples - }, - - # https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e2= 6d7457fee1d66cd4e2737/src/types/gecko-profile.js#L156 - "frameTable": { - "schema": { - "location": 0, - "relevantForJS": 1, - "innerWindowID": 2, - "implementation": 3, - "optimizations": 4, - "line": 5, - "column": 6, - "category": 7, - "subcategory": 8, - }, - "data": self.frameTable, - }, - - # https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e2= 6d7457fee1d66cd4e2737/src/types/gecko-profile.js#L216 - "stackTable": { - "schema": { - "prefix": 0, - "frame": 1, - }, - "data": self.stackTable, - }, - "stringTable": self.stringTable, - "registerTime": 0, - "unregisterTime": None, - "processType": "default", - } - -# Uses perf script python interface to parse each -# event and store the data in the thread builder. -def process_event(param_dict: Dict) -> None: - global start_time - global tid_to_thread - time_stamp =3D (param_dict['sample']['time'] // 1000) / 1000 - pid =3D param_dict['sample']['pid'] - tid =3D param_dict['sample']['tid'] - comm =3D param_dict['comm'] - - # Start time is the time of the first sample - if not start_time: - start_time =3D time_stamp - - # Parse and append the callchain of the current sample into a stack. - stack =3D [] - if param_dict['callchain']: - for call in param_dict['callchain']: - if 'sym' not in call: - continue - stack.append(f'{call["sym"]["name"]} (in {call["dso"]})') - if len(stack) !=3D 0: - # Reverse the stack, as root come first and the leaf at the end. - stack =3D stack[::-1] - - # During perf record if -g is not used, the callchain is not available. - # In that case, the symbol and dso are available in the event parameters. - else: - func =3D param_dict['symbol'] if 'symbol' in param_dict else '[unknown]' - dso =3D param_dict['dso'] if 'dso' in param_dict else '[unknown]' - stack.append(f'{func} (in {dso})') - - # Add sample to the specific thread. - thread =3D tid_to_thread.get(tid) - if thread is None: - thread =3D Thread(comm=3Dcomm, pid=3Dpid, tid=3Dtid) - tid_to_thread[tid] =3D thread - thread._add_sample(comm=3Dcomm, stack=3Dstack, time_ms=3Dtime_stamp) - -def trace_begin() -> None: - global output_file - if (output_file is None): - print("Staring Firefox Profiler on your default browser...") - global http_server_thread - http_server_thread =3D threading.Thread(target=3Dtest, args=3D(CORSReque= stHandler, HTTPServer,)) - http_server_thread.daemon =3D True - http_server_thread.start() - -# Trace_end runs at the end and will be used to aggregate -# the data into the final json object and print it out to stdout. -def trace_end() -> None: - global output_file - threads =3D [thread._to_json_dict() for thread in tid_to_thread.values()] - - # Schema: https://github.com/firefox-devtools/profiler/blob/53970305b51b9= b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L305 - gecko_profile_with_meta =3D { - "meta": { - "interval": 1, - "processType": 0, - "product": PRODUCT, - "stackwalk": 1, - "debug": 0, - "gcpoison": 0, - "asyncstack": 1, - "startTime": start_time, - "shutdownTime": None, - "version": 24, - "presymbolicated": True, - "categories": CATEGORIES, - "markerSchema": [], - }, - "libs": [], - "threads": threads, - "processes": [], - "pausedRanges": [], - } - # launch the profiler on local host if not specified --save-only args, ot= herwise print to file - if (output_file is None): - output_file =3D 'gecko_profile.json' - with open(output_file, 'w') as f: - json.dump(gecko_profile_with_meta, f, indent=3D2) - launchFirefox(output_file) - time.sleep(1) - print(f'[ perf gecko: Captured and wrote into {output_file} ]') - else: - print(f'[ perf gecko: Captured and wrote into {output_file} ]') - with open(output_file, 'w') as f: - json.dump(gecko_profile_with_meta, f, indent=3D2) - -# Used to enable Cross-Origin Resource Sharing (CORS) for requests coming = from 'https://profiler.firefox.com', allowing it to access resources from t= his server. -class CORSRequestHandler(SimpleHTTPRequestHandler): - def end_headers (self): - self.send_header('Access-Control-Allow-Origin', 'https://profiler.firefo= x.com') - SimpleHTTPRequestHandler.end_headers(self) - -# start a local server to serve the gecko_profile.json file to the profile= r.firefox.com -def launchFirefox(file): - safe_string =3D urllib.parse.quote_plus(f'http://localhost:8000/{file}') - url =3D 'https://profiler.firefox.com/from-url/' + safe_string - webbrowser.open(f'{url}') - -def main() -> None: - global output_file - global CATEGORIES - parser =3D argparse.ArgumentParser(description=3D"Convert perf.data to Fi= refox\'s Gecko Profile format which can be uploaded to profiler.firefox.com= for visualization") - - # Add the command-line options - # Colors must be defined according to this: - # https://github.com/firefox-devtools/profiler/blob/50124adbfa488adba6e26= 74a8f2618cf34b59cd2/res/css/categories.css - parser.add_argument('--user-color', default=3D'yellow', help=3D'Color for= the User category', choices=3D['yellow', 'blue', 'purple', 'green', 'orang= e', 'red', 'grey', 'magenta']) - parser.add_argument('--kernel-color', default=3D'orange', help=3D'Color f= or the Kernel category', choices=3D['yellow', 'blue', 'purple', 'green', 'o= range', 'red', 'grey', 'magenta']) - # If --save-only is specified, the output will be saved to a file instead= of opening Firefox's profiler directly. - parser.add_argument('--save-only', help=3D'Save the output to a file inst= ead of opening Firefox\'s profiler') - - # Parse the command-line arguments - args =3D parser.parse_args() - # Access the values provided by the user - user_color =3D args.user_color - kernel_color =3D args.kernel_color - output_file =3D args.save_only - - CATEGORIES =3D [ - { - "name": 'User', - "color": user_color, - "subcategories": ['Other'] - }, - { - "name": 'Kernel', - "color": kernel_color, - "subcategories": ['Other'] - }, - ] - -if __name__ =3D=3D '__main__': - main() diff --git a/tools/perf/scripts/python/intel-pt-events.py b/tools/perf/scri= pts/python/intel-pt-events.py deleted file mode 100644 index 346c89bd16d6..000000000000 --- a/tools/perf/scripts/python/intel-pt-events.py +++ /dev/null @@ -1,494 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# intel-pt-events.py: Print Intel PT Events including Power Events and PTW= RITE -# Copyright (c) 2017-2021, Intel Corporation. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms and conditions of the GNU General Public License, -# version 2, as published by the Free Software Foundation. -# -# This program is distributed in the hope it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. - -from __future__ import division, print_function - -import io -import os -import sys -import struct -import argparse -import contextlib - -from libxed import LibXED -from ctypes import create_string_buffer, addressof - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -from perf_trace_context import perf_set_itrace_options, \ - perf_sample_insn, perf_sample_srccode - -try: - broken_pipe_exception =3D BrokenPipeError -except: - broken_pipe_exception =3D IOError - -glb_switch_str =3D {} -glb_insn =3D False -glb_disassembler =3D None -glb_src =3D False -glb_source_file_name =3D None -glb_line_number =3D None -glb_dso =3D None -glb_stash_dict =3D {} -glb_output =3D None -glb_output_pos =3D 0 -glb_cpu =3D -1 -glb_time =3D 0 - -def get_optional_null(perf_dict, field): - if field in perf_dict: - return perf_dict[field] - return "" - -def get_optional_zero(perf_dict, field): - if field in perf_dict: - return perf_dict[field] - return 0 - -def get_optional_bytes(perf_dict, field): - if field in perf_dict: - return perf_dict[field] - return bytes() - -def get_optional(perf_dict, field): - if field in perf_dict: - return perf_dict[field] - return "[unknown]" - -def get_offset(perf_dict, field): - if field in perf_dict: - return "+%#x" % perf_dict[field] - return "" - -def trace_begin(): - ap =3D argparse.ArgumentParser(usage =3D "", add_help =3D False) - ap.add_argument("--insn-trace", action=3D'store_true') - ap.add_argument("--src-trace", action=3D'store_true') - ap.add_argument("--all-switch-events", action=3D'store_true') - ap.add_argument("--interleave", type=3Dint, nargs=3D'?', const=3D4, defau= lt=3D0) - global glb_args - global glb_insn - global glb_src - glb_args =3D ap.parse_args() - if glb_args.insn_trace: - print("Intel PT Instruction Trace") - itrace =3D "i0nsepwxI" - glb_insn =3D True - elif glb_args.src_trace: - print("Intel PT Source Trace") - itrace =3D "i0nsepwxI" - glb_insn =3D True - glb_src =3D True - else: - print("Intel PT Branch Trace, Power Events, Event Trace and PTWRITE") - itrace =3D "bepwxI" - global glb_disassembler - try: - glb_disassembler =3D LibXED() - except: - glb_disassembler =3D None - perf_set_itrace_options(perf_script_context, itrace) - -def trace_end(): - if glb_args.interleave: - flush_stashed_output() - print("End") - -def trace_unhandled(event_name, context, event_fields_dict): - print(' '.join(['%s=3D%s'%(k,str(v))for k,v in sorted(event_fields_dict.= items())])) - -def stash_output(): - global glb_stash_dict - global glb_output_pos - output_str =3D glb_output.getvalue()[glb_output_pos:] - n =3D len(output_str) - if n: - glb_output_pos +=3D n - if glb_cpu not in glb_stash_dict: - glb_stash_dict[glb_cpu] =3D [] - glb_stash_dict[glb_cpu].append(output_str) - -def flush_stashed_output(): - global glb_stash_dict - while glb_stash_dict: - cpus =3D list(glb_stash_dict.keys()) - # Output at most glb_args.interleave output strings per cpu - for cpu in cpus: - items =3D glb_stash_dict[cpu] - countdown =3D glb_args.interleave - while len(items) and countdown: - sys.stdout.write(items[0]) - del items[0] - countdown -=3D 1 - if not items: - del glb_stash_dict[cpu] - -def print_ptwrite(raw_buf): - data =3D struct.unpack_from("> 32) & 0x3 - print("hints: %#x extensions: %#x" % (hints, extensions), end=3D' ') - -def print_pwre(raw_buf): - data =3D struct.unpack_from("> 7) & 1 - cstate =3D (payload >> 12) & 0xf - subcstate =3D (payload >> 8) & 0xf - print("hw: %u cstate: %u sub-cstate: %u" % (hw, cstate, subcstate), - end=3D' ') - -def print_exstop(raw_buf): - data =3D struct.unpack_from("> 4) & 0xf - wake_reason =3D (payload >> 8) & 0xf - print("deepest cstate: %u last cstate: %u wake reason: %#x" % - (deepest_cstate, last_cstate, wake_reason), end=3D' ') - -def print_psb(raw_buf): - data =3D struct.unpack_from("> 7 - vector =3D data[1] - evd_cnt =3D data[2] - s =3D glb_cfe[typ] - if s: - print(" cfe: %s IP: %u vector: %u" % (s, ip_flag, vector), end=3D' ') - else: - print(" cfe: %u IP: %u vector: %u" % (typ, ip_flag, vector), end=3D' ') - pos =3D 4 - for i in range(evd_cnt): - data =3D struct.unpack_from("%u %s branch" % (old_iflag, iflag, s), end=3D' ') - -def common_start_str(comm, sample): - ts =3D sample["time"] - cpu =3D sample["cpu"] - pid =3D sample["pid"] - tid =3D sample["tid"] - if "machine_pid" in sample: - machine_pid =3D sample["machine_pid"] - vcpu =3D sample["vcpu"] - return "VM:%5d VCPU:%03d %16s %5u/%-5u [%03u] %9u.%09u " % (machine_pid= , vcpu, comm, pid, tid, cpu, ts / 1000000000, ts %1000000000) - else: - return "%16s %5u/%-5u [%03u] %9u.%09u " % (comm, pid, tid, cpu, ts / 10= 00000000, ts %1000000000) - -def print_common_start(comm, sample, name): - flags_disp =3D get_optional_null(sample, "flags_disp") - # Unused fields: - # period =3D sample["period"] - # phys_addr =3D sample["phys_addr"] - # weight =3D sample["weight"] - # transaction =3D sample["transaction"] - # cpumode =3D get_optional_zero(sample, "cpumode") - print(common_start_str(comm, sample) + "%8s %21s" % (name, flags_disp), = end=3D' ') - -def print_instructions_start(comm, sample): - if "x" in get_optional_null(sample, "flags"): - print(common_start_str(comm, sample) + "x", end=3D' ') - else: - print(common_start_str(comm, sample), end=3D' ') - -def disassem(insn, ip): - inst =3D glb_disassembler.Instruction() - glb_disassembler.SetMode(inst, 0) # Assume 64-bit - buf =3D create_string_buffer(64) - buf.value =3D insn - return glb_disassembler.DisassembleOne(inst, addressof(buf), len(insn), i= p) - -def print_common_ip(param_dict, sample, symbol, dso): - ip =3D sample["ip"] - offs =3D get_offset(param_dict, "symoff") - if "cyc_cnt" in sample: - cyc_cnt =3D sample["cyc_cnt"] - insn_cnt =3D get_optional_zero(sample, "insn_cnt") - ipc_str =3D " IPC: %#.2f (%u/%u)" % (insn_cnt / cyc_cnt, insn_cnt, cyc_= cnt) - else: - ipc_str =3D "" - if glb_insn and glb_disassembler is not None: - insn =3D perf_sample_insn(perf_script_context) - if insn and len(insn): - cnt, text =3D disassem(insn, ip) - byte_str =3D ("%x" % ip).rjust(16) - if sys.version_info.major >=3D 3: - for k in range(cnt): - byte_str +=3D " %02x" % insn[k] - else: - for k in xrange(cnt): - byte_str +=3D " %02x" % ord(insn[k]) - print("%-40s %-30s" % (byte_str, text), end=3D' ') - print("%s%s (%s)" % (symbol, offs, dso), end=3D' ') - else: - print("%16x %s%s (%s)" % (ip, symbol, offs, dso), end=3D' ') - if "addr_correlates_sym" in sample: - addr =3D sample["addr"] - dso =3D get_optional(sample, "addr_dso") - symbol =3D get_optional(sample, "addr_symbol") - offs =3D get_offset(sample, "addr_symoff") - print("=3D> %x %s%s (%s)%s" % (addr, symbol, offs, dso, ipc_str)) - else: - print(ipc_str) - -def print_srccode(comm, param_dict, sample, symbol, dso, with_insn): - ip =3D sample["ip"] - if symbol =3D=3D "[unknown]": - start_str =3D common_start_str(comm, sample) + ("%x" % ip).rjust(16).lju= st(40) - else: - offs =3D get_offset(param_dict, "symoff") - start_str =3D common_start_str(comm, sample) + (symbol + offs).ljust(40) - - if with_insn and glb_insn and glb_disassembler is not None: - insn =3D perf_sample_insn(perf_script_context) - if insn and len(insn): - cnt, text =3D disassem(insn, ip) - start_str +=3D text.ljust(30) - - global glb_source_file_name - global glb_line_number - global glb_dso - - source_file_name, line_number, source_line =3D perf_sample_srccode(perf_s= cript_context) - if source_file_name: - if glb_line_number =3D=3D line_number and glb_source_file_name =3D=3D so= urce_file_name: - src_str =3D "" - else: - if len(source_file_name) > 40: - src_file =3D ("..." + source_file_name[-37:]) + " " - else: - src_file =3D source_file_name.ljust(41) - if source_line is None: - src_str =3D src_file + str(line_number).rjust(4) + " " - else: - src_str =3D src_file + str(line_number).rjust(4) + " " + source_line - glb_dso =3D None - elif dso =3D=3D glb_dso: - src_str =3D "" - else: - src_str =3D dso - glb_dso =3D dso - - glb_line_number =3D line_number - glb_source_file_name =3D source_file_name - - print(start_str, src_str) - -def do_process_event(param_dict): - sample =3D param_dict["sample"] - raw_buf =3D param_dict["raw_buf"] - comm =3D param_dict["comm"] - name =3D param_dict["ev_name"] - # Unused fields: - # callchain =3D param_dict["callchain"] - # brstack =3D param_dict["brstack"] - # brstacksym =3D param_dict["brstacksym"] - # event_attr =3D param_dict["attr"] - - # Symbol and dso info are not always resolved - dso =3D get_optional(param_dict, "dso") - symbol =3D get_optional(param_dict, "symbol") - - cpu =3D sample["cpu"] - if cpu in glb_switch_str: - print(glb_switch_str[cpu]) - del glb_switch_str[cpu] - - if name.startswith("instructions"): - if glb_src: - print_srccode(comm, param_dict, sample, symbol, dso, True) - else: - print_instructions_start(comm, sample) - print_common_ip(param_dict, sample, symbol, dso) - elif name.startswith("branches"): - if glb_src: - print_srccode(comm, param_dict, sample, symbol, dso, False) - else: - print_common_start(comm, sample, name) - print_common_ip(param_dict, sample, symbol, dso) - elif name =3D=3D "ptwrite": - print_common_start(comm, sample, name) - print_ptwrite(raw_buf) - print_common_ip(param_dict, sample, symbol, dso) - elif name =3D=3D "cbr": - print_common_start(comm, sample, name) - print_cbr(raw_buf) - print_common_ip(param_dict, sample, symbol, dso) - elif name =3D=3D "mwait": - print_common_start(comm, sample, name) - print_mwait(raw_buf) - print_common_ip(param_dict, sample, symbol, dso) - elif name =3D=3D "pwre": - print_common_start(comm, sample, name) - print_pwre(raw_buf) - print_common_ip(param_dict, sample, symbol, dso) - elif name =3D=3D "exstop": - print_common_start(comm, sample, name) - print_exstop(raw_buf) - print_common_ip(param_dict, sample, symbol, dso) - elif name =3D=3D "pwrx": - print_common_start(comm, sample, name) - print_pwrx(raw_buf) - print_common_ip(param_dict, sample, symbol, dso) - elif name =3D=3D "psb": - print_common_start(comm, sample, name) - print_psb(raw_buf) - print_common_ip(param_dict, sample, symbol, dso) - elif name =3D=3D "evt": - print_common_start(comm, sample, name) - print_evt(raw_buf) - print_common_ip(param_dict, sample, symbol, dso) - elif name =3D=3D "iflag": - print_common_start(comm, sample, name) - print_iflag(raw_buf) - print_common_ip(param_dict, sample, symbol, dso) - else: - print_common_start(comm, sample, name) - print_common_ip(param_dict, sample, symbol, dso) - -def interleave_events(param_dict): - global glb_cpu - global glb_time - global glb_output - global glb_output_pos - - sample =3D param_dict["sample"] - glb_cpu =3D sample["cpu"] - ts =3D sample["time"] - - if glb_time !=3D ts: - glb_time =3D ts - flush_stashed_output() - - glb_output_pos =3D 0 - with contextlib.redirect_stdout(io.StringIO()) as glb_output: - do_process_event(param_dict) - - stash_output() - -def process_event(param_dict): - try: - if glb_args.interleave: - interleave_events(param_dict) - else: - do_process_event(param_dict) - except broken_pipe_exception: - # Stop python printing broken pipe errors and traceback - sys.stdout =3D open(os.devnull, 'w') - sys.exit(1) - -def auxtrace_error(typ, code, cpu, pid, tid, ip, ts, msg, cpumode, *x): - if glb_args.interleave: - flush_stashed_output() - if len(x) >=3D 2 and x[0]: - machine_pid =3D x[0] - vcpu =3D x[1] - else: - machine_pid =3D 0 - vcpu =3D -1 - try: - if machine_pid: - print("VM:%5d VCPU:%03d %16s %5u/%-5u [%03u] %9u.%09u error type %u co= de %u: %s ip 0x%16x" % - (machine_pid, vcpu, "Trace error", pid, tid, cpu, ts / 1000000000, ts = %1000000000, typ, code, msg, ip)) - else: - print("%16s %5u/%-5u [%03u] %9u.%09u error type %u code %u: %s ip 0x%1= 6x" % - ("Trace error", pid, tid, cpu, ts / 1000000000, ts %1000000000, typ, c= ode, msg, ip)) - except broken_pipe_exception: - # Stop python printing broken pipe errors and traceback - sys.stdout =3D open(os.devnull, 'w') - sys.exit(1) - -def context_switch(ts, cpu, pid, tid, np_pid, np_tid, machine_pid, out, ou= t_preempt, *x): - if glb_args.interleave: - flush_stashed_output() - if out: - out_str =3D "Switch out " - else: - out_str =3D "Switch In " - if out_preempt: - preempt_str =3D "preempt" - else: - preempt_str =3D "" - if len(x) >=3D 2 and x[0]: - machine_pid =3D x[0] - vcpu =3D x[1] - else: - vcpu =3D None; - if machine_pid =3D=3D -1: - machine_str =3D "" - elif vcpu is None: - machine_str =3D "machine PID %d" % machine_pid - else: - machine_str =3D "machine PID %d VCPU %d" % (machine_pid, vcpu) - switch_str =3D "%16s %5d/%-5d [%03u] %9u.%09u %5d/%-5d %s %s" % \ - (out_str, pid, tid, cpu, ts / 1000000000, ts %1000000000, np_pid, np_tid= , machine_str, preempt_str) - if glb_args.all_switch_events: - print(switch_str) - else: - global glb_switch_str - glb_switch_str[cpu] =3D switch_str diff --git a/tools/perf/scripts/python/libxed.py b/tools/perf/scripts/pytho= n/libxed.py deleted file mode 100644 index 2c70a5a7eb9c..000000000000 --- a/tools/perf/scripts/python/libxed.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env python -# SPDX-License-Identifier: GPL-2.0 -# libxed.py: Python wrapper for libxed.so -# Copyright (c) 2014-2021, Intel Corporation. - -# To use Intel XED, libxed.so must be present. To build and install -# libxed.so: -# git clone https://github.com/intelxed/mbuild.git mbuild -# git clone https://github.com/intelxed/xed -# cd xed -# ./mfile.py --share -# sudo ./mfile.py --prefix=3D/usr/local install -# sudo ldconfig -# - -import sys - -from ctypes import CDLL, Structure, create_string_buffer, addressof, sizeo= f, \ - c_void_p, c_bool, c_byte, c_char, c_int, c_uint, c_longlong, c_ulongl= ong - -# XED Disassembler - -class xed_state_t(Structure): - - _fields_ =3D [ - ("mode", c_int), - ("width", c_int) - ] - -class XEDInstruction(): - - def __init__(self, libxed): - # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow fo= r future expansion - xedd_t =3D c_byte * 512 - self.xedd =3D xedd_t() - self.xedp =3D addressof(self.xedd) - libxed.xed_decoded_inst_zero(self.xedp) - self.state =3D xed_state_t() - self.statep =3D addressof(self.state) - # Buffer for disassembled instruction text - self.buffer =3D create_string_buffer(256) - self.bufferp =3D addressof(self.buffer) - -class LibXED(): - - def __init__(self): - try: - self.libxed =3D CDLL("libxed.so") - except: - self.libxed =3D None - if not self.libxed: - self.libxed =3D CDLL("/usr/local/lib/libxed.so") - - self.xed_tables_init =3D self.libxed.xed_tables_init - self.xed_tables_init.restype =3D None - self.xed_tables_init.argtypes =3D [] - - self.xed_decoded_inst_zero =3D self.libxed.xed_decoded_inst_zero - self.xed_decoded_inst_zero.restype =3D None - self.xed_decoded_inst_zero.argtypes =3D [ c_void_p ] - - self.xed_operand_values_set_mode =3D self.libxed.xed_operand_values_set_= mode - self.xed_operand_values_set_mode.restype =3D None - self.xed_operand_values_set_mode.argtypes =3D [ c_void_p, c_void_p ] - - self.xed_decoded_inst_zero_keep_mode =3D self.libxed.xed_decoded_inst_ze= ro_keep_mode - self.xed_decoded_inst_zero_keep_mode.restype =3D None - self.xed_decoded_inst_zero_keep_mode.argtypes =3D [ c_void_p ] - - self.xed_decode =3D self.libxed.xed_decode - self.xed_decode.restype =3D c_int - self.xed_decode.argtypes =3D [ c_void_p, c_void_p, c_uint ] - - self.xed_format_context =3D self.libxed.xed_format_context - self.xed_format_context.restype =3D c_uint - self.xed_format_context.argtypes =3D [ c_int, c_void_p, c_void_p, c_int,= c_ulonglong, c_void_p, c_void_p ] - - self.xed_tables_init() - - def Instruction(self): - return XEDInstruction(self) - - def SetMode(self, inst, mode): - if mode: - inst.state.mode =3D 4 # 32-bit - inst.state.width =3D 4 # 4 bytes - else: - inst.state.mode =3D 1 # 64-bit - inst.state.width =3D 8 # 8 bytes - self.xed_operand_values_set_mode(inst.xedp, inst.statep) - - def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): - self.xed_decoded_inst_zero_keep_mode(inst.xedp) - err =3D self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) - if err: - return 0, "" - # Use AT&T mode (2), alternative is Intel (3) - ok =3D self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.b= uffer), ip, 0, 0) - if not ok: - return 0, "" - if sys.version_info[0] =3D=3D 2: - result =3D inst.buffer.value - else: - result =3D inst.buffer.value.decode() - # Return instruction length and the disassembled instruction text - # For now, assume the length is in byte 166 - return inst.xedd[166], result diff --git a/tools/perf/scripts/python/mem-phys-addr.py b/tools/perf/script= s/python/mem-phys-addr.py deleted file mode 100644 index 5e237a5a5f1b..000000000000 --- a/tools/perf/scripts/python/mem-phys-addr.py +++ /dev/null @@ -1,127 +0,0 @@ -# mem-phys-addr.py: Resolve physical address samples -# SPDX-License-Identifier: GPL-2.0 -# -# Copyright (c) 2018, Intel Corporation. - -import os -import sys -import re -import bisect -import collections -from dataclasses import dataclass -from typing import (Dict, Optional) - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -@dataclass(frozen=3DTrue) -class IomemEntry: - """Read from a line in /proc/iomem""" - begin: int - end: int - indent: int - label: str - -# Physical memory layout from /proc/iomem. Key is the indent and then -# a list of ranges. -iomem: Dict[int, list[IomemEntry]] =3D collections.defaultdict(list) -# Child nodes from the iomem parent. -children: Dict[IomemEntry, set[IomemEntry]] =3D collections.defaultdict(se= t) -# Maximum indent seen before an entry in the iomem file. -max_indent: int =3D 0 -# Count for each range of memory. -load_mem_type_cnt: Dict[IomemEntry, int] =3D collections.Counter() -# Perf event name set from the first sample in the data. -event_name: Optional[str] =3D None - -def parse_iomem(): - """Populate iomem from /proc/iomem file""" - global iomem - global max_indent - global children - with open('/proc/iomem', 'r', encoding=3D'ascii') as f: - for line in f: - indent =3D 0 - while line[indent] =3D=3D ' ': - indent +=3D 1 - if indent > max_indent: - max_indent =3D indent - m =3D re.split('-|:', line, 2) - begin =3D int(m[0], 16) - end =3D int(m[1], 16) - label =3D m[2].strip() - entry =3D IomemEntry(begin, end, indent, label) - # Before adding entry, search for a parent node using its begi= n. - if indent > 0: - parent =3D find_memory_type(begin) - assert parent, f"Given indent expected a parent for {label= }" - children[parent].add(entry) - iomem[indent].append(entry) - -def find_memory_type(phys_addr) -> Optional[IomemEntry]: - """Search iomem for the range containing phys_addr with the maximum in= dent""" - for i in range(max_indent, -1, -1): - if i not in iomem: - continue - position =3D bisect.bisect_right(iomem[i], phys_addr, - key=3Dlambda entry: entry.begin) - if position is None: - continue - iomem_entry =3D iomem[i][position-1] - if iomem_entry.begin <=3D phys_addr <=3D iomem_entry.end: - return iomem_entry - print(f"Didn't find {phys_addr}") - return None - -def print_memory_type(): - print(f"Event: {event_name}") - print(f"{'Memory type':<40} {'count':>10} {'percentage':>10}") - print(f"{'-' * 40:<40} {'-' * 10:>10} {'-' * 10:>10}") - total =3D sum(load_mem_type_cnt.values()) - # Add count from children into the parent. - for i in range(max_indent, -1, -1): - if i not in iomem: - continue - for entry in iomem[i]: - global children - for child in children[entry]: - if load_mem_type_cnt[child] > 0: - load_mem_type_cnt[entry] +=3D load_mem_type_cnt[child] - - def print_entries(entries): - """Print counts from parents down to their children""" - global children - for entry in sorted(entries, - key =3D lambda entry: load_mem_type_cnt[entry], - reverse =3D True): - count =3D load_mem_type_cnt[entry] - if count > 0: - mem_type =3D ' ' * entry.indent + f"{entry.begin:x}-{entry= .end:x} : {entry.label}" - percent =3D 100 * count / total - print(f"{mem_type:<40} {count:>10} {percent:>10.1f}") - print_entries(children[entry]) - - print_entries(iomem[0]) - -def trace_begin(): - parse_iomem() - -def trace_end(): - print_memory_type() - -def process_event(param_dict): - if "sample" not in param_dict: - return - - sample =3D param_dict["sample"] - if "phys_addr" not in sample: - return - - phys_addr =3D sample["phys_addr"] - entry =3D find_memory_type(phys_addr) - if entry: - load_mem_type_cnt[entry] +=3D 1 - - global event_name - if event_name is None: - event_name =3D param_dict["ev_name"] diff --git a/tools/perf/scripts/python/net_dropmonitor.py b/tools/perf/scri= pts/python/net_dropmonitor.py deleted file mode 100755 index a97e7a6e0940..000000000000 --- a/tools/perf/scripts/python/net_dropmonitor.py +++ /dev/null @@ -1,78 +0,0 @@ -# Monitor the system for dropped packets and proudce a report of drop loca= tions and counts -# SPDX-License-Identifier: GPL-2.0 - -from __future__ import print_function - -import os -import sys - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -from perf_trace_context import * -from Core import * -from Util import * - -drop_log =3D {} -kallsyms =3D [] - -def get_kallsyms_table(): - global kallsyms - - try: - f =3D open("/proc/kallsyms", "r") - except: - return - - for line in f: - loc =3D int(line.split()[0], 16) - name =3D line.split()[2] - kallsyms.append((loc, name)) - kallsyms.sort() - -def get_sym(sloc): - loc =3D int(sloc) - - # Invariant: kallsyms[i][0] <=3D loc for all 0 <=3D i <=3D start - # kallsyms[i][0] > loc for all end <=3D i < len(kallsyms) - start, end =3D -1, len(kallsyms) - while end !=3D start + 1: - pivot =3D (start + end) // 2 - if loc < kallsyms[pivot][0]: - end =3D pivot - else: - start =3D pivot - - # Now (start =3D=3D -1 or kallsyms[start][0] <=3D loc) - # and (start =3D=3D len(kallsyms) - 1 or loc < kallsyms[start + 1][0]) - if start >=3D 0: - symloc, name =3D kallsyms[start] - return (name, loc - symloc) - else: - return (None, 0) - -def print_drop_table(): - print("%25s %25s %25s" % ("LOCATION", "OFFSET", "COUNT")) - for i in drop_log.keys(): - (sym, off) =3D get_sym(i) - if sym =3D=3D None: - sym =3D i - print("%25s %25s %25s" % (sym, off, drop_log[i])) - - -def trace_begin(): - print("Starting trace (Ctrl-C to dump results)") - -def trace_end(): - print("Gathering kallsyms data") - get_kallsyms_table() - print_drop_table() - -# called from perf, when it finds a corresponding event -def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm, callchain, - skbaddr, location, protocol, reason): - slocation =3D str(location) - try: - drop_log[slocation] =3D drop_log[slocation] + 1 - except: - drop_log[slocation] =3D 1 diff --git a/tools/perf/scripts/python/netdev-times.py b/tools/perf/scripts= /python/netdev-times.py deleted file mode 100644 index 30c4bccee5b2..000000000000 --- a/tools/perf/scripts/python/netdev-times.py +++ /dev/null @@ -1,473 +0,0 @@ -# Display a process of packets and processed time. -# SPDX-License-Identifier: GPL-2.0 -# It helps us to investigate networking or network device. -# -# options -# tx: show only tx chart -# rx: show only rx chart -# dev=3D: show only thing related to specified device -# debug: work with debug mode. It shows buffer status. - -from __future__ import print_function - -import os -import sys - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -from perf_trace_context import * -from Core import * -from Util import * -from functools import cmp_to_key - -all_event_list =3D []; # insert all tracepoint event related with this scr= ipt -irq_dic =3D {}; # key is cpu and value is a list which stacks irqs - # which raise NET_RX softirq -net_rx_dic =3D {}; # key is cpu and value include time of NET_RX softirq-e= ntry - # and a list which stacks receive -receive_hunk_list =3D []; # a list which include a sequence of receive eve= nts -rx_skb_list =3D []; # received packet list for matching - # skb_copy_datagram_iovec - -buffer_budget =3D 65536; # the budget of rx_skb_list, tx_queue_list and - # tx_xmit_list -of_count_rx_skb_list =3D 0; # overflow count - -tx_queue_list =3D []; # list of packets which pass through dev_queue_xmit -of_count_tx_queue_list =3D 0; # overflow count - -tx_xmit_list =3D []; # list of packets which pass through dev_hard_start_= xmit -of_count_tx_xmit_list =3D 0; # overflow count - -tx_free_list =3D []; # list of packets which is freed - -# options -show_tx =3D 0; -show_rx =3D 0; -dev =3D 0; # store a name of device specified by option "dev=3D" -debug =3D 0; - -# indices of event_info tuple -EINFO_IDX_NAME=3D 0 -EINFO_IDX_CONTEXT=3D1 -EINFO_IDX_CPU=3D 2 -EINFO_IDX_TIME=3D 3 -EINFO_IDX_PID=3D 4 -EINFO_IDX_COMM=3D 5 - -# Calculate a time interval(msec) from src(nsec) to dst(nsec) -def diff_msec(src, dst): - return (dst - src) / 1000000.0 - -# Display a process of transmitting a packet -def print_transmit(hunk): - if dev !=3D 0 and hunk['dev'].find(dev) < 0: - return - print("%7s %5d %6d.%06dsec %12.3fmsec %12.3fmsec" % - (hunk['dev'], hunk['len'], - nsecs_secs(hunk['queue_t']), - nsecs_nsecs(hunk['queue_t'])/1000, - diff_msec(hunk['queue_t'], hunk['xmit_t']), - diff_msec(hunk['xmit_t'], hunk['free_t']))) - -# Format for displaying rx packet processing -PF_IRQ_ENTRY=3D " irq_entry(+%.3fmsec irq=3D%d:%s)" -PF_SOFT_ENTRY=3D" softirq_entry(+%.3fmsec)" -PF_NAPI_POLL=3D " napi_poll_exit(+%.3fmsec %s)" -PF_JOINT=3D " |" -PF_WJOINT=3D " | |" -PF_NET_RECV=3D " |---netif_receive_skb(+%.3fmsec skb=3D%x len=3D%= d)" -PF_NET_RX=3D " |---netif_rx(+%.3fmsec skb=3D%x)" -PF_CPY_DGRAM=3D " | skb_copy_datagram_iovec(+%.3fmsec %d:%s)" -PF_KFREE_SKB=3D " | kfree_skb(+%.3fmsec location=3D%x)" -PF_CONS_SKB=3D " | consume_skb(+%.3fmsec)" - -# Display a process of received packets and interrputs associated with -# a NET_RX softirq -def print_receive(hunk): - show_hunk =3D 0 - irq_list =3D hunk['irq_list'] - cpu =3D irq_list[0]['cpu'] - base_t =3D irq_list[0]['irq_ent_t'] - # check if this hunk should be showed - if dev !=3D 0: - for i in range(len(irq_list)): - if irq_list[i]['name'].find(dev) >=3D 0: - show_hunk =3D 1 - break - else: - show_hunk =3D 1 - if show_hunk =3D=3D 0: - return - - print("%d.%06dsec cpu=3D%d" % - (nsecs_secs(base_t), nsecs_nsecs(base_t)/1000, cpu)) - for i in range(len(irq_list)): - print(PF_IRQ_ENTRY % - (diff_msec(base_t, irq_list[i]['irq_ent_t']), - irq_list[i]['irq'], irq_list[i]['name'])) - print(PF_JOINT) - irq_event_list =3D irq_list[i]['event_list'] - for j in range(len(irq_event_list)): - irq_event =3D irq_event_list[j] - if irq_event['event'] =3D=3D 'netif_rx': - print(PF_NET_RX % - (diff_msec(base_t, irq_event['time']), - irq_event['skbaddr'])) - print(PF_JOINT) - print(PF_SOFT_ENTRY % - diff_msec(base_t, hunk['sirq_ent_t'])) - print(PF_JOINT) - event_list =3D hunk['event_list'] - for i in range(len(event_list)): - event =3D event_list[i] - if event['event_name'] =3D=3D 'napi_poll': - print(PF_NAPI_POLL % - (diff_msec(base_t, event['event_t']), - event['dev'])) - if i =3D=3D len(event_list) - 1: - print("") - else: - print(PF_JOINT) - else: - print(PF_NET_RECV % - (diff_msec(base_t, event['event_t']), - event['skbaddr'], - event['len'])) - if 'comm' in event.keys(): - print(PF_WJOINT) - print(PF_CPY_DGRAM % - (diff_msec(base_t, event['comm_t']), - event['pid'], event['comm'])) - elif 'handle' in event.keys(): - print(PF_WJOINT) - if event['handle'] =3D=3D "kfree_skb": - print(PF_KFREE_SKB % - (diff_msec(base_t, - event['comm_t']), - event['location'])) - elif event['handle'] =3D=3D "consume_skb": - print(PF_CONS_SKB % - diff_msec(base_t, - event['comm_t'])) - print(PF_JOINT) - -def trace_begin(): - global show_tx - global show_rx - global dev - global debug - - for i in range(len(sys.argv)): - if i =3D=3D 0: - continue - arg =3D sys.argv[i] - if arg =3D=3D 'tx': - show_tx =3D 1 - elif arg =3D=3D'rx': - show_rx =3D 1 - elif arg.find('dev=3D',0, 4) >=3D 0: - dev =3D arg[4:] - elif arg =3D=3D 'debug': - debug =3D 1 - if show_tx =3D=3D 0 and show_rx =3D=3D 0: - show_tx =3D 1 - show_rx =3D 1 - -def trace_end(): - # order all events in time - all_event_list.sort(key=3Dcmp_to_key(lambda a,b :a[EINFO_IDX_TIME] < b[EI= NFO_IDX_TIME])) - # process all events - for i in range(len(all_event_list)): - event_info =3D all_event_list[i] - name =3D event_info[EINFO_IDX_NAME] - if name =3D=3D 'irq__softirq_exit': - handle_irq_softirq_exit(event_info) - elif name =3D=3D 'irq__softirq_entry': - handle_irq_softirq_entry(event_info) - elif name =3D=3D 'irq__softirq_raise': - handle_irq_softirq_raise(event_info) - elif name =3D=3D 'irq__irq_handler_entry': - handle_irq_handler_entry(event_info) - elif name =3D=3D 'irq__irq_handler_exit': - handle_irq_handler_exit(event_info) - elif name =3D=3D 'napi__napi_poll': - handle_napi_poll(event_info) - elif name =3D=3D 'net__netif_receive_skb': - handle_netif_receive_skb(event_info) - elif name =3D=3D 'net__netif_rx': - handle_netif_rx(event_info) - elif name =3D=3D 'skb__skb_copy_datagram_iovec': - handle_skb_copy_datagram_iovec(event_info) - elif name =3D=3D 'net__net_dev_queue': - handle_net_dev_queue(event_info) - elif name =3D=3D 'net__net_dev_xmit': - handle_net_dev_xmit(event_info) - elif name =3D=3D 'skb__kfree_skb': - handle_kfree_skb(event_info) - elif name =3D=3D 'skb__consume_skb': - handle_consume_skb(event_info) - # display receive hunks - if show_rx: - for i in range(len(receive_hunk_list)): - print_receive(receive_hunk_list[i]) - # display transmit hunks - if show_tx: - print(" dev len Qdisc " - " netdevice free") - for i in range(len(tx_free_list)): - print_transmit(tx_free_list[i]) - if debug: - print("debug buffer status") - print("----------------------------") - print("xmit Qdisc:remain:%d overflow:%d" % - (len(tx_queue_list), of_count_tx_queue_list)) - print("xmit netdevice:remain:%d overflow:%d" % - (len(tx_xmit_list), of_count_tx_xmit_list)) - print("receive:remain:%d overflow:%d" % - (len(rx_skb_list), of_count_rx_skb_list)) - -# called from perf, when it finds a correspoinding event -def irq__softirq_entry(name, context, cpu, sec, nsec, pid, comm, callchain= , vec): - if symbol_str("irq__softirq_entry", "vec", vec) !=3D "NET_RX": - return - event_info =3D (name, context, cpu, nsecs(sec, nsec), pid, comm, vec) - all_event_list.append(event_info) - -def irq__softirq_exit(name, context, cpu, sec, nsec, pid, comm, callchain,= vec): - if symbol_str("irq__softirq_entry", "vec", vec) !=3D "NET_RX": - return - event_info =3D (name, context, cpu, nsecs(sec, nsec), pid, comm, vec) - all_event_list.append(event_info) - -def irq__softirq_raise(name, context, cpu, sec, nsec, pid, comm, callchain= , vec): - if symbol_str("irq__softirq_entry", "vec", vec) !=3D "NET_RX": - return - event_info =3D (name, context, cpu, nsecs(sec, nsec), pid, comm, vec) - all_event_list.append(event_info) - -def irq__irq_handler_entry(name, context, cpu, sec, nsec, pid, comm, - callchain, irq, irq_name): - event_info =3D (name, context, cpu, nsecs(sec, nsec), pid, comm, - irq, irq_name) - all_event_list.append(event_info) - -def irq__irq_handler_exit(name, context, cpu, sec, nsec, pid, comm, callch= ain, irq, ret): - event_info =3D (name, context, cpu, nsecs(sec, nsec), pid, comm, irq, ret) - all_event_list.append(event_info) - -def napi__napi_poll(name, context, cpu, sec, nsec, pid, comm, callchain, n= api, - dev_name, work=3DNone, budget=3DNone): - event_info =3D (name, context, cpu, nsecs(sec, nsec), pid, comm, - napi, dev_name, work, budget) - all_event_list.append(event_info) - -def net__netif_receive_skb(name, context, cpu, sec, nsec, pid, comm, callc= hain, skbaddr, - skblen, dev_name): - event_info =3D (name, context, cpu, nsecs(sec, nsec), pid, comm, - skbaddr, skblen, dev_name) - all_event_list.append(event_info) - -def net__netif_rx(name, context, cpu, sec, nsec, pid, comm, callchain, skb= addr, - skblen, dev_name): - event_info =3D (name, context, cpu, nsecs(sec, nsec), pid, comm, - skbaddr, skblen, dev_name) - all_event_list.append(event_info) - -def net__net_dev_queue(name, context, cpu, sec, nsec, pid, comm, callchain, - skbaddr, skblen, dev_name): - event_info =3D (name, context, cpu, nsecs(sec, nsec), pid, comm, - skbaddr, skblen, dev_name) - all_event_list.append(event_info) - -def net__net_dev_xmit(name, context, cpu, sec, nsec, pid, comm, callchain, - skbaddr, skblen, rc, dev_name): - event_info =3D (name, context, cpu, nsecs(sec, nsec), pid, comm, - skbaddr, skblen, rc ,dev_name) - all_event_list.append(event_info) - -def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm, callchain, - skbaddr, location, protocol, reason): - event_info =3D (name, context, cpu, nsecs(sec, nsec), pid, comm, - skbaddr, location, protocol, reason) - all_event_list.append(event_info) - -def skb__consume_skb(name, context, cpu, sec, nsec, pid, comm, callchain, - skbaddr, location): - event_info =3D (name, context, cpu, nsecs(sec, nsec), pid, comm, - skbaddr) - all_event_list.append(event_info) - -def skb__skb_copy_datagram_iovec(name, context, cpu, sec, nsec, pid, comm,= callchain, - skbaddr, skblen): - event_info =3D (name, context, cpu, nsecs(sec, nsec), pid, comm, - skbaddr, skblen) - all_event_list.append(event_info) - -def handle_irq_handler_entry(event_info): - (name, context, cpu, time, pid, comm, irq, irq_name) =3D event_info - if cpu not in irq_dic.keys(): - irq_dic[cpu] =3D [] - irq_record =3D {'irq':irq, 'name':irq_name, 'cpu':cpu, 'irq_ent_t':time} - irq_dic[cpu].append(irq_record) - -def handle_irq_handler_exit(event_info): - (name, context, cpu, time, pid, comm, irq, ret) =3D event_info - if cpu not in irq_dic.keys(): - return - irq_record =3D irq_dic[cpu].pop() - if irq !=3D irq_record['irq']: - return - irq_record.update({'irq_ext_t':time}) - # if an irq doesn't include NET_RX softirq, drop. - if 'event_list' in irq_record.keys(): - irq_dic[cpu].append(irq_record) - -def handle_irq_softirq_raise(event_info): - (name, context, cpu, time, pid, comm, vec) =3D event_info - if cpu not in irq_dic.keys() \ - or len(irq_dic[cpu]) =3D=3D 0: - return - irq_record =3D irq_dic[cpu].pop() - if 'event_list' in irq_record.keys(): - irq_event_list =3D irq_record['event_list'] - else: - irq_event_list =3D [] - irq_event_list.append({'time':time, 'event':'sirq_raise'}) - irq_record.update({'event_list':irq_event_list}) - irq_dic[cpu].append(irq_record) - -def handle_irq_softirq_entry(event_info): - (name, context, cpu, time, pid, comm, vec) =3D event_info - net_rx_dic[cpu] =3D {'sirq_ent_t':time, 'event_list':[]} - -def handle_irq_softirq_exit(event_info): - (name, context, cpu, time, pid, comm, vec) =3D event_info - irq_list =3D [] - event_list =3D 0 - if cpu in irq_dic.keys(): - irq_list =3D irq_dic[cpu] - del irq_dic[cpu] - if cpu in net_rx_dic.keys(): - sirq_ent_t =3D net_rx_dic[cpu]['sirq_ent_t'] - event_list =3D net_rx_dic[cpu]['event_list'] - del net_rx_dic[cpu] - if irq_list =3D=3D [] or event_list =3D=3D 0: - return - rec_data =3D {'sirq_ent_t':sirq_ent_t, 'sirq_ext_t':time, - 'irq_list':irq_list, 'event_list':event_list} - # merge information related to a NET_RX softirq - receive_hunk_list.append(rec_data) - -def handle_napi_poll(event_info): - (name, context, cpu, time, pid, comm, napi, dev_name, - work, budget) =3D event_info - if cpu in net_rx_dic.keys(): - event_list =3D net_rx_dic[cpu]['event_list'] - rec_data =3D {'event_name':'napi_poll', - 'dev':dev_name, 'event_t':time, - 'work':work, 'budget':budget} - event_list.append(rec_data) - -def handle_netif_rx(event_info): - (name, context, cpu, time, pid, comm, - skbaddr, skblen, dev_name) =3D event_info - if cpu not in irq_dic.keys() \ - or len(irq_dic[cpu]) =3D=3D 0: - return - irq_record =3D irq_dic[cpu].pop() - if 'event_list' in irq_record.keys(): - irq_event_list =3D irq_record['event_list'] - else: - irq_event_list =3D [] - irq_event_list.append({'time':time, 'event':'netif_rx', - 'skbaddr':skbaddr, 'skblen':skblen, 'dev_name':dev_name}) - irq_record.update({'event_list':irq_event_list}) - irq_dic[cpu].append(irq_record) - -def handle_netif_receive_skb(event_info): - global of_count_rx_skb_list - - (name, context, cpu, time, pid, comm, - skbaddr, skblen, dev_name) =3D event_info - if cpu in net_rx_dic.keys(): - rec_data =3D {'event_name':'netif_receive_skb', - 'event_t':time, 'skbaddr':skbaddr, 'len':skblen} - event_list =3D net_rx_dic[cpu]['event_list'] - event_list.append(rec_data) - rx_skb_list.insert(0, rec_data) - if len(rx_skb_list) > buffer_budget: - rx_skb_list.pop() - of_count_rx_skb_list +=3D 1 - -def handle_net_dev_queue(event_info): - global of_count_tx_queue_list - - (name, context, cpu, time, pid, comm, - skbaddr, skblen, dev_name) =3D event_info - skb =3D {'dev':dev_name, 'skbaddr':skbaddr, 'len':skblen, 'queue_t':time} - tx_queue_list.insert(0, skb) - if len(tx_queue_list) > buffer_budget: - tx_queue_list.pop() - of_count_tx_queue_list +=3D 1 - -def handle_net_dev_xmit(event_info): - global of_count_tx_xmit_list - - (name, context, cpu, time, pid, comm, - skbaddr, skblen, rc, dev_name) =3D event_info - if rc =3D=3D 0: # NETDEV_TX_OK - for i in range(len(tx_queue_list)): - skb =3D tx_queue_list[i] - if skb['skbaddr'] =3D=3D skbaddr: - skb['xmit_t'] =3D time - tx_xmit_list.insert(0, skb) - del tx_queue_list[i] - if len(tx_xmit_list) > buffer_budget: - tx_xmit_list.pop() - of_count_tx_xmit_list +=3D 1 - return - -def handle_kfree_skb(event_info): - (name, context, cpu, time, pid, comm, - skbaddr, location, protocol, reason) =3D event_info - for i in range(len(tx_queue_list)): - skb =3D tx_queue_list[i] - if skb['skbaddr'] =3D=3D skbaddr: - del tx_queue_list[i] - return - for i in range(len(tx_xmit_list)): - skb =3D tx_xmit_list[i] - if skb['skbaddr'] =3D=3D skbaddr: - skb['free_t'] =3D time - tx_free_list.append(skb) - del tx_xmit_list[i] - return - for i in range(len(rx_skb_list)): - rec_data =3D rx_skb_list[i] - if rec_data['skbaddr'] =3D=3D skbaddr: - rec_data.update({'handle':"kfree_skb", - 'comm':comm, 'pid':pid, 'comm_t':time}) - del rx_skb_list[i] - return - -def handle_consume_skb(event_info): - (name, context, cpu, time, pid, comm, skbaddr) =3D event_info - for i in range(len(tx_xmit_list)): - skb =3D tx_xmit_list[i] - if skb['skbaddr'] =3D=3D skbaddr: - skb['free_t'] =3D time - tx_free_list.append(skb) - del tx_xmit_list[i] - return - -def handle_skb_copy_datagram_iovec(event_info): - (name, context, cpu, time, pid, comm, skbaddr, skblen) =3D event_info - for i in range(len(rx_skb_list)): - rec_data =3D rx_skb_list[i] - if skbaddr =3D=3D rec_data['skbaddr']: - rec_data.update({'handle':"skb_copy_datagram_iovec", - 'comm':comm, 'pid':pid, 'comm_t':time}) - del rx_skb_list[i] - return diff --git a/tools/perf/scripts/python/powerpc-hcalls.py b/tools/perf/scrip= ts/python/powerpc-hcalls.py deleted file mode 100644 index 8b78dc790adb..000000000000 --- a/tools/perf/scripts/python/powerpc-hcalls.py +++ /dev/null @@ -1,202 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0+ -# -# Copyright (C) 2018 Ravi Bangoria, IBM Corporation -# -# Hypervisor call statisics - -from __future__ import print_function - -import os -import sys - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -from perf_trace_context import * -from Core import * -from Util import * - -# output: { -# opcode: { -# 'min': minimum time nsec -# 'max': maximum time nsec -# 'time': average time nsec -# 'cnt': counter -# } ... -# } -output =3D {} - -# d_enter: { -# cpu: { -# opcode: nsec -# } ... -# } -d_enter =3D {} - -hcall_table =3D { - 4: 'H_REMOVE', - 8: 'H_ENTER', - 12: 'H_READ', - 16: 'H_CLEAR_MOD', - 20: 'H_CLEAR_REF', - 24: 'H_PROTECT', - 28: 'H_GET_TCE', - 32: 'H_PUT_TCE', - 36: 'H_SET_SPRG0', - 40: 'H_SET_DABR', - 44: 'H_PAGE_INIT', - 48: 'H_SET_ASR', - 52: 'H_ASR_ON', - 56: 'H_ASR_OFF', - 60: 'H_LOGICAL_CI_LOAD', - 64: 'H_LOGICAL_CI_STORE', - 68: 'H_LOGICAL_CACHE_LOAD', - 72: 'H_LOGICAL_CACHE_STORE', - 76: 'H_LOGICAL_ICBI', - 80: 'H_LOGICAL_DCBF', - 84: 'H_GET_TERM_CHAR', - 88: 'H_PUT_TERM_CHAR', - 92: 'H_REAL_TO_LOGICAL', - 96: 'H_HYPERVISOR_DATA', - 100: 'H_EOI', - 104: 'H_CPPR', - 108: 'H_IPI', - 112: 'H_IPOLL', - 116: 'H_XIRR', - 120: 'H_MIGRATE_DMA', - 124: 'H_PERFMON', - 220: 'H_REGISTER_VPA', - 224: 'H_CEDE', - 228: 'H_CONFER', - 232: 'H_PROD', - 236: 'H_GET_PPP', - 240: 'H_SET_PPP', - 244: 'H_PURR', - 248: 'H_PIC', - 252: 'H_REG_CRQ', - 256: 'H_FREE_CRQ', - 260: 'H_VIO_SIGNAL', - 264: 'H_SEND_CRQ', - 272: 'H_COPY_RDMA', - 276: 'H_REGISTER_LOGICAL_LAN', - 280: 'H_FREE_LOGICAL_LAN', - 284: 'H_ADD_LOGICAL_LAN_BUFFER', - 288: 'H_SEND_LOGICAL_LAN', - 292: 'H_BULK_REMOVE', - 304: 'H_MULTICAST_CTRL', - 308: 'H_SET_XDABR', - 312: 'H_STUFF_TCE', - 316: 'H_PUT_TCE_INDIRECT', - 332: 'H_CHANGE_LOGICAL_LAN_MAC', - 336: 'H_VTERM_PARTNER_INFO', - 340: 'H_REGISTER_VTERM', - 344: 'H_FREE_VTERM', - 348: 'H_RESET_EVENTS', - 352: 'H_ALLOC_RESOURCE', - 356: 'H_FREE_RESOURCE', - 360: 'H_MODIFY_QP', - 364: 'H_QUERY_QP', - 368: 'H_REREGISTER_PMR', - 372: 'H_REGISTER_SMR', - 376: 'H_QUERY_MR', - 380: 'H_QUERY_MW', - 384: 'H_QUERY_HCA', - 388: 'H_QUERY_PORT', - 392: 'H_MODIFY_PORT', - 396: 'H_DEFINE_AQP1', - 400: 'H_GET_TRACE_BUFFER', - 404: 'H_DEFINE_AQP0', - 408: 'H_RESIZE_MR', - 412: 'H_ATTACH_MCQP', - 416: 'H_DETACH_MCQP', - 420: 'H_CREATE_RPT', - 424: 'H_REMOVE_RPT', - 428: 'H_REGISTER_RPAGES', - 432: 'H_DISABLE_AND_GETC', - 436: 'H_ERROR_DATA', - 440: 'H_GET_HCA_INFO', - 444: 'H_GET_PERF_COUNT', - 448: 'H_MANAGE_TRACE', - 468: 'H_FREE_LOGICAL_LAN_BUFFER', - 472: 'H_POLL_PENDING', - 484: 'H_QUERY_INT_STATE', - 580: 'H_ILLAN_ATTRIBUTES', - 592: 'H_MODIFY_HEA_QP', - 596: 'H_QUERY_HEA_QP', - 600: 'H_QUERY_HEA', - 604: 'H_QUERY_HEA_PORT', - 608: 'H_MODIFY_HEA_PORT', - 612: 'H_REG_BCMC', - 616: 'H_DEREG_BCMC', - 620: 'H_REGISTER_HEA_RPAGES', - 624: 'H_DISABLE_AND_GET_HEA', - 628: 'H_GET_HEA_INFO', - 632: 'H_ALLOC_HEA_RESOURCE', - 644: 'H_ADD_CONN', - 648: 'H_DEL_CONN', - 664: 'H_JOIN', - 676: 'H_VASI_STATE', - 688: 'H_ENABLE_CRQ', - 696: 'H_GET_EM_PARMS', - 720: 'H_SET_MPP', - 724: 'H_GET_MPP', - 748: 'H_HOME_NODE_ASSOCIATIVITY', - 756: 'H_BEST_ENERGY', - 764: 'H_XIRR_X', - 768: 'H_RANDOM', - 772: 'H_COP', - 788: 'H_GET_MPP_X', - 796: 'H_SET_MODE', - 61440: 'H_RTAS', -} - -def hcall_table_lookup(opcode): - if (opcode in hcall_table): - return hcall_table[opcode] - else: - return opcode - -print_ptrn =3D '%-28s%10s%10s%10s%10s' - -def trace_end(): - print(print_ptrn % ('hcall', 'count', 'min(ns)', 'max(ns)', 'avg(ns)')) - print('-' * 68) - for opcode in output: - h_name =3D hcall_table_lookup(opcode) - time =3D output[opcode]['time'] - cnt =3D output[opcode]['cnt'] - min_t =3D output[opcode]['min'] - max_t =3D output[opcode]['max'] - - print(print_ptrn % (h_name, cnt, min_t, max_t, time//cnt)) - -def powerpc__hcall_exit(name, context, cpu, sec, nsec, pid, comm, callchai= n, - opcode, retval): - if (cpu in d_enter and opcode in d_enter[cpu]): - diff =3D nsecs(sec, nsec) - d_enter[cpu][opcode] - - if (opcode in output): - output[opcode]['time'] +=3D diff - output[opcode]['cnt'] +=3D 1 - if (output[opcode]['min'] > diff): - output[opcode]['min'] =3D diff - if (output[opcode]['max'] < diff): - output[opcode]['max'] =3D diff - else: - output[opcode] =3D { - 'time': diff, - 'cnt': 1, - 'min': diff, - 'max': diff, - } - - del d_enter[cpu][opcode] -# else: -# print("Can't find matching hcall_enter event. Ignoring sample") - -def powerpc__hcall_entry(event_name, context, cpu, sec, nsec, pid, comm, - callchain, opcode): - if (cpu in d_enter): - d_enter[cpu][opcode] =3D nsecs(sec, nsec) - else: - d_enter[cpu] =3D {opcode: nsecs(sec, nsec)} diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scri= pts/python/sched-migration.py deleted file mode 100644 index 8196e3087c9e..000000000000 --- a/tools/perf/scripts/python/sched-migration.py +++ /dev/null @@ -1,462 +0,0 @@ -# Cpu task migration overview toy -# -# Copyright (C) 2010 Frederic Weisbecker -# -# perf script event handlers have been generated by perf script -g python -# -# This software is distributed under the terms of the GNU General -# Public License ("GPL") version 2 as published by the Free Software -# Foundation. -from __future__ import print_function - -import os -import sys - -from collections import defaultdict -try: - from UserList import UserList -except ImportError: - # Python 3: UserList moved to the collections package - from collections import UserList - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') -sys.path.append('scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -from perf_trace_context import * -from Core import * -from SchedGui import * - - -threads =3D { 0 : "idle"} - -def thread_name(pid): - return "%s:%d" % (threads[pid], pid) - -class RunqueueEventUnknown: - @staticmethod - def color(): - return None - - def __repr__(self): - return "unknown" - -class RunqueueEventSleep: - @staticmethod - def color(): - return (0, 0, 0xff) - - def __init__(self, sleeper): - self.sleeper =3D sleeper - - def __repr__(self): - return "%s gone to sleep" % thread_name(self.sleeper) - -class RunqueueEventWakeup: - @staticmethod - def color(): - return (0xff, 0xff, 0) - - def __init__(self, wakee): - self.wakee =3D wakee - - def __repr__(self): - return "%s woke up" % thread_name(self.wakee) - -class RunqueueEventFork: - @staticmethod - def color(): - return (0, 0xff, 0) - - def __init__(self, child): - self.child =3D child - - def __repr__(self): - return "new forked task %s" % thread_name(self.child) - -class RunqueueMigrateIn: - @staticmethod - def color(): - return (0, 0xf0, 0xff) - - def __init__(self, new): - self.new =3D new - - def __repr__(self): - return "task migrated in %s" % thread_name(self.new) - -class RunqueueMigrateOut: - @staticmethod - def color(): - return (0xff, 0, 0xff) - - def __init__(self, old): - self.old =3D old - - def __repr__(self): - return "task migrated out %s" % thread_name(self.old) - -class RunqueueSnapshot: - def __init__(self, tasks =3D [0], event =3D RunqueueEventUnknown()): - self.tasks =3D tuple(tasks) - self.event =3D event - - def sched_switch(self, prev, prev_state, next): - event =3D RunqueueEventUnknown() - - if taskState(prev_state) =3D=3D "R" and next in self.tasks \ - and prev in self.tasks: - return self - - if taskState(prev_state) !=3D "R": - event =3D RunqueueEventSleep(prev) - - next_tasks =3D list(self.tasks[:]) - if prev in self.tasks: - if taskState(prev_state) !=3D "R": - next_tasks.remove(prev) - elif taskState(prev_state) =3D=3D "R": - next_tasks.append(prev) - - if next not in next_tasks: - next_tasks.append(next) - - return RunqueueSnapshot(next_tasks, event) - - def migrate_out(self, old): - if old not in self.tasks: - return self - next_tasks =3D [task for task in self.tasks if task !=3D old] - - return RunqueueSnapshot(next_tasks, RunqueueMigrateOut(old)) - - def __migrate_in(self, new, event): - if new in self.tasks: - self.event =3D event - return self - next_tasks =3D self.tasks[:] + tuple([new]) - - return RunqueueSnapshot(next_tasks, event) - - def migrate_in(self, new): - return self.__migrate_in(new, RunqueueMigrateIn(new)) - - def wake_up(self, new): - return self.__migrate_in(new, RunqueueEventWakeup(new)) - - def wake_up_new(self, new): - return self.__migrate_in(new, RunqueueEventFork(new)) - - def load(self): - """ Provide the number of tasks on the runqueue. - Don't count idle""" - return len(self.tasks) - 1 - - def __repr__(self): - ret =3D self.tasks.__repr__() - ret +=3D self.origin_tostring() - - return ret - -class TimeSlice: - def __init__(self, start, prev): - self.start =3D start - self.prev =3D prev - self.end =3D start - # cpus that triggered the event - self.event_cpus =3D [] - if prev is not None: - self.total_load =3D prev.total_load - self.rqs =3D prev.rqs.copy() - else: - self.rqs =3D defaultdict(RunqueueSnapshot) - self.total_load =3D 0 - - def __update_total_load(self, old_rq, new_rq): - diff =3D new_rq.load() - old_rq.load() - self.total_load +=3D diff - - def sched_switch(self, ts_list, prev, prev_state, next, cpu): - old_rq =3D self.prev.rqs[cpu] - new_rq =3D old_rq.sched_switch(prev, prev_state, next) - - if old_rq is new_rq: - return - - self.rqs[cpu] =3D new_rq - self.__update_total_load(old_rq, new_rq) - ts_list.append(self) - self.event_cpus =3D [cpu] - - def migrate(self, ts_list, new, old_cpu, new_cpu): - if old_cpu =3D=3D new_cpu: - return - old_rq =3D self.prev.rqs[old_cpu] - out_rq =3D old_rq.migrate_out(new) - self.rqs[old_cpu] =3D out_rq - self.__update_total_load(old_rq, out_rq) - - new_rq =3D self.prev.rqs[new_cpu] - in_rq =3D new_rq.migrate_in(new) - self.rqs[new_cpu] =3D in_rq - self.__update_total_load(new_rq, in_rq) - - ts_list.append(self) - - if old_rq is not out_rq: - self.event_cpus.append(old_cpu) - self.event_cpus.append(new_cpu) - - def wake_up(self, ts_list, pid, cpu, fork): - old_rq =3D self.prev.rqs[cpu] - if fork: - new_rq =3D old_rq.wake_up_new(pid) - else: - new_rq =3D old_rq.wake_up(pid) - - if new_rq is old_rq: - return - self.rqs[cpu] =3D new_rq - self.__update_total_load(old_rq, new_rq) - ts_list.append(self) - self.event_cpus =3D [cpu] - - def next(self, t): - self.end =3D t - return TimeSlice(t, self) - -class TimeSliceList(UserList): - def __init__(self, arg =3D []): - self.data =3D arg - - def get_time_slice(self, ts): - if len(self.data) =3D=3D 0: - slice =3D TimeSlice(ts, TimeSlice(-1, None)) - else: - slice =3D self.data[-1].next(ts) - return slice - - def find_time_slice(self, ts): - start =3D 0 - end =3D len(self.data) - found =3D -1 - searching =3D True - while searching: - if start =3D=3D end or start =3D=3D end - 1: - searching =3D False - - i =3D (end + start) / 2 - if self.data[i].start <=3D ts and self.data[i].end >=3D ts: - found =3D i - end =3D i - continue - - if self.data[i].end < ts: - start =3D i - - elif self.data[i].start > ts: - end =3D i - - return found - - def set_root_win(self, win): - self.root_win =3D win - - def mouse_down(self, cpu, t): - idx =3D self.find_time_slice(t) - if idx =3D=3D -1: - return - - ts =3D self[idx] - rq =3D ts.rqs[cpu] - raw =3D "CPU: %d\n" % cpu - raw +=3D "Last event : %s\n" % rq.event.__repr__() - raw +=3D "Timestamp : %d.%06d\n" % (ts.start / (10 ** 9), (ts.start % (1= 0 ** 9)) / 1000) - raw +=3D "Duration : %6d us\n" % ((ts.end - ts.start) / (10 ** 6)) - raw +=3D "Load =3D %d\n" % rq.load() - for t in rq.tasks: - raw +=3D "%s \n" % thread_name(t) - - self.root_win.update_summary(raw) - - def update_rectangle_cpu(self, slice, cpu): - rq =3D slice.rqs[cpu] - - if slice.total_load !=3D 0: - load_rate =3D rq.load() / float(slice.total_load) - else: - load_rate =3D 0 - - red_power =3D int(0xff - (0xff * load_rate)) - color =3D (0xff, red_power, red_power) - - top_color =3D None - - if cpu in slice.event_cpus: - top_color =3D rq.event.color() - - self.root_win.paint_rectangle_zone(cpu, color, top_color, slice.start, s= lice.end) - - def fill_zone(self, start, end): - i =3D self.find_time_slice(start) - if i =3D=3D -1: - return - - for i in range(i, len(self.data)): - timeslice =3D self.data[i] - if timeslice.start > end: - return - - for cpu in timeslice.rqs: - self.update_rectangle_cpu(timeslice, cpu) - - def interval(self): - if len(self.data) =3D=3D 0: - return (0, 0) - - return (self.data[0].start, self.data[-1].end) - - def nr_rectangles(self): - last_ts =3D self.data[-1] - max_cpu =3D 0 - for cpu in last_ts.rqs: - if cpu > max_cpu: - max_cpu =3D cpu - return max_cpu - - -class SchedEventProxy: - def __init__(self): - self.current_tsk =3D defaultdict(lambda : -1) - self.timeslices =3D TimeSliceList() - - def sched_switch(self, headers, prev_comm, prev_pid, prev_prio, prev_stat= e, - next_comm, next_pid, next_prio): - """ Ensure the task we sched out this cpu is really the one - we logged. Otherwise we may have missed traces """ - - on_cpu_task =3D self.current_tsk[headers.cpu] - - if on_cpu_task !=3D -1 and on_cpu_task !=3D prev_pid: - print("Sched switch event rejected ts: %s cpu: %d prev: %s(%d) next: %s= (%d)" % \ - headers.ts_format(), headers.cpu, prev_comm, prev_pid, next_comm, next= _pid) - - threads[prev_pid] =3D prev_comm - threads[next_pid] =3D next_comm - self.current_tsk[headers.cpu] =3D next_pid - - ts =3D self.timeslices.get_time_slice(headers.ts()) - ts.sched_switch(self.timeslices, prev_pid, prev_state, next_pid, headers= .cpu) - - def migrate(self, headers, pid, prio, orig_cpu, dest_cpu): - ts =3D self.timeslices.get_time_slice(headers.ts()) - ts.migrate(self.timeslices, pid, orig_cpu, dest_cpu) - - def wake_up(self, headers, comm, pid, success, target_cpu, fork): - if success =3D=3D 0: - return - ts =3D self.timeslices.get_time_slice(headers.ts()) - ts.wake_up(self.timeslices, pid, target_cpu, fork) - - -def trace_begin(): - global parser - parser =3D SchedEventProxy() - -def trace_end(): - app =3D wx.App(False) - timeslices =3D parser.timeslices - frame =3D RootFrame(timeslices, "Migration") - app.MainLoop() - -def sched__sched_stat_runtime(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, comm, pid, runtime, vruntime): - pass - -def sched__sched_stat_iowait(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, comm, pid, delay): - pass - -def sched__sched_stat_sleep(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, comm, pid, delay): - pass - -def sched__sched_stat_wait(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, comm, pid, delay): - pass - -def sched__sched_process_fork(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, parent_comm, parent_pid, child_comm, child_pid): - pass - -def sched__sched_process_wait(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, comm, pid, prio): - pass - -def sched__sched_process_exit(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, comm, pid, prio): - pass - -def sched__sched_process_free(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, comm, pid, prio): - pass - -def sched__sched_migrate_task(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, comm, pid, prio, orig_cpu, - dest_cpu): - headers =3D EventHeaders(common_cpu, common_secs, common_nsecs, - common_pid, common_comm, common_callchain) - parser.migrate(headers, pid, prio, orig_cpu, dest_cpu) - -def sched__sched_switch(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, common_callchain, - prev_comm, prev_pid, prev_prio, prev_state, - next_comm, next_pid, next_prio): - - headers =3D EventHeaders(common_cpu, common_secs, common_nsecs, - common_pid, common_comm, common_callchain) - parser.sched_switch(headers, prev_comm, prev_pid, prev_prio, prev_state, - next_comm, next_pid, next_prio) - -def sched__sched_wakeup_new(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, comm, pid, prio, success, - target_cpu): - headers =3D EventHeaders(common_cpu, common_secs, common_nsecs, - common_pid, common_comm, common_callchain) - parser.wake_up(headers, comm, pid, success, target_cpu, 1) - -def sched__sched_wakeup(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, comm, pid, prio, success, - target_cpu): - headers =3D EventHeaders(common_cpu, common_secs, common_nsecs, - common_pid, common_comm, common_callchain) - parser.wake_up(headers, comm, pid, success, target_cpu, 0) - -def sched__sched_wait_task(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, comm, pid, prio): - pass - -def sched__sched_kthread_stop_ret(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, ret): - pass - -def sched__sched_kthread_stop(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, comm, pid): - pass - -def trace_unhandled(event_name, context, event_fields_dict): - pass diff --git a/tools/perf/scripts/python/sctop.py b/tools/perf/scripts/python= /sctop.py deleted file mode 100644 index 6e0278dcb092..000000000000 --- a/tools/perf/scripts/python/sctop.py +++ /dev/null @@ -1,89 +0,0 @@ -# system call top -# (c) 2010, Tom Zanussi -# Licensed under the terms of the GNU GPL License version 2 -# -# Periodically displays system-wide system call totals, broken down by -# syscall. If a [comm] arg is specified, only syscalls called by -# [comm] are displayed. If an [interval] arg is specified, the display -# will be refreshed every [interval] seconds. The default interval is -# 3 seconds. - -from __future__ import print_function - -import os, sys, time - -try: - import thread -except ImportError: - import _thread as thread - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -from perf_trace_context import * -from Core import * -from Util import * - -usage =3D "perf script -s sctop.py [comm] [interval]\n"; - -for_comm =3D None -default_interval =3D 3 -interval =3D default_interval - -if len(sys.argv) > 3: - sys.exit(usage) - -if len(sys.argv) > 2: - for_comm =3D sys.argv[1] - interval =3D int(sys.argv[2]) -elif len(sys.argv) > 1: - try: - interval =3D int(sys.argv[1]) - except ValueError: - for_comm =3D sys.argv[1] - interval =3D default_interval - -syscalls =3D autodict() - -def trace_begin(): - thread.start_new_thread(print_syscall_totals, (interval,)) - pass - -def raw_syscalls__sys_enter(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, id, args): - if for_comm is not None: - if common_comm !=3D for_comm: - return - try: - syscalls[id] +=3D 1 - except TypeError: - syscalls[id] =3D 1 - -def syscalls__sys_enter(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - id, args): - raw_syscalls__sys_enter(**locals()) - -def print_syscall_totals(interval): - while 1: - clear_term() - if for_comm is not None: - print("\nsyscall events for %s:\n" % (for_comm)) - else: - print("\nsyscall events:\n") - - print("%-40s %10s" % ("event", "count")) - print("%-40s %10s" % - ("----------------------------------------", - "----------")) - - for id, val in sorted(syscalls.items(), - key =3D lambda kv: (kv[1], kv[0]), - reverse =3D True): - try: - print("%-40s %10d" % (syscall_name(id), val)) - except TypeError: - pass - syscalls.clear() - time.sleep(interval) diff --git a/tools/perf/scripts/python/stackcollapse.py b/tools/perf/script= s/python/stackcollapse.py deleted file mode 100755 index b1c4def1410a..000000000000 --- a/tools/perf/scripts/python/stackcollapse.py +++ /dev/null @@ -1,127 +0,0 @@ -# stackcollapse.py - format perf samples with one line per distinct call s= tack -# SPDX-License-Identifier: GPL-2.0 -# -# This script's output has two space-separated fields. The first is a sem= icolon -# separated stack including the program name (from the "comm" field) and t= he -# function names from the call stack. The second is a count: -# -# swapper;start_kernel;rest_init;cpu_idle;default_idle;native_safe_halt 2 -# -# The file is sorted according to the first field. -# -# Input may be created and processed using: -# -# perf record -a -g -F 99 sleep 60 -# perf script report stackcollapse > out.stacks-folded -# -# (perf script record stackcollapse works too). -# -# Written by Paolo Bonzini -# Based on Brendan Gregg's stackcollapse-perf.pl script. - -from __future__ import print_function - -import os -import sys -from collections import defaultdict -from optparse import OptionParser, make_option - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -from perf_trace_context import * -from Core import * -from EventClass import * - -# command line parsing - -option_list =3D [ - # formatting options for the bottom entry of the stack - make_option("--include-tid", dest=3D"include_tid", - action=3D"store_true", default=3DFalse, - help=3D"include thread id in stack"), - make_option("--include-pid", dest=3D"include_pid", - action=3D"store_true", default=3DFalse, - help=3D"include process id in stack"), - make_option("--no-comm", dest=3D"include_comm", - action=3D"store_false", default=3DTrue, - help=3D"do not separate stacks according to comm"), - make_option("--tidy-java", dest=3D"tidy_java", - action=3D"store_true", default=3DFalse, - help=3D"beautify Java signatures"), - make_option("--kernel", dest=3D"annotate_kernel", - action=3D"store_true", default=3DFalse, - help=3D"annotate kernel functions with _[k]") -] - -parser =3D OptionParser(option_list=3Doption_list) -(opts, args) =3D parser.parse_args() - -if len(args) !=3D 0: - parser.error("unexpected command line argument") -if opts.include_tid and not opts.include_comm: - parser.error("requesting tid but not comm is invalid") -if opts.include_pid and not opts.include_comm: - parser.error("requesting pid but not comm is invalid") - -# event handlers - -lines =3D defaultdict(lambda: 0) - -def process_event(param_dict): - def tidy_function_name(sym, dso): - if sym is None: - sym =3D '[unknown]' - - sym =3D sym.replace(';', ':') - if opts.tidy_java: - # the original stackcollapse-perf.pl script gives the - # example of converting this: - # Lorg/mozilla/javascript/MemberBox;.(Ljava/lang/refl= ect/Method;)V - # to this: - # org/mozilla/javascript/MemberBox:.init - sym =3D sym.replace('<', '') - sym =3D sym.replace('>', '') - if sym[0] =3D=3D 'L' and sym.find('/'): - sym =3D sym[1:] - try: - sym =3D sym[:sym.index('(')] - except ValueError: - pass - - if opts.annotate_kernel and dso =3D=3D '[kernel.kallsyms]': - return sym + '_[k]' - else: - return sym - - stack =3D list() - if 'callchain' in param_dict: - for entry in param_dict['callchain']: - entry.setdefault('sym', dict()) - entry['sym'].setdefault('name', None) - entry.setdefault('dso', None) - stack.append(tidy_function_name(entry['sym']['name'], - entry['dso'])) - else: - param_dict.setdefault('symbol', None) - param_dict.setdefault('dso', None) - stack.append(tidy_function_name(param_dict['symbol'], - param_dict['dso'])) - - if opts.include_comm: - comm =3D param_dict["comm"].replace(' ', '_') - sep =3D "-" - if opts.include_pid: - comm =3D comm + sep + str(param_dict['sample']['pid']) - sep =3D "/" - if opts.include_tid: - comm =3D comm + sep + str(param_dict['sample']['tid']) - stack.append(comm) - - stack_string =3D ';'.join(reversed(stack)) - lines[stack_string] =3D lines[stack_string] + 1 - -def trace_end(): - list =3D sorted(lines) - for stack in list: - print("%s %d" % (stack, lines[stack])) diff --git a/tools/perf/scripts/python/stat-cpi.py b/tools/perf/scripts/pyt= hon/stat-cpi.py deleted file mode 100644 index 01fa933ff3cf..000000000000 --- a/tools/perf/scripts/python/stat-cpi.py +++ /dev/null @@ -1,79 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -from __future__ import print_function - -data =3D {} -times =3D [] -threads =3D [] -cpus =3D [] - -def get_key(time, event, cpu, thread): - return "%d-%s-%d-%d" % (time, event, cpu, thread) - -def store_key(time, cpu, thread): - if (time not in times): - times.append(time) - - if (cpu not in cpus): - cpus.append(cpu) - - if (thread not in threads): - threads.append(thread) - -def store(time, event, cpu, thread, val, ena, run): - #print("event %s cpu %d, thread %d, time %d, val %d, ena %d, run %d" % - # (event, cpu, thread, time, val, ena, run)) - - store_key(time, cpu, thread) - key =3D get_key(time, event, cpu, thread) - data[key] =3D [ val, ena, run] - -def get(time, event, cpu, thread): - key =3D get_key(time, event, cpu, thread) - return data[key][0] - -def stat__cycles_k(cpu, thread, time, val, ena, run): - store(time, "cycles", cpu, thread, val, ena, run); - -def stat__instructions_k(cpu, thread, time, val, ena, run): - store(time, "instructions", cpu, thread, val, ena, run); - -def stat__cycles_u(cpu, thread, time, val, ena, run): - store(time, "cycles", cpu, thread, val, ena, run); - -def stat__instructions_u(cpu, thread, time, val, ena, run): - store(time, "instructions", cpu, thread, val, ena, run); - -def stat__cycles(cpu, thread, time, val, ena, run): - store(time, "cycles", cpu, thread, val, ena, run); - -def stat__instructions(cpu, thread, time, val, ena, run): - store(time, "instructions", cpu, thread, val, ena, run); - -def stat__interval(time): - for cpu in cpus: - for thread in threads: - cyc =3D get(time, "cycles", cpu, thread) - ins =3D get(time, "instructions", cpu, thread) - cpi =3D 0 - - if ins !=3D 0: - cpi =3D cyc/float(ins) - - print("%15f: cpu %d, thread %d -> cpi %f (%d/%d)" % (time/(flo= at(1000000000)), cpu, thread, cpi, cyc, ins)) - -def trace_end(): - pass -# XXX trace_end callback could be used as an alternative place -# to compute same values as in the script above: -# -# for time in times: -# for cpu in cpus: -# for thread in threads: -# cyc =3D get(time, "cycles", cpu, thread) -# ins =3D get(time, "instructions", cpu, thread) -# -# if ins !=3D 0: -# cpi =3D cyc/float(ins) -# -# print("time %.9f, cpu %d, thread %d -> cpi %f" % (time/(f= loat(1000000000)), cpu, thread, cpi)) diff --git a/tools/perf/scripts/python/syscall-counts-by-pid.py b/tools/per= f/scripts/python/syscall-counts-by-pid.py deleted file mode 100644 index f254e40c6f0f..000000000000 --- a/tools/perf/scripts/python/syscall-counts-by-pid.py +++ /dev/null @@ -1,75 +0,0 @@ -# system call counts, by pid -# (c) 2010, Tom Zanussi -# Licensed under the terms of the GNU GPL License version 2 -# -# Displays system-wide system call totals, broken down by syscall. -# If a [comm] arg is specified, only syscalls called by [comm] are display= ed. - -from __future__ import print_function - -import os, sys - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -from perf_trace_context import * -from Core import * -from Util import syscall_name - -usage =3D "perf script -s syscall-counts-by-pid.py [comm]\n"; - -for_comm =3D None -for_pid =3D None - -if len(sys.argv) > 2: - sys.exit(usage) - -if len(sys.argv) > 1: - try: - for_pid =3D int(sys.argv[1]) - except: - for_comm =3D sys.argv[1] - -syscalls =3D autodict() - -def trace_begin(): - print("Press control+C to stop and show the summary") - -def trace_end(): - print_syscall_totals() - -def raw_syscalls__sys_enter(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, id, args): - if (for_comm and common_comm !=3D for_comm) or \ - (for_pid and common_pid !=3D for_pid ): - return - try: - syscalls[common_comm][common_pid][id] +=3D 1 - except TypeError: - syscalls[common_comm][common_pid][id] =3D 1 - -def syscalls__sys_enter(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - id, args): - raw_syscalls__sys_enter(**locals()) - -def print_syscall_totals(): - if for_comm is not None: - print("\nsyscall events for %s:\n" % (for_comm)) - else: - print("\nsyscall events by comm/pid:\n") - - print("%-40s %10s" % ("comm [pid]/syscalls", "count")) - print("%-40s %10s" % ("----------------------------------------", - "----------")) - - comm_keys =3D syscalls.keys() - for comm in comm_keys: - pid_keys =3D syscalls[comm].keys() - for pid in pid_keys: - print("\n%s [%d]" % (comm, pid)) - id_keys =3D syscalls[comm][pid].keys() - for id, val in sorted(syscalls[comm][pid].items(), - key =3D lambda kv: (kv[1], kv[0]), reverse =3D True): - print(" %-38s %10d" % (syscall_name(id), val)) diff --git a/tools/perf/scripts/python/syscall-counts.py b/tools/perf/scrip= ts/python/syscall-counts.py deleted file mode 100644 index 8adb95ff1664..000000000000 --- a/tools/perf/scripts/python/syscall-counts.py +++ /dev/null @@ -1,65 +0,0 @@ -# system call counts -# (c) 2010, Tom Zanussi -# Licensed under the terms of the GNU GPL License version 2 -# -# Displays system-wide system call totals, broken down by syscall. -# If a [comm] arg is specified, only syscalls called by [comm] are display= ed. - -from __future__ import print_function - -import os -import sys - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -from perf_trace_context import * -from Core import * -from Util import syscall_name - -usage =3D "perf script -s syscall-counts.py [comm]\n"; - -for_comm =3D None - -if len(sys.argv) > 2: - sys.exit(usage) - -if len(sys.argv) > 1: - for_comm =3D sys.argv[1] - -syscalls =3D autodict() - -def trace_begin(): - print("Press control+C to stop and show the summary") - -def trace_end(): - print_syscall_totals() - -def raw_syscalls__sys_enter(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - common_callchain, id, args): - if for_comm is not None: - if common_comm !=3D for_comm: - return - try: - syscalls[id] +=3D 1 - except TypeError: - syscalls[id] =3D 1 - -def syscalls__sys_enter(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, id, args): - raw_syscalls__sys_enter(**locals()) - -def print_syscall_totals(): - if for_comm is not None: - print("\nsyscall events for %s:\n" % (for_comm)) - else: - print("\nsyscall events:\n") - - print("%-40s %10s" % ("event", "count")) - print("%-40s %10s" % ("----------------------------------------", - "-----------")) - - for id, val in sorted(syscalls.items(), - key =3D lambda kv: (kv[1], kv[0]), reverse =3D True): - print("%-40s %10d" % (syscall_name(id), val)) diff --git a/tools/perf/scripts/python/task-analyzer.py b/tools/perf/script= s/python/task-analyzer.py deleted file mode 100755 index 3f1df9894246..000000000000 --- a/tools/perf/scripts/python/task-analyzer.py +++ /dev/null @@ -1,934 +0,0 @@ -# task-analyzer.py - comprehensive perf tasks analysis -# SPDX-License-Identifier: GPL-2.0 -# Copyright (c) 2022, Hagen Paul Pfeifer -# Licensed under the terms of the GNU GPL License version 2 -# -# Usage: -# -# perf record -e sched:sched_switch -a -- sleep 10 -# perf script report task-analyzer -# - -from __future__ import print_function -import sys -import os -import string -import argparse -import decimal - - -sys.path.append( - os.environ["PERF_EXEC_PATH"] + "/scripts/python/Perf-Trace-Util/lib/Pe= rf/Trace" -) -from perf_trace_context import * -from Core import * - -# Definition of possible ASCII color codes -_COLORS =3D { - "grey": "\033[90m", - "red": "\033[91m", - "green": "\033[92m", - "yellow": "\033[93m", - "blue": "\033[94m", - "violet": "\033[95m", - "reset": "\033[0m", -} - -# Columns will have a static size to align everything properly -# Support of 116 days of active update with nano precision -LEN_SWITCHED_IN =3D len("9999999.999999999") # 17 -LEN_SWITCHED_OUT =3D len("9999999.999999999") # 17 -LEN_CPU =3D len("000") -LEN_PID =3D len("maxvalue") # 8 -LEN_TID =3D len("maxvalue") # 8 -LEN_COMM =3D len("max-comms-length") # 16 -LEN_RUNTIME =3D len("999999.999") # 10 -# Support of 3.45 hours of timespans -LEN_OUT_IN =3D len("99999999999.999") # 15 -LEN_OUT_OUT =3D len("99999999999.999") # 15 -LEN_IN_IN =3D len("99999999999.999") # 15 -LEN_IN_OUT =3D len("99999999999.999") # 15 - - -# py2/py3 compatibility layer, see PEP469 -try: - dict.iteritems -except AttributeError: - # py3 - def itervalues(d): - return iter(d.values()) - - def iteritems(d): - return iter(d.items()) - -else: - # py2 - def itervalues(d): - return d.itervalues() - - def iteritems(d): - return d.iteritems() - - -def _check_color(): - global _COLORS - """user enforced no-color or if stdout is no tty we disable colors""" - if sys.stdout.isatty() and args.stdio_color !=3D "never": - return - _COLORS =3D { - "grey": "", - "red": "", - "green": "", - "yellow": "", - "blue": "", - "violet": "", - "reset": "", - } - - -def _parse_args(): - global args - parser =3D argparse.ArgumentParser(description=3D"Analyze tasks behavi= or") - parser.add_argument( - "--time-limit", - default=3D[], - help=3D - "print tasks only in time[s] window e.g" - " --time-limit 123.111:789.222(print all between 123.111 and 789.2= 22)" - " --time-limit 123: (print all from 123)" - " --time-limit :456 (print all until incl. 456)", - ) - parser.add_argument( - "--summary", action=3D"store_true", help=3D"print addtional runtim= e information" - ) - parser.add_argument( - "--summary-only", action=3D"store_true", help=3D"print only summar= y without traces" - ) - parser.add_argument( - "--summary-extended", - action=3D"store_true", - help=3D"print the summary with additional information of max inter= task times" - " relative to the prev task", - ) - parser.add_argument( - "--ns", action=3D"store_true", help=3D"show timestamps in nanoseco= nds" - ) - parser.add_argument( - "--ms", action=3D"store_true", help=3D"show timestamps in millisec= onds" - ) - parser.add_argument( - "--extended-times", - action=3D"store_true", - help=3D"Show the elapsed times between schedule in/schedule out" - " of this task and the schedule in/schedule out of previous oc= currence" - " of the same task", - ) - parser.add_argument( - "--filter-tasks", - default=3D[], - help=3D"filter out unneeded tasks by tid, pid or processname." - " E.g --filter-task 1337,/sbin/init ", - ) - parser.add_argument( - "--limit-to-tasks", - default=3D[], - help=3D"limit output to selected task by tid, pid, processname." - " E.g --limit-to-tasks 1337,/sbin/init", - ) - parser.add_argument( - "--highlight-tasks", - default=3D"", - help=3D"colorize special tasks by their pid/tid/comm." - " E.g. --highlight-tasks 1:red,mutt:yellow" - " Colors available: red,grey,yellow,blue,violet,green", - ) - parser.add_argument( - "--rename-comms-by-tids", - default=3D"", - help=3D"rename task names by using tid (:,:)" - " This option is handy for inexpressive processnames like pyth= on interpreted" - " process. E.g --rename 1337:my-python-app", - ) - parser.add_argument( - "--stdio-color", - default=3D"auto", - choices=3D["always", "never", "auto"], - help=3D"always, never or auto, allowing configuring color output" - " via the command line", - ) - parser.add_argument( - "--csv", - default=3D"", - help=3D"Write trace to file selected by user. Options, like --ns o= r --extended" - "-times are used.", - ) - parser.add_argument( - "--csv-summary", - default=3D"", - help=3D"Write summary to file selected by user. Options, like --ns= or" - " --summary-extended are used.", - ) - args =3D parser.parse_args() - args.tid_renames =3D dict() - - _argument_filter_sanity_check() - _argument_prepare_check() - - -def time_uniter(unit): - picker =3D { - "s": 1, - "ms": 1e3, - "us": 1e6, - "ns": 1e9, - } - return picker[unit] - - -def _init_db(): - global db - db =3D dict() - db["running"] =3D dict() - db["cpu"] =3D dict() - db["tid"] =3D dict() - db["global"] =3D [] - if args.summary or args.summary_extended or args.summary_only: - db["task_info"] =3D dict() - db["runtime_info"] =3D dict() - # min values for summary depending on the header - db["task_info"]["pid"] =3D len("PID") - db["task_info"]["tid"] =3D len("TID") - db["task_info"]["comm"] =3D len("Comm") - db["runtime_info"]["runs"] =3D len("Runs") - db["runtime_info"]["acc"] =3D len("Accumulated") - db["runtime_info"]["max"] =3D len("Max") - db["runtime_info"]["max_at"] =3D len("Max At") - db["runtime_info"]["min"] =3D len("Min") - db["runtime_info"]["mean"] =3D len("Mean") - db["runtime_info"]["median"] =3D len("Median") - if args.summary_extended: - db["inter_times"] =3D dict() - db["inter_times"]["out_in"] =3D len("Out-In") - db["inter_times"]["inter_at"] =3D len("At") - db["inter_times"]["out_out"] =3D len("Out-Out") - db["inter_times"]["in_in"] =3D len("In-In") - db["inter_times"]["in_out"] =3D len("In-Out") - - -def _median(numbers): - """phython3 hat statistics module - we have nothing""" - n =3D len(numbers) - index =3D n // 2 - if n % 2: - return sorted(numbers)[index] - return sum(sorted(numbers)[index - 1 : index + 1]) / 2 - - -def _mean(numbers): - return sum(numbers) / len(numbers) - - -class Timespans(object): - """ - The elapsed time between two occurrences of the same task is being tra= cked with the - help of this class. There are 4 of those Timespans Out-Out, In-Out, Ou= t-In and - In-In. - The first half of the name signals the first time point of the - first task. The second half of the name represents the second - timepoint of the second task. - """ - - def __init__(self): - self._last_start =3D None - self._last_finish =3D None - self.out_out =3D -1 - self.in_out =3D -1 - self.out_in =3D -1 - self.in_in =3D -1 - if args.summary_extended: - self._time_in =3D -1 - self.max_out_in =3D -1 - self.max_at =3D -1 - self.max_in_out =3D -1 - self.max_in_in =3D -1 - self.max_out_out =3D -1 - - def feed(self, task): - """ - Called for every recorded trace event to find process pair and cal= culate the - task timespans. Chronological ordering, feed does not do reordering - """ - if not self._last_finish: - self._last_start =3D task.time_in(time_unit) - self._last_finish =3D task.time_out(time_unit) - return - self._time_in =3D task.time_in() - time_in =3D task.time_in(time_unit) - time_out =3D task.time_out(time_unit) - self.in_in =3D time_in - self._last_start - self.out_in =3D time_in - self._last_finish - self.in_out =3D time_out - self._last_start - self.out_out =3D time_out - self._last_finish - if args.summary_extended: - self._update_max_entries() - self._last_finish =3D task.time_out(time_unit) - self._last_start =3D task.time_in(time_unit) - - def _update_max_entries(self): - if self.in_in > self.max_in_in: - self.max_in_in =3D self.in_in - if self.out_out > self.max_out_out: - self.max_out_out =3D self.out_out - if self.in_out > self.max_in_out: - self.max_in_out =3D self.in_out - if self.out_in > self.max_out_in: - self.max_out_in =3D self.out_in - self.max_at =3D self._time_in - - - -class Summary(object): - """ - Primary instance for calculating the summary output. Processes the who= le trace to - find and memorize relevant data such as mean, max et cetera. This inst= ance handles - dynamic alignment aspects for summary output. - """ - - def __init__(self): - self._body =3D [] - - class AlignmentHelper: - """ - Used to calculated the alignment for the output of the summary. - """ - def __init__(self, pid, tid, comm, runs, acc, mean, - median, min, max, max_at): - self.pid =3D pid - self.tid =3D tid - self.comm =3D comm - self.runs =3D runs - self.acc =3D acc - self.mean =3D mean - self.median =3D median - self.min =3D min - self.max =3D max - self.max_at =3D max_at - if args.summary_extended: - self.out_in =3D None - self.inter_at =3D None - self.out_out =3D None - self.in_in =3D None - self.in_out =3D None - - def _print_header(self): - ''' - Output is trimmed in _format_stats thus additional adjustment in t= he header - is needed, depending on the choice of timeunit. The adjustment cor= responds - to the amount of column titles being adjusted in _column_titles. - ''' - decimal_precision =3D 6 if not args.ns else 9 - fmt =3D " {{:^{}}}".format(sum(db["task_info"].values())) - fmt +=3D " {{:^{}}}".format( - sum(db["runtime_info"].values()) - 2 * decimal_precision - ) - _header =3D ("Task Information", "Runtime Information") - - if args.summary_extended: - fmt +=3D " {{:^{}}}".format( - sum(db["inter_times"].values()) - 4 * decimal_precision - ) - _header +=3D ("Max Inter Task Times",) - fd_sum.write(fmt.format(*_header) + "\n") - - def _column_titles(self): - """ - Cells are being processed and displayed in different way so an ali= gnment adjust - is implemented depeding on the choice of the timeunit. The positio= ns of the max - values are being displayed in grey. Thus in their format two addit= ional {}, - are placed for color set and reset. - """ - separator, fix_csv_align =3D _prepare_fmt_sep() - decimal_precision, time_precision =3D _prepare_fmt_precision() - fmt =3D "{{:>{}}}".format(db["task_info"]["pid"] * fix_csv_align) - fmt +=3D "{}{{:>{}}}".format(separator, db["task_info"]["tid"] * f= ix_csv_align) - fmt +=3D "{}{{:>{}}}".format(separator, db["task_info"]["comm"] * = fix_csv_align) - fmt +=3D "{}{{:>{}}}".format(separator, db["runtime_info"]["runs"]= * fix_csv_align) - fmt +=3D "{}{{:>{}}}".format(separator, db["runtime_info"]["acc"] = * fix_csv_align) - fmt +=3D "{}{{:>{}}}".format(separator, db["runtime_info"]["mean"]= * fix_csv_align) - fmt +=3D "{}{{:>{}}}".format( - separator, db["runtime_info"]["median"] * fix_csv_align - ) - fmt +=3D "{}{{:>{}}}".format( - separator, (db["runtime_info"]["min"] - decimal_precision) * f= ix_csv_align - ) - fmt +=3D "{}{{:>{}}}".format( - separator, (db["runtime_info"]["max"] - decimal_precision) * f= ix_csv_align - ) - fmt +=3D "{}{{}}{{:>{}}}{{}}".format( - separator, (db["runtime_info"]["max_at"] - time_precision) * f= ix_csv_align - ) - - column_titles =3D ("PID", "TID", "Comm") - column_titles +=3D ("Runs", "Accumulated", "Mean", "Median", "Min"= , "Max") - column_titles +=3D (_COLORS["grey"], "Max At", _COLORS["reset"]) - - if args.summary_extended: - fmt +=3D "{}{{:>{}}}".format( - separator, - (db["inter_times"]["out_in"] - decimal_precision) * fix_cs= v_align - ) - fmt +=3D "{}{{}}{{:>{}}}{{}}".format( - separator, - (db["inter_times"]["inter_at"] - time_precision) * fix_csv= _align - ) - fmt +=3D "{}{{:>{}}}".format( - separator, - (db["inter_times"]["out_out"] - decimal_precision) * fix_c= sv_align - ) - fmt +=3D "{}{{:>{}}}".format( - separator, - (db["inter_times"]["in_in"] - decimal_precision) * fix_csv= _align - ) - fmt +=3D "{}{{:>{}}}".format( - separator, - (db["inter_times"]["in_out"] - decimal_precision) * fix_cs= v_align - ) - - column_titles +=3D ("Out-In", _COLORS["grey"], "Max At", _COLO= RS["reset"], - "Out-Out", "In-In", "In-Out") - - fd_sum.write(fmt.format(*column_titles) + "\n") - - - def _task_stats(self): - """calculates the stats of every task and constructs the printable= summary""" - for tid in sorted(db["tid"]): - color_one_sample =3D _COLORS["grey"] - color_reset =3D _COLORS["reset"] - no_executed =3D 0 - runtimes =3D [] - time_in =3D [] - timespans =3D Timespans() - for task in db["tid"][tid]: - pid =3D task.pid - comm =3D task.comm - no_executed +=3D 1 - runtimes.append(task.runtime(time_unit)) - time_in.append(task.time_in()) - timespans.feed(task) - if len(runtimes) > 1: - color_one_sample =3D "" - color_reset =3D "" - time_max =3D max(runtimes) - time_min =3D min(runtimes) - max_at =3D time_in[runtimes.index(max(runtimes))] - - # The size of the decimal after sum,mean and median varies, th= us we cut - # the decimal number, by rounding it. It has no impact on the = output, - # because we have a precision of the decimal points at the out= put. - time_sum =3D round(sum(runtimes), 3) - time_mean =3D round(_mean(runtimes), 3) - time_median =3D round(_median(runtimes), 3) - - align_helper =3D self.AlignmentHelper(pid, tid, comm, no_execu= ted, time_sum, - time_mean, time_median, time_min, time= _max, max_at) - self._body.append([pid, tid, comm, no_executed, time_sum, colo= r_one_sample, - time_mean, time_median, time_min, time_max, - _COLORS["grey"], max_at, _COLORS["reset"],= color_reset]) - if args.summary_extended: - self._body[-1].extend([timespans.max_out_in, - _COLORS["grey"], timespans.max_at, - _COLORS["reset"], timespans.max_out_out, - timespans.max_in_in, - timespans.max_in_out]) - align_helper.out_in =3D timespans.max_out_in - align_helper.inter_at =3D timespans.max_at - align_helper.out_out =3D timespans.max_out_out - align_helper.in_in =3D timespans.max_in_in - align_helper.in_out =3D timespans.max_in_out - self._calc_alignments_summary(align_helper) - - def _format_stats(self): - separator, fix_csv_align =3D _prepare_fmt_sep() - decimal_precision, time_precision =3D _prepare_fmt_precision() - len_pid =3D db["task_info"]["pid"] * fix_csv_align - len_tid =3D db["task_info"]["tid"] * fix_csv_align - len_comm =3D db["task_info"]["comm"] * fix_csv_align - len_runs =3D db["runtime_info"]["runs"] * fix_csv_align - len_acc =3D db["runtime_info"]["acc"] * fix_csv_align - len_mean =3D db["runtime_info"]["mean"] * fix_csv_align - len_median =3D db["runtime_info"]["median"] * fix_csv_align - len_min =3D (db["runtime_info"]["min"] - decimal_precision) * fix_= csv_align - len_max =3D (db["runtime_info"]["max"] - decimal_precision) * fix_= csv_align - len_max_at =3D (db["runtime_info"]["max_at"] - time_precision) * f= ix_csv_align - if args.summary_extended: - len_out_in =3D ( - db["inter_times"]["out_in"] - decimal_precision - ) * fix_csv_align - len_inter_at =3D ( - db["inter_times"]["inter_at"] - time_precision - ) * fix_csv_align - len_out_out =3D ( - db["inter_times"]["out_out"] - decimal_precision - ) * fix_csv_align - len_in_in =3D (db["inter_times"]["in_in"] - decimal_precision)= * fix_csv_align - len_in_out =3D ( - db["inter_times"]["in_out"] - decimal_precision - ) * fix_csv_align - - fmt =3D "{{:{}d}}".format(len_pid) - fmt +=3D "{}{{:{}d}}".format(separator, len_tid) - fmt +=3D "{}{{:>{}}}".format(separator, len_comm) - fmt +=3D "{}{{:{}d}}".format(separator, len_runs) - fmt +=3D "{}{{:{}.{}f}}".format(separator, len_acc, time_precision) - fmt +=3D "{}{{}}{{:{}.{}f}}".format(separator, len_mean, time_prec= ision) - fmt +=3D "{}{{:{}.{}f}}".format(separator, len_median, time_precis= ion) - fmt +=3D "{}{{:{}.{}f}}".format(separator, len_min, time_precision) - fmt +=3D "{}{{:{}.{}f}}".format(separator, len_max, time_precision) - fmt +=3D "{}{{}}{{:{}.{}f}}{{}}{{}}".format( - separator, len_max_at, decimal_precision - ) - if args.summary_extended: - fmt +=3D "{}{{:{}.{}f}}".format(separator, len_out_in, time_pr= ecision) - fmt +=3D "{}{{}}{{:{}.{}f}}{{}}".format( - separator, len_inter_at, decimal_precision - ) - fmt +=3D "{}{{:{}.{}f}}".format(separator, len_out_out, time_p= recision) - fmt +=3D "{}{{:{}.{}f}}".format(separator, len_in_in, time_pre= cision) - fmt +=3D "{}{{:{}.{}f}}".format(separator, len_in_out, time_pr= ecision) - return fmt - - - def _calc_alignments_summary(self, align_helper): - # Length is being cut in 3 groups so that further addition is easi= er to handle. - # The length of every argument from the alignment helper is being = checked if it - # is longer than the longest until now. In that case the length is= being saved. - for key in db["task_info"]: - if len(str(getattr(align_helper, key))) > db["task_info"][key]: - db["task_info"][key] =3D len(str(getattr(align_helper, key= ))) - for key in db["runtime_info"]: - if len(str(getattr(align_helper, key))) > db["runtime_info"][k= ey]: - db["runtime_info"][key] =3D len(str(getattr(align_helper, = key))) - if args.summary_extended: - for key in db["inter_times"]: - if len(str(getattr(align_helper, key))) > db["inter_times"= ][key]: - db["inter_times"][key] =3D len(str(getattr(align_helpe= r, key))) - - - def print(self): - self._task_stats() - fmt =3D self._format_stats() - - if not args.csv_summary: - print("\nSummary") - self._print_header() - self._column_titles() - for i in range(len(self._body)): - fd_sum.write(fmt.format(*tuple(self._body[i])) + "\n") - - - -class Task(object): - """ The class is used to handle the information of a given task.""" - - def __init__(self, id, tid, cpu, comm): - self.id =3D id - self.tid =3D tid - self.cpu =3D cpu - self.comm =3D comm - self.pid =3D None - self._time_in =3D None - self._time_out =3D None - - def schedule_in_at(self, time): - """set the time where the task was scheduled in""" - self._time_in =3D time - - def schedule_out_at(self, time): - """set the time where the task was scheduled out""" - self._time_out =3D time - - def time_out(self, unit=3D"s"): - """return time where a given task was scheduled out""" - factor =3D time_uniter(unit) - return self._time_out * decimal.Decimal(factor) - - def time_in(self, unit=3D"s"): - """return time where a given task was scheduled in""" - factor =3D time_uniter(unit) - return self._time_in * decimal.Decimal(factor) - - def runtime(self, unit=3D"us"): - factor =3D time_uniter(unit) - return (self._time_out - self._time_in) * decimal.Decimal(factor) - - def update_pid(self, pid): - self.pid =3D pid - - -def _task_id(pid, cpu): - """returns a "unique-enough" identifier, please do not change""" - return "{}-{}".format(pid, cpu) - - -def _filter_non_printable(unfiltered): - """comm names may contain loony chars like '\x00000'""" - filtered =3D "" - for char in unfiltered: - if char not in string.printable: - continue - filtered +=3D char - return filtered - - -def _fmt_header(): - separator, fix_csv_align =3D _prepare_fmt_sep() - fmt =3D "{{:>{}}}".format(LEN_SWITCHED_IN*fix_csv_align) - fmt +=3D "{}{{:>{}}}".format(separator, LEN_SWITCHED_OUT*fix_csv_align) - fmt +=3D "{}{{:>{}}}".format(separator, LEN_CPU*fix_csv_align) - fmt +=3D "{}{{:>{}}}".format(separator, LEN_PID*fix_csv_align) - fmt +=3D "{}{{:>{}}}".format(separator, LEN_TID*fix_csv_align) - fmt +=3D "{}{{:>{}}}".format(separator, LEN_COMM*fix_csv_align) - fmt +=3D "{}{{:>{}}}".format(separator, LEN_RUNTIME*fix_csv_align) - fmt +=3D "{}{{:>{}}}".format(separator, LEN_OUT_IN*fix_csv_align) - if args.extended_times: - fmt +=3D "{}{{:>{}}}".format(separator, LEN_OUT_OUT*fix_csv_align) - fmt +=3D "{}{{:>{}}}".format(separator, LEN_IN_IN*fix_csv_align) - fmt +=3D "{}{{:>{}}}".format(separator, LEN_IN_OUT*fix_csv_align) - return fmt - - -def _fmt_body(): - separator, fix_csv_align =3D _prepare_fmt_sep() - decimal_precision, time_precision =3D _prepare_fmt_precision() - fmt =3D "{{}}{{:{}.{}f}}".format(LEN_SWITCHED_IN*fix_csv_align, decima= l_precision) - fmt +=3D "{}{{:{}.{}f}}".format( - separator, LEN_SWITCHED_OUT*fix_csv_align, decimal_precision - ) - fmt +=3D "{}{{:{}d}}".format(separator, LEN_CPU*fix_csv_align) - fmt +=3D "{}{{:{}d}}".format(separator, LEN_PID*fix_csv_align) - fmt +=3D "{}{{}}{{:{}d}}{{}}".format(separator, LEN_TID*fix_csv_align) - fmt +=3D "{}{{}}{{:>{}}}".format(separator, LEN_COMM*fix_csv_align) - fmt +=3D "{}{{:{}.{}f}}".format(separator, LEN_RUNTIME*fix_csv_align, = time_precision) - if args.extended_times: - fmt +=3D "{}{{:{}.{}f}}".format(separator, LEN_OUT_IN*fix_csv_alig= n, time_precision) - fmt +=3D "{}{{:{}.{}f}}".format(separator, LEN_OUT_OUT*fix_csv_ali= gn, time_precision) - fmt +=3D "{}{{:{}.{}f}}".format(separator, LEN_IN_IN*fix_csv_align= , time_precision) - fmt +=3D "{}{{:{}.{}f}}{{}}".format( - separator, LEN_IN_OUT*fix_csv_align, time_precision - ) - else: - fmt +=3D "{}{{:{}.{}f}}{{}}".format( - separator, LEN_OUT_IN*fix_csv_align, time_precision - ) - return fmt - - -def _print_header(): - fmt =3D _fmt_header() - header =3D ("Switched-In", "Switched-Out", "CPU", "PID", "TID", "Comm"= , "Runtime", - "Time Out-In") - if args.extended_times: - header +=3D ("Time Out-Out", "Time In-In", "Time In-Out") - fd_task.write(fmt.format(*header) + "\n") - - - -def _print_task_finish(task): - """calculating every entry of a row and printing it immediately""" - c_row_set =3D "" - c_row_reset =3D "" - out_in =3D -1 - out_out =3D -1 - in_in =3D -1 - in_out =3D -1 - fmt =3D _fmt_body() - # depending on user provided highlight option we change the color - # for particular tasks - if str(task.tid) in args.highlight_tasks_map: - c_row_set =3D _COLORS[args.highlight_tasks_map[str(task.tid)]] - c_row_reset =3D _COLORS["reset"] - if task.comm in args.highlight_tasks_map: - c_row_set =3D _COLORS[args.highlight_tasks_map[task.comm]] - c_row_reset =3D _COLORS["reset"] - # grey-out entries if PID =3D=3D TID, they - # are identical, no threaded model so the - # thread id (tid) do not matter - c_tid_set =3D "" - c_tid_reset =3D "" - if task.pid =3D=3D task.tid: - c_tid_set =3D _COLORS["grey"] - c_tid_reset =3D _COLORS["reset"] - if task.tid in db["tid"]: - # get last task of tid - last_tid_task =3D db["tid"][task.tid][-1] - # feed the timespan calculate, last in tid db - # and second the current one - timespan_gap_tid =3D Timespans() - timespan_gap_tid.feed(last_tid_task) - timespan_gap_tid.feed(task) - out_in =3D timespan_gap_tid.out_in - out_out =3D timespan_gap_tid.out_out - in_in =3D timespan_gap_tid.in_in - in_out =3D timespan_gap_tid.in_out - - - if args.extended_times: - line_out =3D fmt.format(c_row_set, task.time_in(), task.time_out()= , task.cpu, - task.pid, c_tid_set, task.tid, c_tid_reset, c_row_= set, task.comm, - task.runtime(time_unit), out_in, out_out, in_in, i= n_out, - c_row_reset) + "\n" - else: - line_out =3D fmt.format(c_row_set, task.time_in(), task.time_out()= , task.cpu, - task.pid, c_tid_set, task.tid, c_tid_reset, c_row_= set, task.comm, - task.runtime(time_unit), out_in, c_row_reset) + "\= n" - try: - fd_task.write(line_out) - except(IOError): - # don't mangle the output if user SIGINT this script - sys.exit() - -def _record_cleanup(_list): - """ - no need to store more then one element if --summarize - is not enabled - """ - if not args.summary and len(_list) > 1: - _list =3D _list[len(_list) - 1 :] - - -def _record_by_tid(task): - tid =3D task.tid - if tid not in db["tid"]: - db["tid"][tid] =3D [] - db["tid"][tid].append(task) - _record_cleanup(db["tid"][tid]) - - -def _record_by_cpu(task): - cpu =3D task.cpu - if cpu not in db["cpu"]: - db["cpu"][cpu] =3D [] - db["cpu"][cpu].append(task) - _record_cleanup(db["cpu"][cpu]) - - -def _record_global(task): - """record all executed task, ordered by finish chronological""" - db["global"].append(task) - _record_cleanup(db["global"]) - - -def _handle_task_finish(tid, cpu, time, perf_sample_dict): - if tid =3D=3D 0: - return - _id =3D _task_id(tid, cpu) - if _id not in db["running"]: - # may happen, if we missed the switch to - # event. Seen in combination with --exclude-perf - # where the start is filtered out, but not the - # switched in. Probably a bug in exclude-perf - # option. - return - task =3D db["running"][_id] - task.schedule_out_at(time) - - # record tid, during schedule in the tid - # is not available, update now - pid =3D int(perf_sample_dict["sample"]["pid"]) - - task.update_pid(pid) - del db["running"][_id] - - # print only tasks which are not being filtered and no print of trace - # for summary only, but record every task. - if not _limit_filtered(tid, pid, task.comm) and not args.summary_only: - _print_task_finish(task) - _record_by_tid(task) - _record_by_cpu(task) - _record_global(task) - - -def _handle_task_start(tid, cpu, comm, time): - if tid =3D=3D 0: - return - if tid in args.tid_renames: - comm =3D args.tid_renames[tid] - _id =3D _task_id(tid, cpu) - if _id in db["running"]: - # handle corner cases where already running tasks - # are switched-to again - saw this via --exclude-perf - # recorded traces. We simple ignore this "second start" - # event. - return - assert _id not in db["running"] - task =3D Task(_id, tid, cpu, comm) - task.schedule_in_at(time) - db["running"][_id] =3D task - - -def _time_to_internal(time_ns): - """ - To prevent float rounding errors we use Decimal internally - """ - return decimal.Decimal(time_ns) / decimal.Decimal(1e9) - - -def _limit_filtered(tid, pid, comm): - if args.filter_tasks: - if str(tid) in args.filter_tasks or comm in args.filter_tasks: - return True - else: - return False - if args.limit_to_tasks: - if str(tid) in args.limit_to_tasks or comm in args.limit_to_tasks: - return False - else: - return True - - -def _argument_filter_sanity_check(): - if args.limit_to_tasks and args.filter_tasks: - sys.exit("Error: Filter and Limit at the same time active.") - if args.extended_times and args.summary_only: - sys.exit("Error: Summary only and extended times active.") - if args.time_limit and ":" not in args.time_limit: - sys.exit( - "Error: No bound set for time limit. Please set bound by ':' e= .g :123." - ) - if args.time_limit and (args.summary or args.summary_only or args.summ= ary_extended): - sys.exit("Error: Cannot set time limit and print summary") - if args.csv_summary: - args.summary =3D True - if args.csv =3D=3D args.csv_summary: - sys.exit("Error: Chosen files for csv and csv summary are the = same") - if args.csv and (args.summary_extended or args.summary) and not args.c= sv_summary: - sys.exit("Error: No file chosen to write summary to. Choose with -= -csv-summary " - "") - if args.csv and args.summary_only: - sys.exit("Error: --csv chosen and --summary-only. Standard task wo= uld not be" - "written to csv file.") - -def _argument_prepare_check(): - global time_unit, fd_task, fd_sum - if args.filter_tasks: - args.filter_tasks =3D args.filter_tasks.split(",") - if args.limit_to_tasks: - args.limit_to_tasks =3D args.limit_to_tasks.split(",") - if args.time_limit: - args.time_limit =3D args.time_limit.split(":") - for rename_tuple in args.rename_comms_by_tids.split(","): - tid_name =3D rename_tuple.split(":") - if len(tid_name) !=3D 2: - continue - args.tid_renames[int(tid_name[0])] =3D tid_name[1] - args.highlight_tasks_map =3D dict() - for highlight_tasks_tuple in args.highlight_tasks.split(","): - tasks_color_map =3D highlight_tasks_tuple.split(":") - # default highlight color to red if no color set by user - if len(tasks_color_map) =3D=3D 1: - tasks_color_map.append("red") - if args.highlight_tasks and tasks_color_map[1].lower() not in _COL= ORS: - sys.exit( - "Error: Color not defined, please choose from grey,red,gre= en,yellow,blue," - "violet" - ) - if len(tasks_color_map) !=3D 2: - continue - args.highlight_tasks_map[tasks_color_map[0]] =3D tasks_color_map[1] - time_unit =3D "us" - if args.ns: - time_unit =3D "ns" - elif args.ms: - time_unit =3D "ms" - - - fd_task =3D sys.stdout - if args.csv: - args.stdio_color =3D "never" - fd_task =3D open(args.csv, "w") - print("generating csv at",args.csv,) - - fd_sum =3D sys.stdout - if args.csv_summary: - args.stdio_color =3D "never" - fd_sum =3D open(args.csv_summary, "w") - print("generating csv summary at",args.csv_summary) - if not args.csv: - args.summary_only =3D True - - -def _is_within_timelimit(time): - """ - Check if a time limit was given by parameter, if so ignore the rest. I= f not, - process the recorded trace in its entirety. - """ - if not args.time_limit: - return True - lower_time_limit =3D args.time_limit[0] - upper_time_limit =3D args.time_limit[1] - # check for upper limit - if upper_time_limit =3D=3D "": - if time >=3D decimal.Decimal(lower_time_limit): - return True - # check for lower limit - if lower_time_limit =3D=3D "": - if time <=3D decimal.Decimal(upper_time_limit): - return True - # quit if time exceeds upper limit. Good for big datasets - else: - quit() - if lower_time_limit !=3D "" and upper_time_limit !=3D "": - if (time >=3D decimal.Decimal(lower_time_limit) and - time <=3D decimal.Decimal(upper_time_limit)): - return True - # quit if time exceeds upper limit. Good for big datasets - elif time > decimal.Decimal(upper_time_limit): - quit() - -def _prepare_fmt_precision(): - decimal_precision =3D 6 - time_precision =3D 3 - if args.ns: - decimal_precision =3D 9 - time_precision =3D 0 - return decimal_precision, time_precision - -def _prepare_fmt_sep(): - separator =3D " " - fix_csv_align =3D 1 - if args.csv or args.csv_summary: - separator =3D ";" - fix_csv_align =3D 0 - return separator, fix_csv_align - -def trace_unhandled(event_name, context, event_fields_dict, perf_sample_di= ct): - pass - - -def trace_begin(): - _parse_args() - _check_color() - _init_db() - if not args.summary_only: - _print_header() - -def trace_end(): - if args.summary or args.summary_extended or args.summary_only: - Summary().print() - -def sched__sched_switch(event_name, context, common_cpu, common_secs, comm= on_nsecs, - common_pid, common_comm, common_callchain, prev_co= mm, - prev_pid, prev_prio, prev_state, next_comm, next_p= id, - next_prio, perf_sample_dict): - # ignore common_secs & common_nsecs cause we need - # high res timestamp anyway, using the raw value is - # faster - time =3D _time_to_internal(perf_sample_dict["sample"]["time"]) - if not _is_within_timelimit(time): - # user specific --time-limit a:b set - return - - next_comm =3D _filter_non_printable(next_comm) - _handle_task_finish(prev_pid, common_cpu, time, perf_sample_dict) - _handle_task_start(next_pid, common_cpu, next_comm, time) diff --git a/tools/perf/tests/shell/script_python.sh b/tools/perf/tests/she= ll/script_python.sh deleted file mode 100755 index 6bc66074a31f..000000000000 --- a/tools/perf/tests/shell/script_python.sh +++ /dev/null @@ -1,113 +0,0 @@ -#!/bin/bash -# perf script python tests -# SPDX-License-Identifier: GPL-2.0 - -set -e - -# set PERF_EXEC_PATH to find scripts in the source directory -perfdir=3D$(dirname "$0")/../.. -if [ -e "$perfdir/scripts/python/Perf-Trace-Util" ]; then - export PERF_EXEC_PATH=3D$perfdir -fi - - -perfdata=3D$(mktemp /tmp/__perf_test_script_python.perf.data.XXXXX) -generated_script=3D$(mktemp /tmp/__perf_test_script.XXXXX.py) - -cleanup() { - rm -f "${perfdata}" - rm -f "${generated_script}" - trap - EXIT TERM INT -} - -trap_cleanup() { - echo "Unexpected signal in ${FUNCNAME[1]}" - cleanup - exit 1 -} -trap trap_cleanup TERM INT -trap cleanup EXIT - -check_python_support() { - if perf check feature -q libpython; then - return 0 - fi - echo "perf script python test [Skipped: no libpython support]" - return 2 -} - -test_script() { - local event_name=3D$1 - local expected_output=3D$2 - local record_opts=3D$3 - - echo "Testing event: $event_name" - - # Try to record. If this fails, it might be permissions or lack of - # support. Return 2 to indicate "skip this event" rather than "fail - # test". - if ! perf record -o "${perfdata}" -e "$event_name" $record_opts -- perf t= est -w thloop > /dev/null 2>&1; then - echo "perf script python test [Skipped: failed to record $event_name]" - return 2 - fi - - echo "Generating python script..." - if ! perf script -i "${perfdata}" -g "${generated_script}"; then - echo "perf script python test [Failed: script generation for $event_name= ]" - return 1 - fi - - if [ ! -f "${generated_script}" ]; then - echo "perf script python test [Failed: script not generated for $event_n= ame]" - return 1 - fi - - # Perf script -g python doesn't generate process_event for generic - # events so append it manually to test that the callback works. - if ! grep -q "def process_event" "${generated_script}"; then - cat <> "${generated_script}" - -def process_event(param_dict): - print("param_dict: %s" % param_dict) -EOF - fi - - echo "Executing python script..." - output=3D$(perf script -i "${perfdata}" -s "${generated_script}" 2>&1) - - if echo "$output" | grep -q "$expected_output"; then - echo "perf script python test [Success: $event_name triggered $expected_= output]" - return 0 - else - echo "perf script python test [Failed: $event_name did not trigger $expe= cted_output]" - echo "Output was:" - echo "$output" | head -n 20 - return 1 - fi -} - -check_python_support || exit 2 - -# Try tracepoint first -test_script "sched:sched_switch" "sched__sched_switch" "-c 1" && res=3D0 |= | res=3D$? - -if [ $res -eq 0 ]; then - exit 0 -elif [ $res -eq 1 ]; then - exit 1 -fi - -# If tracepoint skipped (res=3D2), try task-clock -# For generic events like task-clock, the generated script uses process_ev= ent() -# which prints the param_dict. -test_script "task-clock" "param_dict" "-c 100" && res=3D0 || res=3D$? - -if [ $res -eq 0 ]; then - exit 0 -elif [ $res -eq 1 ]; then - exit 1 -fi - -# If both skipped -echo "perf script python test [Skipped: Could not record tracepoint or tas= k-clock]" -exit 2 diff --git a/tools/perf/util/scripting-engines/Build b/tools/perf/util/scri= pting-engines/Build index ce14ef44b200..54920e7e1d5d 100644 --- a/tools/perf/util/scripting-engines/Build +++ b/tools/perf/util/scripting-engines/Build @@ -1,7 +1 @@ - -perf-util-$(CONFIG_LIBPYTHON) +=3D trace-event-python.o - - - -# -Wno-declaration-after-statement: The python headers have mixed code wit= h declarations (decls after asserts, for instance) -CFLAGS_trace-event-python.o +=3D $(PYTHON_EMBED_CCOPTS) -Wno-redundant-dec= ls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-deprecated= -declarations -Wno-switch-enum -Wno-declaration-after-statement +# No embedded scripting engines diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools= /perf/util/scripting-engines/trace-event-python.c deleted file mode 100644 index 5a30caaec73e..000000000000 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ /dev/null @@ -1,2209 +0,0 @@ -/* - * trace-event-python. Feed trace events to an embedded Python interprete= r. - * - * Copyright (C) 2010 Tom Zanussi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 = USA - * - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_LIBTRACEEVENT -#include -#endif - -#include "../build-id.h" -#include "../counts.h" -#include "../debug.h" -#include "../dso.h" -#include "../callchain.h" -#include "../env.h" -#include "../evsel.h" -#include "../event.h" -#include "../thread.h" -#include "../comm.h" -#include "../machine.h" -#include "../mem-info.h" -#include "../db-export.h" -#include "../thread-stack.h" -#include "../trace-event.h" -#include "../call-path.h" -#include "dwarf-regs.h" -#include "map.h" -#include "symbol.h" -#include "thread_map.h" -#include "print_binary.h" -#include "stat.h" -#include "mem-events.h" -#include "util/perf_regs.h" - -#define _PyUnicode_FromString(arg) \ - PyUnicode_FromString(arg) -#define _PyUnicode_FromStringAndSize(arg1, arg2) \ - PyUnicode_FromStringAndSize((arg1), (arg2)) -#define _PyBytes_FromStringAndSize(arg1, arg2) \ - PyBytes_FromStringAndSize((arg1), (arg2)) -#define _PyLong_FromLong(arg) \ - PyLong_FromLong(arg) -#define _PyLong_AsLong(arg) \ - PyLong_AsLong(arg) -#define _PyCapsule_New(arg1, arg2, arg3) \ - PyCapsule_New((arg1), (arg2), (arg3)) - -PyMODINIT_FUNC PyInit_perf_trace_context(void); - -#ifdef HAVE_LIBTRACEEVENT -#define TRACE_EVENT_TYPE_MAX \ - ((1 << (sizeof(unsigned short) * 8)) - 1) - -#define N_COMMON_FIELDS 7 - -static char *cur_field_name; -static int zero_flag_atom; -#endif - -#define MAX_FIELDS 64 - -extern struct scripting_context *scripting_context; - -static PyObject *main_module, *main_dict; - -struct tables { - struct db_export dbe; - PyObject *evsel_handler; - PyObject *machine_handler; - PyObject *thread_handler; - PyObject *comm_handler; - PyObject *comm_thread_handler; - PyObject *dso_handler; - PyObject *symbol_handler; - PyObject *branch_type_handler; - PyObject *sample_handler; - PyObject *call_path_handler; - PyObject *call_return_handler; - PyObject *synth_handler; - PyObject *context_switch_handler; - bool db_export_mode; -}; - -static struct tables tables_global; - -static void handler_call_die(const char *handler_name) __noreturn; -static void handler_call_die(const char *handler_name) -{ - PyErr_Print(); - Py_FatalError("problem in Python trace event handler"); - // Py_FatalError does not return - // but we have to make the compiler happy - abort(); -} - -/* - * Insert val into the dictionary and decrement the reference counter. - * This is necessary for dictionaries since PyDict_SetItemString() does not - * steal a reference, as opposed to PyTuple_SetItem(). - */ -static void pydict_set_item_string_decref(PyObject *dict, const char *key,= PyObject *val) -{ - PyDict_SetItemString(dict, key, val); - Py_DECREF(val); -} - -static PyObject *get_handler(const char *handler_name) -{ - PyObject *handler; - - handler =3D PyDict_GetItemString(main_dict, handler_name); - if (handler && !PyCallable_Check(handler)) - return NULL; - return handler; -} - -static void call_object(PyObject *handler, PyObject *args, const char *die= _msg) -{ - PyObject *retval; - - retval =3D PyObject_CallObject(handler, args); - if (retval =3D=3D NULL) - handler_call_die(die_msg); - Py_DECREF(retval); -} - -static void try_call_object(const char *handler_name, PyObject *args) -{ - PyObject *handler; - - handler =3D get_handler(handler_name); - if (handler) - call_object(handler, args, handler_name); -} - -#ifdef HAVE_LIBTRACEEVENT -static int get_argument_count(PyObject *handler) -{ - int arg_count =3D 0; - - PyObject *code_obj =3D code_obj =3D PyObject_GetAttrString(handler, "__co= de__"); - PyErr_Clear(); - if (code_obj) { - PyObject *arg_count_obj =3D PyObject_GetAttrString(code_obj, - "co_argcount"); - if (arg_count_obj) { - arg_count =3D (int) _PyLong_AsLong(arg_count_obj); - Py_DECREF(arg_count_obj); - } - Py_DECREF(code_obj); - } - return arg_count; -} - -static void define_value(enum tep_print_arg_type field_type, - const char *ev_name, - const char *field_name, - const char *field_value, - const char *field_str) -{ - const char *handler_name =3D "define_flag_value"; - PyObject *t; - unsigned long long value; - unsigned n =3D 0; - - if (field_type =3D=3D TEP_PRINT_SYMBOL) - handler_name =3D "define_symbolic_value"; - - t =3D PyTuple_New(4); - if (!t) - Py_FatalError("couldn't create Python tuple"); - - value =3D eval_flag(field_value); - - PyTuple_SetItem(t, n++, _PyUnicode_FromString(ev_name)); - PyTuple_SetItem(t, n++, _PyUnicode_FromString(field_name)); - PyTuple_SetItem(t, n++, _PyLong_FromLong(value)); - PyTuple_SetItem(t, n++, _PyUnicode_FromString(field_str)); - - try_call_object(handler_name, t); - - Py_DECREF(t); -} - -static void define_values(enum tep_print_arg_type field_type, - struct tep_print_flag_sym *field, - const char *ev_name, - const char *field_name) -{ - define_value(field_type, ev_name, field_name, field->value, - field->str); - - if (field->next) - define_values(field_type, field->next, ev_name, field_name); -} - -static void define_field(enum tep_print_arg_type field_type, - const char *ev_name, - const char *field_name, - const char *delim) -{ - const char *handler_name =3D "define_flag_field"; - PyObject *t; - unsigned n =3D 0; - - if (field_type =3D=3D TEP_PRINT_SYMBOL) - handler_name =3D "define_symbolic_field"; - - if (field_type =3D=3D TEP_PRINT_FLAGS) - t =3D PyTuple_New(3); - else - t =3D PyTuple_New(2); - if (!t) - Py_FatalError("couldn't create Python tuple"); - - PyTuple_SetItem(t, n++, _PyUnicode_FromString(ev_name)); - PyTuple_SetItem(t, n++, _PyUnicode_FromString(field_name)); - if (field_type =3D=3D TEP_PRINT_FLAGS) - PyTuple_SetItem(t, n++, _PyUnicode_FromString(delim)); - - try_call_object(handler_name, t); - - Py_DECREF(t); -} - -static void define_event_symbols(struct tep_event *event, - const char *ev_name, - struct tep_print_arg *args) -{ - if (args =3D=3D NULL) - return; - - switch (args->type) { - case TEP_PRINT_NULL: - break; - case TEP_PRINT_ATOM: - define_value(TEP_PRINT_FLAGS, ev_name, cur_field_name, "0", - args->atom.atom); - zero_flag_atom =3D 0; - break; - case TEP_PRINT_FIELD: - free(cur_field_name); - cur_field_name =3D strdup(args->field.name); - break; - case TEP_PRINT_FLAGS: - define_event_symbols(event, ev_name, args->flags.field); - define_field(TEP_PRINT_FLAGS, ev_name, cur_field_name, - args->flags.delim); - define_values(TEP_PRINT_FLAGS, args->flags.flags, ev_name, - cur_field_name); - break; - case TEP_PRINT_SYMBOL: - define_event_symbols(event, ev_name, args->symbol.field); - define_field(TEP_PRINT_SYMBOL, ev_name, cur_field_name, NULL); - define_values(TEP_PRINT_SYMBOL, args->symbol.symbols, ev_name, - cur_field_name); - break; - case TEP_PRINT_HEX: - case TEP_PRINT_HEX_STR: - define_event_symbols(event, ev_name, args->hex.field); - define_event_symbols(event, ev_name, args->hex.size); - break; - case TEP_PRINT_INT_ARRAY: - define_event_symbols(event, ev_name, args->int_array.field); - define_event_symbols(event, ev_name, args->int_array.count); - define_event_symbols(event, ev_name, args->int_array.el_size); - break; - case TEP_PRINT_STRING: - break; - case TEP_PRINT_TYPE: - define_event_symbols(event, ev_name, args->typecast.item); - break; - case TEP_PRINT_OP: - if (strcmp(args->op.op, ":") =3D=3D 0) - zero_flag_atom =3D 1; - define_event_symbols(event, ev_name, args->op.left); - define_event_symbols(event, ev_name, args->op.right); - break; - default: - /* gcc warns for these? */ - case TEP_PRINT_BSTRING: - case TEP_PRINT_DYNAMIC_ARRAY: - case TEP_PRINT_DYNAMIC_ARRAY_LEN: - case TEP_PRINT_FUNC: - case TEP_PRINT_BITMASK: - /* we should warn... */ - return; - } - - if (args->next) - define_event_symbols(event, ev_name, args->next); -} - -static PyObject *get_field_numeric_entry(struct tep_event *event, - struct tep_format_field *field, void *data) -{ - bool is_array =3D field->flags & TEP_FIELD_IS_ARRAY; - PyObject *obj =3D NULL, *list =3D NULL; - unsigned long long val; - unsigned int item_size, n_items, i; - - if (is_array) { - list =3D PyList_New(field->arraylen); - if (!list) - Py_FatalError("couldn't create Python list"); - item_size =3D field->size / field->arraylen; - n_items =3D field->arraylen; - } else { - item_size =3D field->size; - n_items =3D 1; - } - - for (i =3D 0; i < n_items; i++) { - - val =3D read_size(event, data + field->offset + i * item_size, - item_size); - if (field->flags & TEP_FIELD_IS_SIGNED) { - if ((long long)val >=3D LONG_MIN && - (long long)val <=3D LONG_MAX) - obj =3D _PyLong_FromLong(val); - else - obj =3D PyLong_FromLongLong(val); - } else { - if (val <=3D LONG_MAX) - obj =3D _PyLong_FromLong(val); - else - obj =3D PyLong_FromUnsignedLongLong(val); - } - if (is_array) - PyList_SET_ITEM(list, i, obj); - } - if (is_array) - obj =3D list; - return obj; -} -#endif - -static const char *get_dsoname(struct map *map) -{ - const char *dsoname =3D "[unknown]"; - struct dso *dso =3D map ? map__dso(map) : NULL; - - if (dso) { - if (symbol_conf.show_kernel_path && dso__long_name(dso)) - dsoname =3D dso__long_name(dso); - else - dsoname =3D dso__name(dso); - } - - return dsoname; -} - -static unsigned long get_offset(struct symbol *sym, struct addr_location *= al) -{ - unsigned long offset; - - if (al->addr < sym->end) - offset =3D al->addr - sym->start; - else - offset =3D al->addr - map__start(al->map) - sym->start; - - return offset; -} - -static PyObject *python_process_callchain(struct perf_sample *sample, - struct evsel *evsel, - struct addr_location *al) -{ - PyObject *pylist; - struct callchain_cursor *cursor; - - pylist =3D PyList_New(0); - if (!pylist) - Py_FatalError("couldn't create Python list"); - - if (!symbol_conf.use_callchain || !sample->callchain) - goto exit; - - cursor =3D get_tls_callchain_cursor(); - if (thread__resolve_callchain(al->thread, cursor, evsel, - sample, NULL, NULL, - scripting_max_stack) !=3D 0) { - pr_err("Failed to resolve callchain. Skipping\n"); - goto exit; - } - callchain_cursor_commit(cursor); - - - while (1) { - PyObject *pyelem; - struct callchain_cursor_node *node; - node =3D callchain_cursor_current(cursor); - if (!node) - break; - - pyelem =3D PyDict_New(); - if (!pyelem) - Py_FatalError("couldn't create Python dictionary"); - - - pydict_set_item_string_decref(pyelem, "ip", - PyLong_FromUnsignedLongLong(node->ip)); - - if (node->ms.sym) { - PyObject *pysym =3D PyDict_New(); - if (!pysym) - Py_FatalError("couldn't create Python dictionary"); - pydict_set_item_string_decref(pysym, "start", - PyLong_FromUnsignedLongLong(node->ms.sym->start)); - pydict_set_item_string_decref(pysym, "end", - PyLong_FromUnsignedLongLong(node->ms.sym->end)); - pydict_set_item_string_decref(pysym, "binding", - _PyLong_FromLong(node->ms.sym->binding)); - pydict_set_item_string_decref(pysym, "name", - _PyUnicode_FromStringAndSize(node->ms.sym->name, - node->ms.sym->namelen)); - pydict_set_item_string_decref(pyelem, "sym", pysym); - - if (node->ms.map) { - struct map *map =3D node->ms.map; - struct addr_location node_al; - unsigned long offset; - - addr_location__init(&node_al); - node_al.addr =3D map__map_ip(map, node->ip); - node_al.map =3D map__get(map); - offset =3D get_offset(node->ms.sym, &node_al); - addr_location__exit(&node_al); - - pydict_set_item_string_decref( - pyelem, "sym_off", - PyLong_FromUnsignedLongLong(offset)); - } - if (node->srcline && strcmp(":0", node->srcline)) { - pydict_set_item_string_decref( - pyelem, "sym_srcline", - _PyUnicode_FromString(node->srcline)); - } - } - - if (node->ms.map) { - const char *dsoname =3D get_dsoname(node->ms.map); - - pydict_set_item_string_decref(pyelem, "dso", - _PyUnicode_FromString(dsoname)); - } - - callchain_cursor_advance(cursor); - PyList_Append(pylist, pyelem); - Py_DECREF(pyelem); - } - -exit: - return pylist; -} - -static PyObject *python_process_brstack(struct perf_sample *sample, - struct thread *thread) -{ - struct branch_stack *br =3D sample->branch_stack; - struct branch_entry *entries =3D perf_sample__branch_entries(sample); - PyObject *pylist; - u64 i; - - pylist =3D PyList_New(0); - if (!pylist) - Py_FatalError("couldn't create Python list"); - - if (!(br && br->nr)) - goto exit; - - for (i =3D 0; i < br->nr; i++) { - PyObject *pyelem; - struct addr_location al; - const char *dsoname; - - pyelem =3D PyDict_New(); - if (!pyelem) - Py_FatalError("couldn't create Python dictionary"); - - pydict_set_item_string_decref(pyelem, "from", - PyLong_FromUnsignedLongLong(entries[i].from)); - pydict_set_item_string_decref(pyelem, "to", - PyLong_FromUnsignedLongLong(entries[i].to)); - pydict_set_item_string_decref(pyelem, "mispred", - PyBool_FromLong(entries[i].flags.mispred)); - pydict_set_item_string_decref(pyelem, "predicted", - PyBool_FromLong(entries[i].flags.predicted)); - pydict_set_item_string_decref(pyelem, "in_tx", - PyBool_FromLong(entries[i].flags.in_tx)); - pydict_set_item_string_decref(pyelem, "abort", - PyBool_FromLong(entries[i].flags.abort)); - pydict_set_item_string_decref(pyelem, "cycles", - PyLong_FromUnsignedLongLong(entries[i].flags.cycles)); - - addr_location__init(&al); - thread__find_map_fb(thread, sample->cpumode, - entries[i].from, &al); - dsoname =3D get_dsoname(al.map); - pydict_set_item_string_decref(pyelem, "from_dsoname", - _PyUnicode_FromString(dsoname)); - - thread__find_map_fb(thread, sample->cpumode, - entries[i].to, &al); - dsoname =3D get_dsoname(al.map); - pydict_set_item_string_decref(pyelem, "to_dsoname", - _PyUnicode_FromString(dsoname)); - - addr_location__exit(&al); - PyList_Append(pylist, pyelem); - Py_DECREF(pyelem); - } - -exit: - return pylist; -} - -static int get_symoff(struct symbol *sym, struct addr_location *al, - bool print_off, char *bf, int size) -{ - unsigned long offset; - - if (!sym || !sym->name[0]) - return scnprintf(bf, size, "%s", "[unknown]"); - - if (!print_off) - return scnprintf(bf, size, "%s", sym->name); - - offset =3D get_offset(sym, al); - - return scnprintf(bf, size, "%s+0x%x", sym->name, offset); -} - -static int get_br_mspred(struct branch_flags *flags, char *bf, int size) -{ - if (!flags->mispred && !flags->predicted) - return scnprintf(bf, size, "%s", "-"); - - if (flags->mispred) - return scnprintf(bf, size, "%s", "M"); - - return scnprintf(bf, size, "%s", "P"); -} - -static PyObject *python_process_brstacksym(struct perf_sample *sample, - struct thread *thread) -{ - struct branch_stack *br =3D sample->branch_stack; - struct branch_entry *entries =3D perf_sample__branch_entries(sample); - PyObject *pylist; - u64 i; - char bf[512]; - - pylist =3D PyList_New(0); - if (!pylist) - Py_FatalError("couldn't create Python list"); - - if (!(br && br->nr)) - goto exit; - - for (i =3D 0; i < br->nr; i++) { - PyObject *pyelem; - struct addr_location al; - - addr_location__init(&al); - pyelem =3D PyDict_New(); - if (!pyelem) - Py_FatalError("couldn't create Python dictionary"); - - thread__find_symbol_fb(thread, sample->cpumode, - entries[i].from, &al); - get_symoff(al.sym, &al, true, bf, sizeof(bf)); - pydict_set_item_string_decref(pyelem, "from", - _PyUnicode_FromString(bf)); - - thread__find_symbol_fb(thread, sample->cpumode, - entries[i].to, &al); - get_symoff(al.sym, &al, true, bf, sizeof(bf)); - pydict_set_item_string_decref(pyelem, "to", - _PyUnicode_FromString(bf)); - - get_br_mspred(&entries[i].flags, bf, sizeof(bf)); - pydict_set_item_string_decref(pyelem, "pred", - _PyUnicode_FromString(bf)); - - if (entries[i].flags.in_tx) { - pydict_set_item_string_decref(pyelem, "in_tx", - _PyUnicode_FromString("X")); - } else { - pydict_set_item_string_decref(pyelem, "in_tx", - _PyUnicode_FromString("-")); - } - - if (entries[i].flags.abort) { - pydict_set_item_string_decref(pyelem, "abort", - _PyUnicode_FromString("A")); - } else { - pydict_set_item_string_decref(pyelem, "abort", - _PyUnicode_FromString("-")); - } - - PyList_Append(pylist, pyelem); - Py_DECREF(pyelem); - addr_location__exit(&al); - } - -exit: - return pylist; -} - -static PyObject *get_sample_value_as_tuple(struct sample_read_value *value, - u64 read_format) -{ - PyObject *t; - - t =3D PyTuple_New(3); - if (!t) - Py_FatalError("couldn't create Python tuple"); - PyTuple_SetItem(t, 0, PyLong_FromUnsignedLongLong(value->id)); - PyTuple_SetItem(t, 1, PyLong_FromUnsignedLongLong(value->value)); - if (read_format & PERF_FORMAT_LOST) - PyTuple_SetItem(t, 2, PyLong_FromUnsignedLongLong(value->lost)); - - return t; -} - -static void set_sample_read_in_dict(PyObject *dict_sample, - struct perf_sample *sample, - struct evsel *evsel) -{ - u64 read_format =3D evsel->core.attr.read_format; - PyObject *values; - unsigned int i; - - if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { - pydict_set_item_string_decref(dict_sample, "time_enabled", - PyLong_FromUnsignedLongLong(sample->read.time_enabled)); - } - - if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { - pydict_set_item_string_decref(dict_sample, "time_running", - PyLong_FromUnsignedLongLong(sample->read.time_running)); - } - - if (read_format & PERF_FORMAT_GROUP) - values =3D PyList_New(sample->read.group.nr); - else - values =3D PyList_New(1); - - if (!values) - Py_FatalError("couldn't create Python list"); - - if (read_format & PERF_FORMAT_GROUP) { - struct sample_read_value *v =3D sample->read.group.values; - - i =3D 0; - sample_read_group__for_each(v, sample->read.group.nr, read_format) { - PyObject *t =3D get_sample_value_as_tuple(v, read_format); - PyList_SET_ITEM(values, i, t); - i++; - } - } else { - PyObject *t =3D get_sample_value_as_tuple(&sample->read.one, - read_format); - PyList_SET_ITEM(values, 0, t); - } - pydict_set_item_string_decref(dict_sample, "values", values); -} - -static void set_sample_datasrc_in_dict(PyObject *dict, - struct perf_sample *sample) -{ - struct mem_info *mi =3D mem_info__new(); - char decode[100]; - - if (!mi) - Py_FatalError("couldn't create mem-info"); - - pydict_set_item_string_decref(dict, "datasrc", - PyLong_FromUnsignedLongLong(sample->data_src)); - - mem_info__data_src(mi)->val =3D sample->data_src; - perf_script__meminfo_scnprintf(decode, 100, mi); - mem_info__put(mi); - - pydict_set_item_string_decref(dict, "datasrc_decode", - _PyUnicode_FromString(decode)); -} - -static void regs_map(struct regs_dump *regs, uint64_t mask, uint16_t e_mac= hine, uint32_t e_flags, - char *bf, int size) -{ - unsigned int i =3D 0, r; - int printed =3D 0; - - bf[0] =3D 0; - - if (size <=3D 0) - return; - - if (!regs || !regs->regs) - return; - - for_each_set_bit(r, (unsigned long *) &mask, sizeof(mask) * 8) { - u64 val =3D regs->regs[i++]; - - printed +=3D scnprintf(bf + printed, size - printed, - "%5s:0x%" PRIx64 " ", - perf_reg_name(r, e_machine, e_flags), val); - } -} - -#define MAX_REG_SIZE 128 - -static int set_regs_in_dict(PyObject *dict, - struct perf_sample *sample, - struct evsel *evsel, - uint16_t e_machine, - uint32_t e_flags) -{ - struct perf_event_attr *attr =3D &evsel->core.attr; - - int size =3D (__sw_hweight64(attr->sample_regs_intr) * MAX_REG_SIZE) + 1; - char *bf =3D NULL; - - if (sample->intr_regs) { - bf =3D malloc(size); - if (!bf) - return -1; - - regs_map(sample->intr_regs, attr->sample_regs_intr, e_machine, e_flags, = bf, size); - - pydict_set_item_string_decref(dict, "iregs", - _PyUnicode_FromString(bf)); - } - - if (sample->user_regs) { - if (!bf) { - bf =3D malloc(size); - if (!bf) - return -1; - } - regs_map(sample->user_regs, attr->sample_regs_user, e_machine, e_flags, = bf, size); - - pydict_set_item_string_decref(dict, "uregs", - _PyUnicode_FromString(bf)); - } - free(bf); - - return 0; -} - -static void set_sym_in_dict(PyObject *dict, struct addr_location *al, - const char *dso_field, const char *dso_bid_field, - const char *dso_map_start, const char *dso_map_end, - const char *sym_field, const char *symoff_field, - const char *map_pgoff) -{ - if (al->map) { - char sbuild_id[SBUILD_ID_SIZE]; - struct dso *dso =3D map__dso(al->map); - - pydict_set_item_string_decref(dict, dso_field, - _PyUnicode_FromString(dso__name(dso))); - build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); - pydict_set_item_string_decref(dict, dso_bid_field, - _PyUnicode_FromString(sbuild_id)); - pydict_set_item_string_decref(dict, dso_map_start, - PyLong_FromUnsignedLong(map__start(al->map))); - pydict_set_item_string_decref(dict, dso_map_end, - PyLong_FromUnsignedLong(map__end(al->map))); - pydict_set_item_string_decref(dict, map_pgoff, - PyLong_FromUnsignedLongLong(map__pgoff(al->map))); - } - if (al->sym) { - pydict_set_item_string_decref(dict, sym_field, - _PyUnicode_FromString(al->sym->name)); - pydict_set_item_string_decref(dict, symoff_field, - PyLong_FromUnsignedLong(get_offset(al->sym, al))); - } -} - -static void set_sample_flags(PyObject *dict, u32 flags) -{ - const char *ch =3D PERF_IP_FLAG_CHARS; - char *p, str[33]; - - for (p =3D str; *ch; ch++, flags >>=3D 1) { - if (flags & 1) - *p++ =3D *ch; - } - *p =3D 0; - pydict_set_item_string_decref(dict, "flags", _PyUnicode_FromString(str)); -} - -static void python_process_sample_flags(struct perf_sample *sample, PyObje= ct *dict_sample) -{ - char flags_disp[SAMPLE_FLAGS_BUF_SIZE]; - - set_sample_flags(dict_sample, sample->flags); - perf_sample__sprintf_flags(sample->flags, flags_disp, sizeof(flags_disp)); - pydict_set_item_string_decref(dict_sample, "flags_disp", - _PyUnicode_FromString(flags_disp)); -} - -static PyObject *get_perf_sample_dict(struct perf_sample *sample, - struct evsel *evsel, - struct addr_location *al, - struct addr_location *addr_al, - PyObject *callchain) -{ - PyObject *dict, *dict_sample, *brstack, *brstacksym; - uint16_t e_machine =3D EM_HOST; - uint32_t e_flags =3D EF_HOST; - - dict =3D PyDict_New(); - if (!dict) - Py_FatalError("couldn't create Python dictionary"); - - dict_sample =3D PyDict_New(); - if (!dict_sample) - Py_FatalError("couldn't create Python dictionary"); - - pydict_set_item_string_decref(dict, "ev_name", _PyUnicode_FromString(evse= l__name(evsel))); - pydict_set_item_string_decref(dict, "attr", _PyBytes_FromStringAndSize((c= onst char *)&evsel->core.attr, sizeof(evsel->core.attr))); - - pydict_set_item_string_decref(dict_sample, "id", - PyLong_FromUnsignedLongLong(sample->id)); - pydict_set_item_string_decref(dict_sample, "stream_id", - PyLong_FromUnsignedLongLong(sample->stream_id)); - pydict_set_item_string_decref(dict_sample, "pid", - _PyLong_FromLong(sample->pid)); - pydict_set_item_string_decref(dict_sample, "tid", - _PyLong_FromLong(sample->tid)); - pydict_set_item_string_decref(dict_sample, "cpu", - _PyLong_FromLong(sample->cpu)); - pydict_set_item_string_decref(dict_sample, "ip", - PyLong_FromUnsignedLongLong(sample->ip)); - pydict_set_item_string_decref(dict_sample, "time", - PyLong_FromUnsignedLongLong(sample->time)); - pydict_set_item_string_decref(dict_sample, "period", - PyLong_FromUnsignedLongLong(sample->period)); - pydict_set_item_string_decref(dict_sample, "phys_addr", - PyLong_FromUnsignedLongLong(sample->phys_addr)); - pydict_set_item_string_decref(dict_sample, "addr", - PyLong_FromUnsignedLongLong(sample->addr)); - set_sample_read_in_dict(dict_sample, sample, evsel); - pydict_set_item_string_decref(dict_sample, "weight", - PyLong_FromUnsignedLongLong(sample->weight)); - pydict_set_item_string_decref(dict_sample, "ins_lat", - PyLong_FromUnsignedLong(sample->ins_lat)); - pydict_set_item_string_decref(dict_sample, "transaction", - PyLong_FromUnsignedLongLong(sample->transaction)); - set_sample_datasrc_in_dict(dict_sample, sample); - pydict_set_item_string_decref(dict, "sample", dict_sample); - - pydict_set_item_string_decref(dict, "raw_buf", _PyBytes_FromStringAndSize( - (const char *)sample->raw_data, sample->raw_size)); - pydict_set_item_string_decref(dict, "comm", - _PyUnicode_FromString(thread__comm_str(al->thread))); - set_sym_in_dict(dict, al, "dso", "dso_bid", "dso_map_start", "dso_map_end= ", - "symbol", "symoff", "map_pgoff"); - - pydict_set_item_string_decref(dict, "callchain", callchain); - - brstack =3D python_process_brstack(sample, al->thread); - pydict_set_item_string_decref(dict, "brstack", brstack); - - brstacksym =3D python_process_brstacksym(sample, al->thread); - pydict_set_item_string_decref(dict, "brstacksym", brstacksym); - - if (sample->machine_pid) { - pydict_set_item_string_decref(dict_sample, "machine_pid", - _PyLong_FromLong(sample->machine_pid)); - pydict_set_item_string_decref(dict_sample, "vcpu", - _PyLong_FromLong(sample->vcpu)); - } - - pydict_set_item_string_decref(dict_sample, "cpumode", - _PyLong_FromLong((unsigned long)sample->cpumode)); - - if (addr_al) { - pydict_set_item_string_decref(dict_sample, "addr_correlates_sym", - PyBool_FromLong(1)); - set_sym_in_dict(dict_sample, addr_al, "addr_dso", "addr_dso_bid", - "addr_dso_map_start", "addr_dso_map_end", - "addr_symbol", "addr_symoff", "addr_map_pgoff"); - } - - if (sample->flags) - python_process_sample_flags(sample, dict_sample); - - /* Instructions per cycle (IPC) */ - if (sample->insn_cnt && sample->cyc_cnt) { - pydict_set_item_string_decref(dict_sample, "insn_cnt", - PyLong_FromUnsignedLongLong(sample->insn_cnt)); - pydict_set_item_string_decref(dict_sample, "cyc_cnt", - PyLong_FromUnsignedLongLong(sample->cyc_cnt)); - } - - if (al->thread) - e_machine =3D thread__e_machine(al->thread, /*machine=3D*/NULL, &e_flags= ); - - if (set_regs_in_dict(dict, sample, evsel, e_machine, e_flags)) - Py_FatalError("Failed to setting regs in dict"); - - return dict; -} - -#ifdef HAVE_LIBTRACEEVENT -static void python_process_tracepoint(struct perf_sample *sample, - struct evsel *evsel, - struct addr_location *al, - struct addr_location *addr_al) -{ - struct tep_event *event; - PyObject *handler, *context, *t, *obj =3D NULL, *callchain; - PyObject *dict =3D NULL, *all_entries_dict =3D NULL; - static char handler_name[256]; - struct tep_format_field *field; - unsigned long s, ns; - unsigned n =3D 0; - int pid; - int cpu =3D sample->cpu; - void *data =3D sample->raw_data; - unsigned long long nsecs =3D sample->time; - const char *comm =3D thread__comm_str(al->thread); - const char *default_handler_name =3D "trace_unhandled"; - DECLARE_BITMAP(events_defined, TRACE_EVENT_TYPE_MAX); - - bitmap_zero(events_defined, TRACE_EVENT_TYPE_MAX); - - event =3D evsel__tp_format(evsel); - if (!event) { - snprintf(handler_name, sizeof(handler_name), - "ug! no event found for type %" PRIu64, (u64)evsel->core.attr.config); - Py_FatalError(handler_name); - } - - pid =3D raw_field_value(event, "common_pid", data); - - sprintf(handler_name, "%s__%s", event->system, event->name); - - if (!__test_and_set_bit(event->id, events_defined)) - define_event_symbols(event, handler_name, event->print_fmt.args); - - handler =3D get_handler(handler_name); - if (!handler) { - handler =3D get_handler(default_handler_name); - if (!handler) - return; - dict =3D PyDict_New(); - if (!dict) - Py_FatalError("couldn't create Python dict"); - } - - t =3D PyTuple_New(MAX_FIELDS); - if (!t) - Py_FatalError("couldn't create Python tuple"); - - - s =3D nsecs / NSEC_PER_SEC; - ns =3D nsecs - s * NSEC_PER_SEC; - - context =3D _PyCapsule_New(scripting_context, NULL, NULL); - - PyTuple_SetItem(t, n++, _PyUnicode_FromString(handler_name)); - PyTuple_SetItem(t, n++, context); - - /* ip unwinding */ - callchain =3D python_process_callchain(sample, evsel, al); - /* Need an additional reference for the perf_sample dict */ - Py_INCREF(callchain); - - if (!dict) { - PyTuple_SetItem(t, n++, _PyLong_FromLong(cpu)); - PyTuple_SetItem(t, n++, _PyLong_FromLong(s)); - PyTuple_SetItem(t, n++, _PyLong_FromLong(ns)); - PyTuple_SetItem(t, n++, _PyLong_FromLong(pid)); - PyTuple_SetItem(t, n++, _PyUnicode_FromString(comm)); - PyTuple_SetItem(t, n++, callchain); - } else { - pydict_set_item_string_decref(dict, "common_cpu", _PyLong_FromLong(cpu)); - pydict_set_item_string_decref(dict, "common_s", _PyLong_FromLong(s)); - pydict_set_item_string_decref(dict, "common_ns", _PyLong_FromLong(ns)); - pydict_set_item_string_decref(dict, "common_pid", _PyLong_FromLong(pid)); - pydict_set_item_string_decref(dict, "common_comm", _PyUnicode_FromString= (comm)); - pydict_set_item_string_decref(dict, "common_callchain", callchain); - } - for (field =3D event->format.fields; field; field =3D field->next) { - unsigned int offset, len; - unsigned long long val; - - if (field->flags & TEP_FIELD_IS_ARRAY) { - offset =3D field->offset; - len =3D field->size; - if (field->flags & TEP_FIELD_IS_DYNAMIC) { - val =3D tep_read_number(scripting_context->pevent, - data + offset, len); - offset =3D val; - len =3D offset >> 16; - offset &=3D 0xffff; - if (tep_field_is_relative(field->flags)) - offset +=3D field->offset + field->size; - } - if (field->flags & TEP_FIELD_IS_STRING && - is_printable_array(data + offset, len)) { - obj =3D _PyUnicode_FromString((char *) data + offset); - } else { - obj =3D PyByteArray_FromStringAndSize((const char *) data + offset, le= n); - field->flags &=3D ~TEP_FIELD_IS_STRING; - } - } else { /* FIELD_IS_NUMERIC */ - obj =3D get_field_numeric_entry(event, field, data); - } - if (!dict) - PyTuple_SetItem(t, n++, obj); - else - pydict_set_item_string_decref(dict, field->name, obj); - - } - - if (dict) - PyTuple_SetItem(t, n++, dict); - - if (get_argument_count(handler) =3D=3D (int) n + 1) { - all_entries_dict =3D get_perf_sample_dict(sample, evsel, al, addr_al, - callchain); - PyTuple_SetItem(t, n++, all_entries_dict); - } else { - Py_DECREF(callchain); - } - - if (_PyTuple_Resize(&t, n) =3D=3D -1) - Py_FatalError("error resizing Python tuple"); - - if (!dict) - call_object(handler, t, handler_name); - else - call_object(handler, t, default_handler_name); - - Py_DECREF(t); -} -#else -static void python_process_tracepoint(struct perf_sample *sample __maybe_u= nused, - struct evsel *evsel __maybe_unused, - struct addr_location *al __maybe_unused, - struct addr_location *addr_al __maybe_unused) -{ - fprintf(stderr, "Tracepoint events are not supported because " - "perf is not linked with libtraceevent.\n"); -} -#endif - -static PyObject *tuple_new(unsigned int sz) -{ - PyObject *t; - - t =3D PyTuple_New(sz); - if (!t) - Py_FatalError("couldn't create Python tuple"); - return t; -} - -static int tuple_set_s64(PyObject *t, unsigned int pos, s64 val) -{ -#if BITS_PER_LONG =3D=3D 64 - return PyTuple_SetItem(t, pos, _PyLong_FromLong(val)); -#endif -#if BITS_PER_LONG =3D=3D 32 - return PyTuple_SetItem(t, pos, PyLong_FromLongLong(val)); -#endif -} - -/* - * Databases support only signed 64-bit numbers, so even though we are - * exporting a u64, it must be as s64. - */ -#define tuple_set_d64 tuple_set_s64 - -static int tuple_set_u64(PyObject *t, unsigned int pos, u64 val) -{ -#if BITS_PER_LONG =3D=3D 64 - return PyTuple_SetItem(t, pos, PyLong_FromUnsignedLong(val)); -#endif -#if BITS_PER_LONG =3D=3D 32 - return PyTuple_SetItem(t, pos, PyLong_FromUnsignedLongLong(val)); -#endif -} - -static int tuple_set_u32(PyObject *t, unsigned int pos, u32 val) -{ - return PyTuple_SetItem(t, pos, PyLong_FromUnsignedLong(val)); -} - -static int tuple_set_s32(PyObject *t, unsigned int pos, s32 val) -{ - return PyTuple_SetItem(t, pos, _PyLong_FromLong(val)); -} - -static int tuple_set_bool(PyObject *t, unsigned int pos, bool val) -{ - return PyTuple_SetItem(t, pos, PyBool_FromLong(val)); -} - -static int tuple_set_string(PyObject *t, unsigned int pos, const char *s) -{ - return PyTuple_SetItem(t, pos, _PyUnicode_FromString(s)); -} - -static int tuple_set_bytes(PyObject *t, unsigned int pos, void *bytes, - unsigned int sz) -{ - return PyTuple_SetItem(t, pos, _PyBytes_FromStringAndSize(bytes, sz)); -} - -static int python_export_evsel(struct db_export *dbe, struct evsel *evsel) -{ - struct tables *tables =3D container_of(dbe, struct tables, dbe); - PyObject *t; - - t =3D tuple_new(2); - - tuple_set_d64(t, 0, evsel->db_id); - tuple_set_string(t, 1, evsel__name(evsel)); - - call_object(tables->evsel_handler, t, "evsel_table"); - - Py_DECREF(t); - - return 0; -} - -static int python_export_machine(struct db_export *dbe, - struct machine *machine) -{ - struct tables *tables =3D container_of(dbe, struct tables, dbe); - PyObject *t; - - t =3D tuple_new(3); - - tuple_set_d64(t, 0, machine->db_id); - tuple_set_s32(t, 1, machine->pid); - tuple_set_string(t, 2, machine->root_dir ? machine->root_dir : ""); - - call_object(tables->machine_handler, t, "machine_table"); - - Py_DECREF(t); - - return 0; -} - -static int python_export_thread(struct db_export *dbe, struct thread *thre= ad, - u64 main_thread_db_id, struct machine *machine) -{ - struct tables *tables =3D container_of(dbe, struct tables, dbe); - PyObject *t; - - t =3D tuple_new(5); - - tuple_set_d64(t, 0, thread__db_id(thread)); - tuple_set_d64(t, 1, machine->db_id); - tuple_set_d64(t, 2, main_thread_db_id); - tuple_set_s32(t, 3, thread__pid(thread)); - tuple_set_s32(t, 4, thread__tid(thread)); - - call_object(tables->thread_handler, t, "thread_table"); - - Py_DECREF(t); - - return 0; -} - -static int python_export_comm(struct db_export *dbe, struct comm *comm, - struct thread *thread) -{ - struct tables *tables =3D container_of(dbe, struct tables, dbe); - PyObject *t; - - t =3D tuple_new(5); - - tuple_set_d64(t, 0, comm->db_id); - tuple_set_string(t, 1, comm__str(comm)); - tuple_set_d64(t, 2, thread__db_id(thread)); - tuple_set_d64(t, 3, comm->start); - tuple_set_s32(t, 4, comm->exec); - - call_object(tables->comm_handler, t, "comm_table"); - - Py_DECREF(t); - - return 0; -} - -static int python_export_comm_thread(struct db_export *dbe, u64 db_id, - struct comm *comm, struct thread *thread) -{ - struct tables *tables =3D container_of(dbe, struct tables, dbe); - PyObject *t; - - t =3D tuple_new(3); - - tuple_set_d64(t, 0, db_id); - tuple_set_d64(t, 1, comm->db_id); - tuple_set_d64(t, 2, thread__db_id(thread)); - - call_object(tables->comm_thread_handler, t, "comm_thread_table"); - - Py_DECREF(t); - - return 0; -} - -static int python_export_dso(struct db_export *dbe, struct dso *dso, - struct machine *machine) -{ - struct tables *tables =3D container_of(dbe, struct tables, dbe); - char sbuild_id[SBUILD_ID_SIZE]; - PyObject *t; - - build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); - - t =3D tuple_new(5); - - tuple_set_d64(t, 0, dso__db_id(dso)); - tuple_set_d64(t, 1, machine->db_id); - tuple_set_string(t, 2, dso__short_name(dso)); - tuple_set_string(t, 3, dso__long_name(dso)); - tuple_set_string(t, 4, sbuild_id); - - call_object(tables->dso_handler, t, "dso_table"); - - Py_DECREF(t); - - return 0; -} - -static int python_export_symbol(struct db_export *dbe, struct symbol *sym, - struct dso *dso) -{ - struct tables *tables =3D container_of(dbe, struct tables, dbe); - u64 *sym_db_id =3D symbol__priv(sym); - PyObject *t; - - t =3D tuple_new(6); - - tuple_set_d64(t, 0, *sym_db_id); - tuple_set_d64(t, 1, dso__db_id(dso)); - tuple_set_d64(t, 2, sym->start); - tuple_set_d64(t, 3, sym->end); - tuple_set_s32(t, 4, sym->binding); - tuple_set_string(t, 5, sym->name); - - call_object(tables->symbol_handler, t, "symbol_table"); - - Py_DECREF(t); - - return 0; -} - -static int python_export_branch_type(struct db_export *dbe, u32 branch_typ= e, - const char *name) -{ - struct tables *tables =3D container_of(dbe, struct tables, dbe); - PyObject *t; - - t =3D tuple_new(2); - - tuple_set_s32(t, 0, branch_type); - tuple_set_string(t, 1, name); - - call_object(tables->branch_type_handler, t, "branch_type_table"); - - Py_DECREF(t); - - return 0; -} - -static void python_export_sample_table(struct db_export *dbe, - struct export_sample *es) -{ - struct tables *tables =3D container_of(dbe, struct tables, dbe); - PyObject *t; - - t =3D tuple_new(28); - - tuple_set_d64(t, 0, es->db_id); - tuple_set_d64(t, 1, es->evsel->db_id); - tuple_set_d64(t, 2, maps__machine(thread__maps(es->al->thread))->db_id); - tuple_set_d64(t, 3, thread__db_id(es->al->thread)); - tuple_set_d64(t, 4, es->comm_db_id); - tuple_set_d64(t, 5, es->dso_db_id); - tuple_set_d64(t, 6, es->sym_db_id); - tuple_set_d64(t, 7, es->offset); - tuple_set_d64(t, 8, es->sample->ip); - tuple_set_d64(t, 9, es->sample->time); - tuple_set_s32(t, 10, es->sample->cpu); - tuple_set_d64(t, 11, es->addr_dso_db_id); - tuple_set_d64(t, 12, es->addr_sym_db_id); - tuple_set_d64(t, 13, es->addr_offset); - tuple_set_d64(t, 14, es->sample->addr); - tuple_set_d64(t, 15, es->sample->period); - tuple_set_d64(t, 16, es->sample->weight); - tuple_set_d64(t, 17, es->sample->transaction); - tuple_set_d64(t, 18, es->sample->data_src); - tuple_set_s32(t, 19, es->sample->flags & PERF_BRANCH_MASK); - tuple_set_s32(t, 20, !!(es->sample->flags & PERF_IP_FLAG_IN_TX)); - tuple_set_d64(t, 21, es->call_path_id); - tuple_set_d64(t, 22, es->sample->insn_cnt); - tuple_set_d64(t, 23, es->sample->cyc_cnt); - tuple_set_s32(t, 24, es->sample->flags); - tuple_set_d64(t, 25, es->sample->id); - tuple_set_d64(t, 26, es->sample->stream_id); - tuple_set_u32(t, 27, es->sample->ins_lat); - - call_object(tables->sample_handler, t, "sample_table"); - - Py_DECREF(t); -} - -static void python_export_synth(struct db_export *dbe, struct export_sampl= e *es) -{ - struct tables *tables =3D container_of(dbe, struct tables, dbe); - PyObject *t; - - t =3D tuple_new(3); - - tuple_set_d64(t, 0, es->db_id); - tuple_set_d64(t, 1, es->evsel->core.attr.config); - tuple_set_bytes(t, 2, es->sample->raw_data, es->sample->raw_size); - - call_object(tables->synth_handler, t, "synth_data"); - - Py_DECREF(t); -} - -static int python_export_sample(struct db_export *dbe, - struct export_sample *es) -{ - struct tables *tables =3D container_of(dbe, struct tables, dbe); - - python_export_sample_table(dbe, es); - - if (es->evsel->core.attr.type =3D=3D PERF_TYPE_SYNTH && tables->synth_han= dler) - python_export_synth(dbe, es); - - return 0; -} - -static int python_export_call_path(struct db_export *dbe, struct call_path= *cp) -{ - struct tables *tables =3D container_of(dbe, struct tables, dbe); - PyObject *t; - u64 parent_db_id, sym_db_id; - - parent_db_id =3D cp->parent ? cp->parent->db_id : 0; - sym_db_id =3D cp->sym ? *(u64 *)symbol__priv(cp->sym) : 0; - - t =3D tuple_new(4); - - tuple_set_d64(t, 0, cp->db_id); - tuple_set_d64(t, 1, parent_db_id); - tuple_set_d64(t, 2, sym_db_id); - tuple_set_d64(t, 3, cp->ip); - - call_object(tables->call_path_handler, t, "call_path_table"); - - Py_DECREF(t); - - return 0; -} - -static int python_export_call_return(struct db_export *dbe, - struct call_return *cr) -{ - struct tables *tables =3D container_of(dbe, struct tables, dbe); - u64 comm_db_id =3D cr->comm ? cr->comm->db_id : 0; - PyObject *t; - - t =3D tuple_new(14); - - tuple_set_d64(t, 0, cr->db_id); - tuple_set_d64(t, 1, thread__db_id(cr->thread)); - tuple_set_d64(t, 2, comm_db_id); - tuple_set_d64(t, 3, cr->cp->db_id); - tuple_set_d64(t, 4, cr->call_time); - tuple_set_d64(t, 5, cr->return_time); - tuple_set_d64(t, 6, cr->branch_count); - tuple_set_d64(t, 7, cr->call_ref); - tuple_set_d64(t, 8, cr->return_ref); - tuple_set_d64(t, 9, cr->cp->parent->db_id); - tuple_set_s32(t, 10, cr->flags); - tuple_set_d64(t, 11, cr->parent_db_id); - tuple_set_d64(t, 12, cr->insn_count); - tuple_set_d64(t, 13, cr->cyc_count); - - call_object(tables->call_return_handler, t, "call_return_table"); - - Py_DECREF(t); - - return 0; -} - -static int python_export_context_switch(struct db_export *dbe, u64 db_id, - struct machine *machine, - struct perf_sample *sample, - u64 th_out_id, u64 comm_out_id, - u64 th_in_id, u64 comm_in_id, int flags) -{ - struct tables *tables =3D container_of(dbe, struct tables, dbe); - PyObject *t; - - t =3D tuple_new(9); - - tuple_set_d64(t, 0, db_id); - tuple_set_d64(t, 1, machine->db_id); - tuple_set_d64(t, 2, sample->time); - tuple_set_s32(t, 3, sample->cpu); - tuple_set_d64(t, 4, th_out_id); - tuple_set_d64(t, 5, comm_out_id); - tuple_set_d64(t, 6, th_in_id); - tuple_set_d64(t, 7, comm_in_id); - tuple_set_s32(t, 8, flags); - - call_object(tables->context_switch_handler, t, "context_switch"); - - Py_DECREF(t); - - return 0; -} - -static int python_process_call_return(struct call_return *cr, u64 *parent_= db_id, - void *data) -{ - struct db_export *dbe =3D data; - - return db_export__call_return(dbe, cr, parent_db_id); -} - -static void python_process_general_event(struct perf_sample *sample, - struct evsel *evsel, - struct addr_location *al, - struct addr_location *addr_al) -{ - PyObject *handler, *t, *dict, *callchain; - static char handler_name[64]; - unsigned n =3D 0; - - snprintf(handler_name, sizeof(handler_name), "%s", "process_event"); - - handler =3D get_handler(handler_name); - if (!handler) - return; - - /* - * Use the MAX_FIELDS to make the function expandable, though - * currently there is only one item for the tuple. - */ - t =3D PyTuple_New(MAX_FIELDS); - if (!t) - Py_FatalError("couldn't create Python tuple"); - - /* ip unwinding */ - callchain =3D python_process_callchain(sample, evsel, al); - dict =3D get_perf_sample_dict(sample, evsel, al, addr_al, callchain); - - PyTuple_SetItem(t, n++, dict); - if (_PyTuple_Resize(&t, n) =3D=3D -1) - Py_FatalError("error resizing Python tuple"); - - call_object(handler, t, handler_name); - - Py_DECREF(t); -} - -static void python_process_event(union perf_event *event, - struct perf_sample *sample, - struct evsel *evsel, - struct addr_location *al, - struct addr_location *addr_al) -{ - struct tables *tables =3D &tables_global; - - scripting_context__update(scripting_context, event, sample, evsel, al, ad= dr_al); - - switch (evsel->core.attr.type) { - case PERF_TYPE_TRACEPOINT: - python_process_tracepoint(sample, evsel, al, addr_al); - break; - /* Reserve for future process_hw/sw/raw APIs */ - default: - if (tables->db_export_mode) - db_export__sample(&tables->dbe, event, sample, evsel, al, addr_al); - else - python_process_general_event(sample, evsel, al, addr_al); - } -} - -static void python_process_throttle(union perf_event *event, - struct perf_sample *sample, - struct machine *machine) -{ - const char *handler_name; - PyObject *handler, *t; - - if (event->header.type =3D=3D PERF_RECORD_THROTTLE) - handler_name =3D "throttle"; - else - handler_name =3D "unthrottle"; - handler =3D get_handler(handler_name); - if (!handler) - return; - - t =3D tuple_new(6); - if (!t) - return; - - tuple_set_u64(t, 0, event->throttle.time); - tuple_set_u64(t, 1, event->throttle.id); - tuple_set_u64(t, 2, event->throttle.stream_id); - tuple_set_s32(t, 3, sample->cpu); - tuple_set_s32(t, 4, sample->pid); - tuple_set_s32(t, 5, sample->tid); - - call_object(handler, t, handler_name); - - Py_DECREF(t); -} - -static void python_do_process_switch(union perf_event *event, - struct perf_sample *sample, - struct machine *machine) -{ - const char *handler_name =3D "context_switch"; - bool out =3D event->header.misc & PERF_RECORD_MISC_SWITCH_OUT; - bool out_preempt =3D out && (event->header.misc & PERF_RECORD_MISC_SWITCH= _OUT_PREEMPT); - pid_t np_pid =3D -1, np_tid =3D -1; - PyObject *handler, *t; - - handler =3D get_handler(handler_name); - if (!handler) - return; - - if (event->header.type =3D=3D PERF_RECORD_SWITCH_CPU_WIDE) { - np_pid =3D event->context_switch.next_prev_pid; - np_tid =3D event->context_switch.next_prev_tid; - } - - t =3D tuple_new(11); - if (!t) - return; - - tuple_set_u64(t, 0, sample->time); - tuple_set_s32(t, 1, sample->cpu); - tuple_set_s32(t, 2, sample->pid); - tuple_set_s32(t, 3, sample->tid); - tuple_set_s32(t, 4, np_pid); - tuple_set_s32(t, 5, np_tid); - tuple_set_s32(t, 6, machine->pid); - tuple_set_bool(t, 7, out); - tuple_set_bool(t, 8, out_preempt); - tuple_set_s32(t, 9, sample->machine_pid); - tuple_set_s32(t, 10, sample->vcpu); - - call_object(handler, t, handler_name); - - Py_DECREF(t); -} - -static void python_process_switch(union perf_event *event, - struct perf_sample *sample, - struct machine *machine) -{ - struct tables *tables =3D &tables_global; - - if (tables->db_export_mode) - db_export__switch(&tables->dbe, event, sample, machine); - else - python_do_process_switch(event, sample, machine); -} - -static void python_process_auxtrace_error(struct perf_session *session __m= aybe_unused, - union perf_event *event) -{ - struct perf_record_auxtrace_error *e =3D &event->auxtrace_error; - u8 cpumode =3D e->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - const char *handler_name =3D "auxtrace_error"; - unsigned long long tm =3D e->time; - const char *msg =3D e->msg; - PyObject *handler, *t; - - handler =3D get_handler(handler_name); - if (!handler) - return; - - if (!e->fmt) { - tm =3D 0; - msg =3D (const char *)&e->time; - } - - t =3D tuple_new(11); - - tuple_set_u32(t, 0, e->type); - tuple_set_u32(t, 1, e->code); - tuple_set_s32(t, 2, e->cpu); - tuple_set_s32(t, 3, e->pid); - tuple_set_s32(t, 4, e->tid); - tuple_set_u64(t, 5, e->ip); - tuple_set_u64(t, 6, tm); - tuple_set_string(t, 7, msg); - tuple_set_u32(t, 8, cpumode); - tuple_set_s32(t, 9, e->machine_pid); - tuple_set_s32(t, 10, e->vcpu); - - call_object(handler, t, handler_name); - - Py_DECREF(t); -} - -static void get_handler_name(char *str, size_t size, - struct evsel *evsel) -{ - char *p =3D str; - - scnprintf(str, size, "stat__%s", evsel__name(evsel)); - - while ((p =3D strchr(p, ':'))) { - *p =3D '_'; - p++; - } -} - -static void -process_stat(struct evsel *counter, struct perf_cpu cpu, int thread, u64 t= stamp, - struct perf_counts_values *count) -{ - PyObject *handler, *t; - static char handler_name[256]; - int n =3D 0; - - t =3D PyTuple_New(MAX_FIELDS); - if (!t) - Py_FatalError("couldn't create Python tuple"); - - get_handler_name(handler_name, sizeof(handler_name), - counter); - - handler =3D get_handler(handler_name); - if (!handler) { - pr_debug("can't find python handler %s\n", handler_name); - return; - } - - PyTuple_SetItem(t, n++, _PyLong_FromLong(cpu.cpu)); - PyTuple_SetItem(t, n++, _PyLong_FromLong(thread)); - - tuple_set_u64(t, n++, tstamp); - tuple_set_u64(t, n++, count->val); - tuple_set_u64(t, n++, count->ena); - tuple_set_u64(t, n++, count->run); - - if (_PyTuple_Resize(&t, n) =3D=3D -1) - Py_FatalError("error resizing Python tuple"); - - call_object(handler, t, handler_name); - - Py_DECREF(t); -} - -static void python_process_stat(struct perf_stat_config *config, - struct evsel *counter, u64 tstamp) -{ - struct perf_thread_map *threads =3D counter->core.threads; - struct perf_cpu_map *cpus =3D counter->core.cpus; - - for (int thread =3D 0; thread < perf_thread_map__nr(threads); thread++) { - unsigned int idx; - struct perf_cpu cpu; - - perf_cpu_map__for_each_cpu(cpu, idx, cpus) { - process_stat(counter, cpu, - perf_thread_map__pid(threads, thread), tstamp, - perf_counts(counter->counts, idx, thread)); - } - } -} - -static void python_process_stat_interval(u64 tstamp) -{ - PyObject *handler, *t; - static const char handler_name[] =3D "stat__interval"; - int n =3D 0; - - t =3D PyTuple_New(MAX_FIELDS); - if (!t) - Py_FatalError("couldn't create Python tuple"); - - handler =3D get_handler(handler_name); - if (!handler) { - pr_debug("can't find python handler %s\n", handler_name); - return; - } - - tuple_set_u64(t, n++, tstamp); - - if (_PyTuple_Resize(&t, n) =3D=3D -1) - Py_FatalError("error resizing Python tuple"); - - call_object(handler, t, handler_name); - - Py_DECREF(t); -} - -static int perf_script_context_init(void) -{ - PyObject *perf_script_context; - PyObject *perf_trace_context; - PyObject *dict; - int ret; - - perf_trace_context =3D PyImport_AddModule("perf_trace_context"); - if (!perf_trace_context) - return -1; - dict =3D PyModule_GetDict(perf_trace_context); - if (!dict) - return -1; - - perf_script_context =3D _PyCapsule_New(scripting_context, NULL, NULL); - if (!perf_script_context) - return -1; - - ret =3D PyDict_SetItemString(dict, "perf_script_context", perf_script_con= text); - if (!ret) - ret =3D PyDict_SetItemString(main_dict, "perf_script_context", perf_scri= pt_context); - Py_DECREF(perf_script_context); - return ret; -} - -static int run_start_sub(void) -{ - main_module =3D PyImport_AddModule("__main__"); - if (main_module =3D=3D NULL) - return -1; - Py_INCREF(main_module); - - main_dict =3D PyModule_GetDict(main_module); - if (main_dict =3D=3D NULL) - goto error; - Py_INCREF(main_dict); - - if (perf_script_context_init()) - goto error; - - try_call_object("trace_begin", NULL); - - return 0; - -error: - Py_XDECREF(main_dict); - Py_XDECREF(main_module); - return -1; -} - -#define SET_TABLE_HANDLER_(name, handler_name, table_name) do { \ - tables->handler_name =3D get_handler(#table_name); \ - if (tables->handler_name) \ - tables->dbe.export_ ## name =3D python_export_ ## name; \ -} while (0) - -#define SET_TABLE_HANDLER(name) \ - SET_TABLE_HANDLER_(name, name ## _handler, name ## _table) - -static void set_table_handlers(struct tables *tables) -{ - const char *perf_db_export_mode =3D "perf_db_export_mode"; - const char *perf_db_export_calls =3D "perf_db_export_calls"; - const char *perf_db_export_callchains =3D "perf_db_export_callchains"; - PyObject *db_export_mode, *db_export_calls, *db_export_callchains; - bool export_calls =3D false; - bool export_callchains =3D false; - int ret; - - memset(tables, 0, sizeof(struct tables)); - if (db_export__init(&tables->dbe)) - Py_FatalError("failed to initialize export"); - - db_export_mode =3D PyDict_GetItemString(main_dict, perf_db_export_mode); - if (!db_export_mode) - return; - - ret =3D PyObject_IsTrue(db_export_mode); - if (ret =3D=3D -1) - handler_call_die(perf_db_export_mode); - if (!ret) - return; - - /* handle export calls */ - tables->dbe.crp =3D NULL; - db_export_calls =3D PyDict_GetItemString(main_dict, perf_db_export_calls); - if (db_export_calls) { - ret =3D PyObject_IsTrue(db_export_calls); - if (ret =3D=3D -1) - handler_call_die(perf_db_export_calls); - export_calls =3D !!ret; - } - - if (export_calls) { - tables->dbe.crp =3D - call_return_processor__new(python_process_call_return, - &tables->dbe); - if (!tables->dbe.crp) - Py_FatalError("failed to create calls processor"); - } - - /* handle export callchains */ - tables->dbe.cpr =3D NULL; - db_export_callchains =3D PyDict_GetItemString(main_dict, - perf_db_export_callchains); - if (db_export_callchains) { - ret =3D PyObject_IsTrue(db_export_callchains); - if (ret =3D=3D -1) - handler_call_die(perf_db_export_callchains); - export_callchains =3D !!ret; - } - - if (export_callchains) { - /* - * Attempt to use the call path root from the call return - * processor, if the call return processor is in use. Otherwise, - * we allocate a new call path root. This prevents exporting - * duplicate call path ids when both are in use simultaneously. - */ - if (tables->dbe.crp) - tables->dbe.cpr =3D tables->dbe.crp->cpr; - else - tables->dbe.cpr =3D call_path_root__new(); - - if (!tables->dbe.cpr) - Py_FatalError("failed to create call path root"); - } - - tables->db_export_mode =3D true; - /* - * Reserve per symbol space for symbol->db_id via symbol__priv() - */ - symbol_conf.priv_size =3D sizeof(u64); - - SET_TABLE_HANDLER(evsel); - SET_TABLE_HANDLER(machine); - SET_TABLE_HANDLER(thread); - SET_TABLE_HANDLER(comm); - SET_TABLE_HANDLER(comm_thread); - SET_TABLE_HANDLER(dso); - SET_TABLE_HANDLER(symbol); - SET_TABLE_HANDLER(branch_type); - SET_TABLE_HANDLER(sample); - SET_TABLE_HANDLER(call_path); - SET_TABLE_HANDLER(call_return); - SET_TABLE_HANDLER(context_switch); - - /* - * Synthesized events are samples but with architecture-specific data - * stored in sample->raw_data. They are exported via - * python_export_sample() and consequently do not need a separate export - * callback. - */ - tables->synth_handler =3D get_handler("synth_data"); -} - -static void _free_command_line(wchar_t **command_line, int num) -{ - int i; - for (i =3D 0; i < num; i++) - PyMem_RawFree(command_line[i]); - free(command_line); -} - - -/* - * Start trace script - */ -static int python_start_script(const char *script, int argc, const char **= argv, - struct perf_session *session) -{ - struct tables *tables =3D &tables_global; - wchar_t **command_line; - char buf[PATH_MAX]; - int i, err =3D 0; - FILE *fp; - - scripting_context->session =3D session; - command_line =3D malloc((argc + 1) * sizeof(wchar_t *)); - if (!command_line) - return -1; - - command_line[0] =3D Py_DecodeLocale(script, NULL); - for (i =3D 1; i < argc + 1; i++) - command_line[i] =3D Py_DecodeLocale(argv[i - 1], NULL); - PyImport_AppendInittab("perf_trace_context", PyInit_perf_trace_context); - Py_Initialize(); - - PySys_SetArgv(argc + 1, command_line); - - fp =3D fopen(script, "r"); - if (!fp) { - sprintf(buf, "Can't open python script \"%s\"", script); - perror(buf); - err =3D -1; - goto error; - } - - err =3D PyRun_SimpleFile(fp, script); - if (err) { - fprintf(stderr, "Error running python script %s\n", script); - goto error; - } - - err =3D run_start_sub(); - if (err) { - fprintf(stderr, "Error starting python script %s\n", script); - goto error; - } - - set_table_handlers(tables); - - if (tables->db_export_mode) { - err =3D db_export__branch_types(&tables->dbe); - if (err) - goto error; - } - - _free_command_line(command_line, argc + 1); - - return err; -error: - Py_Finalize(); - _free_command_line(command_line, argc + 1); - - return err; -} - -static int python_flush_script(void) -{ - return 0; -} - -/* - * Stop trace script - */ -static int python_stop_script(void) -{ - struct tables *tables =3D &tables_global; - - try_call_object("trace_end", NULL); - - db_export__exit(&tables->dbe); - - Py_XDECREF(main_dict); - Py_XDECREF(main_module); - Py_Finalize(); - - return 0; -} - -#ifdef HAVE_LIBTRACEEVENT -static int python_generate_script(struct tep_handle *pevent, const char *o= utfile) -{ - int i, not_first, count, nr_events; - struct tep_event **all_events; - struct tep_event *event =3D NULL; - struct tep_format_field *f; - char fname[PATH_MAX]; - FILE *ofp; - - sprintf(fname, "%s.py", outfile); - ofp =3D fopen(fname, "w"); - if (ofp =3D=3D NULL) { - fprintf(stderr, "couldn't open %s\n", fname); - return -1; - } - fprintf(ofp, "# perf script event handlers, " - "generated by perf script -g python\n"); - - fprintf(ofp, "# Licensed under the terms of the GNU GPL" - " License version 2\n\n"); - - fprintf(ofp, "# The common_* event handler fields are the most useful " - "fields common to\n"); - - fprintf(ofp, "# all events. They don't necessarily correspond to " - "the 'common_*' fields\n"); - - fprintf(ofp, "# in the format files. Those fields not available as " - "handler params can\n"); - - fprintf(ofp, "# be retrieved using Python functions of the form " - "common_*(context).\n"); - - fprintf(ofp, "# See the perf-script-python Documentation for the list " - "of available functions.\n\n"); - - fprintf(ofp, "from __future__ import print_function\n\n"); - fprintf(ofp, "import os\n"); - fprintf(ofp, "import sys\n\n"); - - fprintf(ofp, "sys.path.append(os.environ['PERF_EXEC_PATH'] + \\\n"); - fprintf(ofp, "\t'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')\n"); - fprintf(ofp, "\nfrom perf_trace_context import *\n"); - fprintf(ofp, "from Core import *\n\n\n"); - - fprintf(ofp, "def trace_begin():\n"); - fprintf(ofp, "\tprint(\"in trace_begin\")\n\n"); - - fprintf(ofp, "def trace_end():\n"); - fprintf(ofp, "\tprint(\"in trace_end\")\n\n"); - - nr_events =3D tep_get_events_count(pevent); - all_events =3D tep_list_events(pevent, TEP_EVENT_SORT_ID); - - for (i =3D 0; all_events && i < nr_events; i++) { - event =3D all_events[i]; - fprintf(ofp, "def %s__%s(", event->system, event->name); - fprintf(ofp, "event_name, "); - fprintf(ofp, "context, "); - fprintf(ofp, "common_cpu,\n"); - fprintf(ofp, "\tcommon_secs, "); - fprintf(ofp, "common_nsecs, "); - fprintf(ofp, "common_pid, "); - fprintf(ofp, "common_comm,\n\t"); - fprintf(ofp, "common_callchain, "); - - not_first =3D 0; - count =3D 0; - - for (f =3D event->format.fields; f; f =3D f->next) { - if (not_first++) - fprintf(ofp, ", "); - if (++count % 5 =3D=3D 0) - fprintf(ofp, "\n\t"); - - fprintf(ofp, "%s", f->name); - } - if (not_first++) - fprintf(ofp, ", "); - if (++count % 5 =3D=3D 0) - fprintf(ofp, "\n\t\t"); - fprintf(ofp, "perf_sample_dict"); - - fprintf(ofp, "):\n"); - - fprintf(ofp, "\t\tprint_header(event_name, common_cpu, " - "common_secs, common_nsecs,\n\t\t\t" - "common_pid, common_comm)\n\n"); - - fprintf(ofp, "\t\tprint(\""); - - not_first =3D 0; - count =3D 0; - - for (f =3D event->format.fields; f; f =3D f->next) { - if (not_first++) - fprintf(ofp, ", "); - if (count && count % 3 =3D=3D 0) { - fprintf(ofp, "\" \\\n\t\t\""); - } - count++; - - fprintf(ofp, "%s=3D", f->name); - if (f->flags & TEP_FIELD_IS_STRING || - f->flags & TEP_FIELD_IS_FLAG || - f->flags & TEP_FIELD_IS_ARRAY || - f->flags & TEP_FIELD_IS_SYMBOLIC) - fprintf(ofp, "%%s"); - else if (f->flags & TEP_FIELD_IS_SIGNED) - fprintf(ofp, "%%d"); - else - fprintf(ofp, "%%u"); - } - - fprintf(ofp, "\" %% \\\n\t\t("); - - not_first =3D 0; - count =3D 0; - - for (f =3D event->format.fields; f; f =3D f->next) { - if (not_first++) - fprintf(ofp, ", "); - - if (++count % 5 =3D=3D 0) - fprintf(ofp, "\n\t\t"); - - if (f->flags & TEP_FIELD_IS_FLAG) { - if ((count - 1) % 5 !=3D 0) { - fprintf(ofp, "\n\t\t"); - count =3D 4; - } - fprintf(ofp, "flag_str(\""); - fprintf(ofp, "%s__%s\", ", event->system, - event->name); - fprintf(ofp, "\"%s\", %s)", f->name, - f->name); - } else if (f->flags & TEP_FIELD_IS_SYMBOLIC) { - if ((count - 1) % 5 !=3D 0) { - fprintf(ofp, "\n\t\t"); - count =3D 4; - } - fprintf(ofp, "symbol_str(\""); - fprintf(ofp, "%s__%s\", ", event->system, - event->name); - fprintf(ofp, "\"%s\", %s)", f->name, - f->name); - } else - fprintf(ofp, "%s", f->name); - } - - fprintf(ofp, "))\n\n"); - - fprintf(ofp, "\t\tprint('Sample: {'+" - "get_dict_as_string(perf_sample_dict['sample'], ', ')+'}')\n\n"); - - fprintf(ofp, "\t\tfor node in common_callchain:"); - fprintf(ofp, "\n\t\t\tif 'sym' in node:"); - fprintf(ofp, "\n\t\t\t\tprint(\"\t[%%x] %%s%%s%%s%%s\" %% ("); - fprintf(ofp, "\n\t\t\t\t\tnode['ip'], node['sym']['name'],"); - fprintf(ofp, "\n\t\t\t\t\t\"+0x{:x}\".format(node['sym_off']) if 'sym_of= f' in node else \"\","); - fprintf(ofp, "\n\t\t\t\t\t\" ({})\".format(node['dso']) if 'dso' in nod= e else \"\","); - fprintf(ofp, "\n\t\t\t\t\t\" \" + node['sym_srcline'] if 'sym_srcline' i= n node else \"\"))"); - fprintf(ofp, "\n\t\t\telse:"); - fprintf(ofp, "\n\t\t\t\tprint(\"\t[%%x]\" %% (node['ip']))\n\n"); - fprintf(ofp, "\t\tprint()\n\n"); - - } - - fprintf(ofp, "def trace_unhandled(event_name, context, " - "event_fields_dict, perf_sample_dict):\n"); - - fprintf(ofp, "\t\tprint(get_dict_as_string(event_fields_dict))\n"); - fprintf(ofp, "\t\tprint('Sample: {'+" - "get_dict_as_string(perf_sample_dict['sample'], ', ')+'}')\n\n"); - - fprintf(ofp, "def print_header(" - "event_name, cpu, secs, nsecs, pid, comm):\n" - "\tprint(\"%%-20s %%5u %%05u.%%09u %%8u %%-20s \" %% \\\n\t" - "(event_name, cpu, secs, nsecs, pid, comm), end=3D\"\")\n\n"); - - fprintf(ofp, "def get_dict_as_string(a_dict, delimiter=3D' '):\n" - "\treturn delimiter.join" - "(['%%s=3D%%s'%%(k,str(v))for k,v in sorted(a_dict.items())])\n"); - - fclose(ofp); - - fprintf(stderr, "generated Python script: %s\n", fname); - - return 0; -} -#else -static int python_generate_script(struct tep_handle *pevent __maybe_unused, - const char *outfile __maybe_unused) -{ - fprintf(stderr, "Generating Python perf-script is not supported." - " Install libtraceevent and rebuild perf to enable it.\n" - "For example:\n # apt install libtraceevent-dev (ubuntu)" - "\n # yum install libtraceevent-devel (Fedora)" - "\n etc.\n"); - return -1; -} -#endif - -struct scripting_ops python_scripting_ops =3D { - .name =3D "Python", - .dirname =3D "python", - .start_script =3D python_start_script, - .flush_script =3D python_flush_script, - .stop_script =3D python_stop_script, - .process_event =3D python_process_event, - .process_switch =3D python_process_switch, - .process_auxtrace_error =3D python_process_auxtrace_error, - .process_stat =3D python_process_stat, - .process_stat_interval =3D python_process_stat_interval, - .process_throttle =3D python_process_throttle, - .generate_script =3D python_generate_script, -}; diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trac= e-event-scripting.c index a82472419611..0a0a50d9e1e1 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c @@ -138,9 +138,7 @@ static void process_event_unsupported(union perf_event = *event __maybe_unused, struct addr_location *al __maybe_unused, struct addr_location *addr_al __maybe_unused) { -} - -static void print_python_unsupported_msg(void) +} static void print_python_unsupported_msg(void) { fprintf(stderr, "Python scripting not supported." " Install libpython and rebuild perf to enable it.\n" @@ -192,20 +190,10 @@ static void register_python_scripting(struct scriptin= g_ops *scripting_ops) } } =20 -#ifndef HAVE_LIBPYTHON_SUPPORT void setup_python_scripting(void) { register_python_scripting(&python_scripting_unsupported_ops); } -#else -extern struct scripting_ops python_scripting_ops; - -void setup_python_scripting(void) -{ - register_python_scripting(&python_scripting_ops); -} -#endif - =20 =20 static const struct { --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 49043399000 for ; Sat, 25 Apr 2026 22:52:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157531; cv=none; b=eJAscvL1/9Mg+Qm1ADNalQiMGMB610+viYqs1pwLTEGGhfZh4XwvPXlFr8awCSFcNoCQH8V0CNEhq8jxJxnJpFb0YQlUvsknvBNvAwLeX2Fm/f1lo2tpyCsWS7gZ6XwejLiqJfDMm27rmm9FO1Fsdap0t8flchGaOuYaePEe8Xo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157531; c=relaxed/simple; bh=S/MOHsrAK2wj/AmmX0K/d6tBXNsHAINGPYqbaAYXZKM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=IaUpoCbEOPYLYBrYaM0+rkFoDhrusMt7Z2EJkKHZz3dOXcPE6cxAddLJlu5VsjhyVuBEtNNRd4edgmD0cwqduMcXZyR1+DZ1IT74d/hrTeNXoNGjIenOZO1YvAIRTa0f5aGsILAwK9uJ5LOtq6ly+XbsoQSDSWXpYRFppXbrUdg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=J8XHvZnj; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="J8XHvZnj" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-12c21dbc9c1so33859207c88.0 for ; Sat, 25 Apr 2026 15:52:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157528; x=1777762328; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=FUJSZggZAnW35gY6L6UfepyO4olhr8IMzDQNCfxT3yU=; b=J8XHvZnjhzImCB3Tet6XTyJeP1JtYnFDylG/CfxcqFhoWgGivfVlXKP5hBFseX4CIf OohRWvQJ6o8ylvOin27IpJbpwAYVdlj98GkqX/2zX84XxKm9f14SkPFOETP6qsexryb4 yTo9JFN6nCbXWQNa8Mfanz5bFV++7LEW6EMtawrcvddNcMiISVfWp8FSCQf6C69wKx6s /TBLk3J4epfcgAywWgfh9KHxQiE2FIbxCt+EE9Gsjf7Mtpg6MV22d6UHPAmTFWToUWWN pijyyvjN0850phFrbJfvc2xIDo9Q+ri0SbIjykevYTnQ1cImUHu/+sD2qVwdwktBkwIw EYuw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157528; x=1777762328; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=FUJSZggZAnW35gY6L6UfepyO4olhr8IMzDQNCfxT3yU=; b=pNcCYxWePYXnWDi3UJrFkqZfQppLaa1sLNO8r/i5h14cDcQztGv1QdCxTXSxyOK5Fj voaz8xt1TiTQShe76NoDZXTsXep2YENcvlQ7kWvI7M9Koc9KW4zlUqkjUBi0Lenz6lT4 2JsPxQbwK4I2K2mrq/NyTJU7/r1KLaddXV6NwGcVAtL/F0yPWHCYzG6UKZ9+TEa7qyls oNAObuO0X5zk8mquEwkUmy2/1bT4EtNoZBK7iywwG8yd8b/khmOFTXSXapVDZNsWpTgw jhD7rI2OnOGw7cqSQMBmGpOF6Na5DJrKcpb52u72LMghiLVGo2Z8eek2p/PQmBOkECK0 MaXA== X-Forwarded-Encrypted: i=1; AFNElJ9KKFjxEqfZejBGBDPmC/rfERz7KK+rEUCBek5zu4XvPOd3oK/iYF6WkVf60WutT3WtpjUc9++yoLE5qfQ=@vger.kernel.org X-Gm-Message-State: AOJu0YydBzr8DMpxP72YPxHVRPzBva7T/dcUuLvfaHaujgPBvgeOVGmZ Pi0BZl7+HKSKxvHAzeg7N7l/1bemuxSKdyHXq6oweyPfKT0mjNWR5VIhWNgwAe93IQT5uQ7EGYW 9Wqc5oKjj1g== X-Received: from dlbvg6.prod.google.com ([2002:a05:7022:7f06:b0:12d:b396:eadf]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:4199:b0:122:2f4:b24b with SMTP id a92af1059eb24-12c73f9bfebmr18766156c88.25.1777157528294; Sat, 25 Apr 2026 15:52:08 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:47 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-56-irogers@google.com> Subject: [PATCH v7 55/59] perf Makefile: Update Python script installation path From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Remove libpython feature test that is now a python-module feature test. Update references from libpython to python-module accordingly. Remove references to legacy 'scripts/python' directory and install scripts directly to 'python' directory under libexec. This aligns with the removal of embedded interpreter and move to standalone scripts. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- tools/build/Makefile.feature | 4 ++-- tools/build/feature/Makefile | 4 ++-- tools/build/feature/test-all.c | 6 +++--- tools/build/feature/test-libpython.c | 10 ---------- tools/build/feature/test-python-module.c | 12 ++++++++++++ tools/perf/Makefile.config | 13 ++++++------- tools/perf/Makefile.perf | 10 ++++------ tools/perf/tests/make | 5 +---- 8 files changed, 30 insertions(+), 34 deletions(-) delete mode 100644 tools/build/feature/test-libpython.c create mode 100644 tools/build/feature/test-python-module.c diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 96d4382144c4..cbe41ba7bae5 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -79,7 +79,7 @@ FEATURE_TESTS_BASIC :=3D \ libelf-zstd \ libnuma \ numa_num_possible_cpus \ - libpython \ + python-module \ libslang \ libtraceevent \ libcpupower \ @@ -140,7 +140,7 @@ FEATURE_DISPLAY ?=3D \ libelf \ libnuma \ numa_num_possible_cpus \ - libpython \ + python-module \ libcapstone \ llvm-perf \ zlib \ diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index 60e3df8142a5..5530f9e03fcf 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -30,7 +30,7 @@ FILES=3D \ test-libdebuginfod.bin \ test-libnuma.bin \ test-numa_num_possible_cpus.bin \ - test-libpython.bin \ + test-python-module.bin \ test-libslang.bin \ test-libtraceevent.bin \ test-libcpupower.bin \ @@ -252,7 +252,7 @@ $(OUTPUT)test-gtk2-infobar.bin: grep-libs =3D $(filter -l%,$(1)) strip-libs =3D $(filter-out -l%,$(1)) =20 -$(OUTPUT)test-libpython.bin: +$(OUTPUT)test-python-module.bin: $(BUILD) $(FLAGS_PYTHON_EMBED) =20 $(OUTPUT)test-libbfd.bin: diff --git a/tools/build/feature/test-all.c b/tools/build/feature/test-all.c index 1488bf6e6078..4400e3d24f81 100644 --- a/tools/build/feature/test-all.c +++ b/tools/build/feature/test-all.c @@ -10,8 +10,8 @@ * Quirk: Python headers cannot be in arbitrary places, so keep this testc= ase at * the top: */ -#define main main_test_libpython -# include "test-libpython.c" +#define main main_test_python_module +# include "test-python-module.c" #undef main =20 #define main main_test_hello @@ -148,7 +148,7 @@ =20 int main(int argc, char *argv[]) { - main_test_libpython(); + main_test_python_module(); main_test_hello(); main_test_libelf(); main_test_gettid(); diff --git a/tools/build/feature/test-libpython.c b/tools/build/feature/tes= t-libpython.c deleted file mode 100644 index 371c9113e49d..000000000000 --- a/tools/build/feature/test-libpython.c +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include - -int main(void) -{ - Py_Initialize(); - - return 0; -} -#undef _GNU_SOURCE diff --git a/tools/build/feature/test-python-module.c b/tools/build/feature= /test-python-module.c new file mode 100644 index 000000000000..d670dba014b0 --- /dev/null +++ b/tools/build/feature/test-python-module.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +int main(void) +{ + static struct PyModuleDef moduledef =3D { + PyModuleDef_HEAD_INIT, + }; + PyObject *module =3D PyModule_Create(&moduledef); + + return module ? 0 : -1; +} diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index ecddd91229c8..e2cef452964f 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -317,7 +317,7 @@ PYTHON_CONFIG_SQ :=3D $(call shell-sq,$(PYTHON_CONFIG)) =20 # Python 3.8 changed the output of `python-config --ldflags` to not includ= e the # '-lpythonX.Y' flag unless '--embed' is also passed. The feature check for -# libpython fails if that flag is not included in LDFLAGS +# python-module fails if that flag is not included in LDFLAGS ifeq ($(shell $(PYTHON_CONFIG_SQ) --ldflags --embed 2>&1 1>/dev/null; echo= $$?), 0) PYTHON_CONFIG_LDFLAGS :=3D --ldflags --embed else @@ -340,8 +340,8 @@ ifdef PYTHON_CONFIG endif endif =20 -FEATURE_CHECK_CFLAGS-libpython :=3D $(PYTHON_EMBED_CCOPTS) -FEATURE_CHECK_LDFLAGS-libpython :=3D $(PYTHON_EMBED_LDOPTS) +FEATURE_CHECK_CFLAGS-python-module :=3D $(PYTHON_EMBED_CCOPTS) +FEATURE_CHECK_LDFLAGS-python-module :=3D $(PYTHON_EMBED_LDOPTS) =20 FEATURE_CHECK_LDFLAGS-libaio =3D -lrt =20 @@ -830,13 +830,12 @@ endif =20 disable-python =3D $(eval $(disable-python_code)) define disable-python_code - CFLAGS +=3D -DNO_LIBPYTHON $(warning $1) - NO_LIBPYTHON :=3D 1 + NO_PYTHON_MODULE :=3D 1 endef =20 PYTHON_EXTENSION_SUFFIX :=3D '.so' -ifdef NO_LIBPYTHON +ifdef NO_PYTHON_MODULE $(call disable-python,Python support disabled by user) else =20 @@ -849,7 +848,7 @@ else $(call disable-python,No 'python-config' tool was found: disables Py= thon support - please install python-devel/python-dev) else =20 - ifneq ($(feature-libpython), 1) + ifneq ($(feature-python-module), 1) $(call disable-python,No 'Python.h' was found: disables Python sup= port - please install python-devel/python-dev) else PYTHON_SETUPTOOLS_INSTALLED :=3D $(shell $(PYTHON) -c 'import set= uptools;' 2> /dev/null && echo "yes" || echo "no") diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 2020532bab9c..e50b1e8cf85d 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -17,9 +17,7 @@ include ../scripts/utilities.mak # # Define CROSS_COMPILE as prefix name of compiler if you want cross-builds. # - -# -# Define NO_LIBPYTHON to disable python script extension. +# Define NO_PYTHON_MODULE to disable python script extension. # # Define PYTHON to point to the python binary if the default # `python' is not correct; for example: PYTHON=3Dpython2 @@ -1099,10 +1097,10 @@ endif $(call QUIET_INSTALL, perf-iostat) \ $(INSTALL) $(OUTPUT)perf-iostat -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' =20 -ifndef NO_LIBPYTHON +ifndef NO_PYTHON_MODULE $(call QUIET_INSTALL, python-scripts) \ - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python= '; \ - $(INSTALL) python/*.py -m 644 -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/sc= ripts/python' + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/python'; \ + $(INSTALL) python/*.py -m 644 -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/py= thon' endif $(call QUIET_INSTALL, dlfilters) \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/dlfilters'; \ diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 31b064928cfc..f2c5f1c254a7 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -75,8 +75,6 @@ make_jevents_all :=3D JEVENTS_ARCH=3Dall make_no_bpf_skel :=3D BUILD_BPF_SKEL=3D0 make_gen_vmlinux_h :=3D GEN_VMLINUX_H=3D1 =20 -make_no_libpython :=3D NO_LIBPYTHON=3D1 -make_no_scripts :=3D NO_LIBPYTHON=3D1 make_no_slang :=3D NO_SLANG=3D1 make_no_gtk2 :=3D NO_GTK2=3D1 make_no_ui :=3D NO_SLANG=3D1 NO_GTK2=3D1 @@ -118,7 +116,7 @@ make_install_prefix_slash :=3D install prefix=3D/tmp/kr= ava/ make_static :=3D LDFLAGS=3D-static NO_PERF_READ_VDSO32=3D1 NO_PERF= _READ_VDSOX32=3D1 NO_JVMTI=3D1 NO_LIBTRACEEVENT=3D1 NO_LIBELF=3D1 =20 # all the NO_* variable combined -make_minimal :=3D NO_LIBPYTHON=3D1 NO_GTK2=3D1 +make_minimal :=3D NO_GTK2=3D1 make_minimal +=3D NO_DEMANGLE=3D1 NO_LIBELF=3D1 NO_BACKTRACE=3D1 make_minimal +=3D NO_LIBNUMA=3D1 NO_LIBBIONIC=3D1 NO_LIBDW=3D1 make_minimal +=3D NO_LIBBPF=3D1 @@ -150,7 +148,6 @@ run +=3D make_jevents_all run +=3D make_no_bpf_skel run +=3D make_gen_vmlinux_h =20 -run +=3D make_no_libpython run +=3D make_no_scripts run +=3D make_no_slang run +=3D make_no_gtk2 --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 19 09:17:50 2026 Received: from mail-dl1-f74.google.com (mail-dl1-f74.google.com [74.125.82.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 571BC3B27F3 for ; Sat, 25 Apr 2026 22:52:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157535; cv=none; b=TEOkLmrJdR4f3qm4ETZXThNwE/IuhhWCt1eCCUx3j4z9Iv6btEdurz77fSO9wefDIffVptzHA9A0Obvs4sB5+LV5eRRoON3xjZdCfpr8UGOdbyIYhQLt5TWVKbwQ81YDCKNOht965mk6qHqI/cjMtIqUKwy8JWh4Sw/W3iD0ruk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777157535; c=relaxed/simple; bh=ylIOos0xHnsexnr6jdmSwf+9UMTS5OICXp7IpauL+jU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=OvQot4beVK4YvhXNRzmJWAdIWbq18FUaAHzltZNdOeAxY9T34QcdBhuv6z1Vxlqlw0g0CdgDmYaPn/ThKsy1JOdp5gW06z7SkBgXuU0ZbYEf6FG+Epkf12gF1p4CIo0xqeZ6Ltkn7un59yQkWYFdGBIT50xi9SDQZD5dbkx9THw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=AwIHqkNR; arc=none smtp.client-ip=74.125.82.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="AwIHqkNR" Received: by mail-dl1-f74.google.com with SMTP id a92af1059eb24-12c8de02a4dso15631996c88.1 for ; Sat, 25 Apr 2026 15:52:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777157531; x=1777762331; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=lO1tHitpn65GyNQ7usXzAhdR+Ee3pXPwIleRJ25KUz8=; b=AwIHqkNRXyi1GgGt3nWELVDaZ7XN1tNm5nFAOLbLm2rU3gSJSn9PpJZVOfEk4jdIOk yKJsNwO2kCENRQM3fqRvhCIWZS0FFkpZF4wmYPmHpHNGre/HlvEOmFr6QIJXz9WBfJW9 onnQz/K2YGagntN6fyYlipdGSZJOHPs8ga7i3e7zZnz9a9675UcnGYWhx8vBw8b8cb+m 1Xz+4iqyfrROBeC5wT949j2fX6Gg9phTOHg34MsQ9NRCBfmgFeiCf7rvsklm6zxl4vr8 LqGGMHUs0RE8P3527EBf+073t4yOFfJsS9VPFmjdaN0C1AxEBtUrzkmgo3HA0ouZ/9rw IYEw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777157531; x=1777762331; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=lO1tHitpn65GyNQ7usXzAhdR+Ee3pXPwIleRJ25KUz8=; b=oJaEsx6GuoVq97uJlW5PmDRQUhJKUu9ZruepgiiO3rxpTKaDReD9MZUThEuCOldeKB VhfwNKN+8pG/jp6BRWF6oxqV3+62roksSxExUQQOZ//2hltNqtQUuWXGW8rbSHiEfBwM xtwTreinBdl/qn2CXoWLYlQUEWoyQMjS2Kr19XeZTtUw3lB7yFi2mMqtn5lz18amH4BM HrZTIrSQt2fibeuYTEHCoCCwEjEc0/J8sdeTbsBc/9J4BHjHzApIp4vSSVB/IAJ7Z0DI yvVCAX6JLJw8CGUIOmtv+XSUIRkEK/ZhwXuLoTv4ofx4yrdFt260eWw3Z4Ae4kDWYeO/ stsg== X-Forwarded-Encrypted: i=1; AFNElJ8uV//XaUEKav8DP3M7sXcBKDB2VkuFeUT9RLXgnw0b5JBf6jYZE9dafHmwIBeHWnQwC1DTzzNn5u8dwTg=@vger.kernel.org X-Gm-Message-State: AOJu0YwmgZgwUGJCMeQLTVCfYRQCH6/sY5aBgkbeIhKgXch7yYTugBp9 j6A1zuEPo4nnGd6dWiWshYGz7Orhs1DvN+6znuzVUgc7W9z1Dr+8P4FHILFlnB4upOgjA4ezcop 7kFV94PPhYw== X-Received: from dybzy3.prod.google.com ([2002:a05:7301:e103:b0:2df:af:8fde]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:693c:2b04:b0:2e1:f72:3f2a with SMTP id 5a478bee46e88-2e4647d03admr20275957eec.4.1777157530358; Sat, 25 Apr 2026 15:52:10 -0700 (PDT) Date: Sat, 25 Apr 2026 15:49:48 -0700 In-Reply-To: <20260425224951.174663-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260425174858.3922152-1-irogers@google.com> <20260425224951.174663-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260425224951.174663-57-irogers@google.com> Subject: [PATCH v7 56/59] perf script: Refactor to support standalone scripts and remove legacy features From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" - Remove -g / --gen-script option as it is no longer needed. - Hide -s / --script option to imply running standalone scripts directly. - Update find_script to search in 'python' instead of 'scripts/python'. - Add support for launching standalone scripts using fork and execvp, skipping the event processing loop. - Update list_available_scripts to look for .py files directly in 'python' directory. - Remove all references to scripting_ops and clean up unused functions and variables. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: - Fixed strncat buffer overflow: Updated the strncat call in find_script() to correctly use the remaining capacity of the destination buffer instead of sizeof(path) - 1 . The declaration of len was moved to the top of the function to conform to C style guidelines. - Fixed execvp path searching: If find_script() finds a local file in the current directory without a slash, it now prepends ./ to it. This ensures that execvp() executed by the child process knows to look in the current directory rather than searching the system $PATH . - Fixed premature loop termination in docstring parsing: Removed a check in read_script_info() that caused the loop to terminate early if any fallback description was found (like an SPDX identifier). This restores the intended behavior of searching the entire header for a better "description:" tag. - Updated subcommands and usage: Removed "record" and "report" from the usage text and stopped passing them as valid subcommands to parse_options_subcommand() . - Fixed lost command-line options: Reconstructed the arguments passed to the standalone script to include -i and the input file path, ensuring that the user's choice of input file is not lost. - Added error message on execvp failure: Added a pr_err call in the child process to print a descriptive error message if execvp() fails to launch the script. - Fixed uninitialized status in waitpid : Initialized status to 0 and verified that waitpid() successfully returned the child's PID before evaluating its exit status. Also removed unnecessary braces and fixed indentation in that block. --- tools/perf/builtin-script.c | 767 +++++++++--------------- tools/perf/util/Build | 1 - tools/perf/util/scripting-engines/Build | 1 - tools/perf/util/trace-event-parse.c | 65 -- tools/perf/util/trace-event-scripting.c | 333 ---------- tools/perf/util/trace-event.h | 75 +-- 6 files changed, 294 insertions(+), 948 deletions(-) delete mode 100644 tools/perf/util/scripting-engines/Build delete mode 100644 tools/perf/util/trace-event-scripting.c diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index c0949556d1bb..9b672edac2ca 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include =20 @@ -77,7 +78,6 @@ #endif =20 static char const *script_name; -static char const *generate_script_lang; static bool reltime; static bool deltatime; static u64 initial_time; @@ -95,6 +95,7 @@ static int max_blocks; static struct dlfilter *dlfilter; static int dlargc; static char **dlargv; +static unsigned int scripting_max_stack =3D PERF_MAX_STACK_DEPTH; =20 enum perf_output_field { PERF_OUTPUT_COMM =3D 1ULL << 0, @@ -1730,6 +1731,143 @@ static int perf_sample__fprintf_bts(struct perf_sam= ple *sample, return printed; } =20 +#define SAMPLE_FLAGS_BUF_SIZE 64 +#define SAMPLE_FLAGS_STR_ALIGNED_SIZE 21 + +static int sample_flags_to_name(u32 flags, char *str, size_t size) +{ + static const struct { + u32 flags; + const char *name; + } sample_flags[] =3D { + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL, "call"}, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN, "return"}, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL, "jcc"}, + {PERF_IP_FLAG_BRANCH, "jmp"}, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_INTERRUPT, "int"= }, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT, "ir= et"}, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_SYSCALLRET, "sys= call"}, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET, "s= ysret"}, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_ASYNC, "async"}, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC | PERF_IP_= FLAG_INTERRUPT, + "hw int"}, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "tx abrt"}, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "tr strt"}, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "tr end"}, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMENTRY, "vmentr= y"}, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMEXIT, "vmexit"= }, + {0, NULL} + }; + static const struct { + u32 flags; + const char *name; + } branch_events[] =3D { + {PERF_IP_FLAG_BRANCH_MISS, "miss"}, + {PERF_IP_FLAG_NOT_TAKEN, "not_taken"}, + {0, NULL} + }; + int i; + const char *prefix; + int pos =3D 0, ret, ev_idx =3D 0; + u32 xf =3D flags & PERF_ADDITIONAL_STATE_MASK; + u32 types, events; + char xs[16] =3D { 0 }; + + /* Clear additional state bits */ + flags &=3D ~PERF_ADDITIONAL_STATE_MASK; + + if (flags & PERF_IP_FLAG_TRACE_BEGIN) + prefix =3D "tr strt "; + else if (flags & PERF_IP_FLAG_TRACE_END) + prefix =3D "tr end "; + else + prefix =3D ""; + + ret =3D snprintf(str + pos, size - pos, "%s", prefix); + if (ret < 0) + return ret; + pos +=3D ret; + + flags &=3D ~(PERF_IP_FLAG_TRACE_BEGIN | PERF_IP_FLAG_TRACE_END); + + types =3D flags & ~PERF_IP_FLAG_BRANCH_EVENT_MASK; + for (i =3D 0; sample_flags[i].name; i++) { + if (sample_flags[i].flags !=3D types) + continue; + + ret =3D snprintf(str + pos, size - pos, "%s", sample_flags[i].name); + if (ret < 0) + return ret; + pos +=3D ret; + break; + } + + events =3D flags & PERF_IP_FLAG_BRANCH_EVENT_MASK; + for (i =3D 0; branch_events[i].name; i++) { + if (!(branch_events[i].flags & events)) + continue; + + ret =3D snprintf(str + pos, size - pos, !ev_idx ? "/%s" : ",%s", + branch_events[i].name); + if (ret < 0) + return ret; + pos +=3D ret; + ev_idx++; + } + + /* Add an end character '/' for events */ + if (ev_idx) { + ret =3D snprintf(str + pos, size - pos, "/"); + if (ret < 0) + return ret; + pos +=3D ret; + } + + if (!xf) + return pos; + + snprintf(xs, sizeof(xs), "(%s%s%s)", + flags & PERF_IP_FLAG_IN_TX ? "x" : "", + flags & PERF_IP_FLAG_INTR_DISABLE ? "D" : "", + flags & PERF_IP_FLAG_INTR_TOGGLE ? "t" : ""); + + /* Right align the string if its length is less than the limit */ + if ((pos + strlen(xs)) < SAMPLE_FLAGS_STR_ALIGNED_SIZE) + ret =3D snprintf(str + pos, size - pos, "%*s", + (int)(SAMPLE_FLAGS_STR_ALIGNED_SIZE - ret), xs); + else + ret =3D snprintf(str + pos, size - pos, " %s", xs); + if (ret < 0) + return ret; + + return pos + ret; +} + +static int perf_sample__sprintf_flags(u32 flags, char *str, size_t sz) +{ + const char *chars =3D PERF_IP_FLAG_CHARS; + const size_t n =3D strlen(PERF_IP_FLAG_CHARS); + size_t i, pos =3D 0; + int ret; + + ret =3D sample_flags_to_name(flags, str, sz); + if (ret > 0) + return ret; + + for (i =3D 0; i < n; i++, flags >>=3D 1) { + if ((flags & 1) && pos < sz) + str[pos++] =3D chars[i]; + } + for (; i < 32; i++, flags >>=3D 1) { + if ((flags & 1) && pos < sz) + str[pos++] =3D '?'; + } + if (pos < sz) + str[pos] =3D 0; + + return pos; +} + static int perf_sample__fprintf_flags(u32 flags, FILE *fp) { char str[SAMPLE_FLAGS_BUF_SIZE]; @@ -2571,8 +2709,6 @@ static void process_event(struct perf_script *script, fflush(fp); } =20 -static struct scripting_ops *scripting_ops; - static void __process_stat(struct evsel *counter, u64 tstamp) { int nthreads =3D perf_thread_map__nr(counter->core.threads); @@ -2607,35 +2743,14 @@ static void __process_stat(struct evsel *counter, u= 64 tstamp) =20 static void process_stat(struct evsel *counter, u64 tstamp) { - if (scripting_ops && scripting_ops->process_stat) - scripting_ops->process_stat(&stat_config, counter, tstamp); - else - __process_stat(counter, tstamp); + __process_stat(counter, tstamp); } =20 -static void process_stat_interval(u64 tstamp) +static void process_stat_interval(u64 tstamp __maybe_unused) { - if (scripting_ops && scripting_ops->process_stat_interval) - scripting_ops->process_stat_interval(tstamp); } =20 -static void setup_scripting(void) -{ =20 - setup_python_scripting(); -} - -static int flush_scripting(void) -{ - return scripting_ops ? scripting_ops->flush_script() : 0; -} - -static int cleanup_scripting(void) -{ - pr_debug("\nperf script stopped\n"); - - return scripting_ops ? scripting_ops->stop_script() : 0; -} =20 static bool filter_cpu(struct perf_sample *sample) { @@ -2708,19 +2823,7 @@ static int process_sample_event(const struct perf_to= ol *tool, goto out_put; } =20 - if (scripting_ops) { - struct addr_location *addr_al_ptr =3D NULL; - - if ((evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) && - sample_addr_correlates_sym(&evsel->core.attr)) { - if (!addr_al.thread) - thread__resolve(al.thread, &addr_al, sample); - addr_al_ptr =3D &addr_al; - } - scripting_ops->process_event(event, sample, evsel, &al, addr_al_ptr); - } else { - process_event(scr, sample, evsel, &al, &addr_al, machine); - } + process_event(scr, sample, evsel, &al, &addr_al, machine); =20 out_put: addr_location__exit(&addr_al); @@ -3029,8 +3132,7 @@ static int process_switch_event(const struct perf_too= l *tool, if (perf_event__process_switch(tool, event, sample, machine) < 0) return -1; =20 - if (scripting_ops && scripting_ops->process_switch && !filter_cpu(sample)) - scripting_ops->process_switch(event, sample, machine); + =20 if (!script->show_switch_events) return 0; @@ -3039,17 +3141,7 @@ static int process_switch_event(const struct perf_to= ol *tool, sample->tid); } =20 -static int process_auxtrace_error(const struct perf_tool *tool, - struct perf_session *session, - union perf_event *event) -{ - if (scripting_ops && scripting_ops->process_auxtrace_error) { - scripting_ops->process_auxtrace_error(session, event); - return 0; - } =20 - return perf_event__process_auxtrace_error(tool, session, event); -} =20 static int process_lost_event(const struct perf_tool *tool, @@ -3063,12 +3155,11 @@ process_lost_event(const struct perf_tool *tool, =20 static int process_throttle_event(const struct perf_tool *tool __maybe_unused, - union perf_event *event, - struct perf_sample *sample, - struct machine *machine) + union perf_event *event __maybe_unused, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) { - if (scripting_ops && scripting_ops->process_throttle) - scripting_ops->process_throttle(event, sample, machine); + return 0; } =20 @@ -3211,10 +3302,9 @@ static int __cmd_script(struct perf_script *script) script->tool.mmap =3D process_mmap_event; script->tool.mmap2 =3D process_mmap2_event; } - if (script->show_switch_events || (scripting_ops && scripting_ops->proces= s_switch)) + if (script->show_switch_events) script->tool.context_switch =3D process_switch_event; - if (scripting_ops && scripting_ops->process_auxtrace_error) - script->tool.auxtrace_error =3D process_auxtrace_error; + script->tool.auxtrace_error =3D perf_event__process_auxtrace_error; if (script->show_namespace_events) script->tool.namespaces =3D process_namespaces_event; if (script->show_cgroup_events) @@ -3251,96 +3341,55 @@ static int __cmd_script(struct perf_script *script) return ret; } =20 -static int list_available_languages_cb(struct scripting_ops *ops, const ch= ar *spec) -{ - fprintf(stderr, " %-42s [%s]\n", spec, ops->name); - return 0; -} =20 -static void list_available_languages(void) -{ - fprintf(stderr, "\n"); - fprintf(stderr, "Scripting language extensions (used in " - "perf script -s [spec:]script.[spec]):\n\n"); - script_spec__for_each(&list_available_languages_cb); - fprintf(stderr, "\n"); -} =20 /* Find script file relative to current directory or exec path */ static char *find_script(const char *script) { char path[PATH_MAX]; + char *exec_path; + size_t len; =20 - if (!scripting_ops) { - const char *ext =3D strrchr(script, '.'); + if (access(script, R_OK) =3D=3D 0) { + if (!strchr(script, '/')) { + snprintf(path, sizeof(path), "./%s", script); + script =3D path; + } + goto found; + } =20 - if (!ext) - return NULL; + exec_path =3D get_argv_exec_path(); + if (!exec_path) + return NULL; =20 - scripting_ops =3D script_spec__lookup(++ext); - if (!scripting_ops) - return NULL; - } + snprintf(path, sizeof(path), "%s/python/%s", exec_path, script); + free(exec_path); + script =3D path; =20 - if (access(script, R_OK)) { - char *exec_path =3D get_argv_exec_path(); + if (access(path, R_OK) =3D=3D 0) + goto found; =20 - if (!exec_path) - return NULL; - snprintf(path, sizeof(path), "%s/scripts/%s/%s", - exec_path, scripting_ops->dirname, script); - free(exec_path); - script =3D path; - if (access(script, R_OK)) - return NULL; - } + /* Try with .py suffix. */ + len =3D strlen(path); + + strncat(path, ".py", sizeof(path) - len - 1); + + if (access(script, R_OK) =3D=3D 0) + goto found; + + /* Failure to find script. */ + return NULL; + +found: return strdup(script); } =20 static int parse_scriptname(const struct option *opt __maybe_unused, const char *str, int unset __maybe_unused) { - char spec[PATH_MAX]; - const char *script, *ext; - int len; - - if (strcmp(str, "lang") =3D=3D 0) { - list_available_languages(); - exit(0); - } - - script =3D strchr(str, ':'); - if (script) { - len =3D script - str; - if (len >=3D PATH_MAX) { - fprintf(stderr, "invalid language specifier"); - return -1; - } - strncpy(spec, str, len); - spec[len] =3D '\0'; - scripting_ops =3D script_spec__lookup(spec); - if (!scripting_ops) { - fprintf(stderr, "invalid language specifier"); - return -1; - } - script++; - } else { - script =3D str; - ext =3D strrchr(script, '.'); - if (!ext) { - fprintf(stderr, "invalid script extension"); - return -1; - } - scripting_ops =3D script_spec__lookup(++ext); - if (!scripting_ops) { - fprintf(stderr, "invalid script extension"); - return -1; - } - } - - script_name =3D find_script(script); + script_name =3D find_script(str); if (!script_name) - script_name =3D strdup(script); + script_name =3D strdup(str); =20 return 0; } @@ -3551,16 +3600,18 @@ static struct script_desc *script_desc__new(const c= har *name) return s; } =20 -static void script_desc__delete(struct script_desc *s) -{ - zfree(&s->name); - zfree(&s->half_liner); - zfree(&s->args); - free(s); -} + =20 static void script_desc__add(struct script_desc *s) { + struct script_desc *pos; + + list_for_each_entry(pos, &script_descs, node) { + if (strcasecmp(s->name, pos->name) < 0) { + list_add_tail(&s->node, &pos->node); + return; + } + } list_add_tail(&s->node, &script_descs); } =20 @@ -3608,15 +3659,57 @@ static int read_script_info(struct script_desc *des= c, const char *filename) { char line[BUFSIZ], *p; FILE *fp; + bool in_docstring =3D false; + bool found_description =3D false; =20 fp =3D fopen(filename, "r"); if (!fp) return -1; =20 while (fgets(line, sizeof(line), fp)) { + static const char * const triple_quote_str[] =3D { + "\"\"\"", + "'''", + "r\"\"\"", + }; p =3D skip_spaces(line); if (strlen(p) =3D=3D 0) continue; + + if (in_docstring) { + if (strlen(p) && p[strlen(p) - 1] =3D=3D '\n') + p[strlen(p) - 1] =3D '\0'; + desc->half_liner =3D strdup(skip_spaces(p)); + in_docstring =3D false; + found_description =3D true; + break; + } + + + for (size_t i =3D 0; i < ARRAY_SIZE(triple_quote_str); i++) { + const char *quote =3D triple_quote_str[i]; + + if (!strstarts(p, quote)) + continue; + + p +=3D strlen(quote); + p =3D skip_spaces(p); + if (strlen(p) > 0) { + if (p[strlen(p) - 1] =3D=3D '\n') + p[strlen(p) - 1] =3D '\0'; + p =3D skip_spaces(p); + if (str_ends_with(p, quote)) + p[strlen(p) - strlen(quote)] =3D '\0'; + desc->half_liner =3D strdup(skip_spaces(p)); + found_description =3D true; + break; + } + in_docstring =3D true; + break; + } + if (in_docstring) + continue; + if (*p !=3D '#') continue; p++; @@ -3630,13 +3723,15 @@ static int read_script_info(struct script_desc *des= c, const char *filename) if (!strncmp(p, "description:", strlen("description:"))) { p +=3D strlen("description:"); desc->half_liner =3D strdup(skip_spaces(p)); - continue; + found_description =3D true; + break; } =20 - if (!strncmp(p, "args:", strlen("args:"))) { - p +=3D strlen("args:"); - desc->args =3D strdup(skip_spaces(p)); - continue; + if (!found_description && strlen(p) > 0 && + strncmp(p, "SPDX-License-Identifier", 23)) { + desc->half_liner =3D strdup(p); + found_description =3D true; + // Don't break, maybe we find a better "description:" later! } } =20 @@ -3667,9 +3762,9 @@ static int list_available_scripts(const struct option= *opt __maybe_unused, const char *s __maybe_unused, int unset __maybe_unused) { - struct dirent *script_dirent, *lang_dirent; - char *buf, *scripts_path, *script_path, *lang_path, *first_half; - DIR *scripts_dir, *lang_dir; + struct dirent *script_dirent; + char *buf, *scripts_path, *script_path, *first_half; + DIR *scripts_dir; struct script_desc *desc; char *script_root; =20 @@ -3680,10 +3775,9 @@ static int list_available_scripts(const struct optio= n *opt __maybe_unused, } scripts_path =3D buf; script_path =3D buf + MAXPATHLEN; - lang_path =3D buf + 2 * MAXPATHLEN; first_half =3D buf + 3 * MAXPATHLEN; =20 - snprintf(scripts_path, MAXPATHLEN, "%s/scripts", get_argv_exec_path()); + snprintf(scripts_path, MAXPATHLEN, "%s/python", get_argv_exec_path()); =20 scripts_dir =3D opendir(scripts_path); if (!scripts_dir) { @@ -3695,26 +3789,24 @@ static int list_available_scripts(const struct opti= on *opt __maybe_unused, exit(-1); } =20 - for_each_lang(scripts_path, scripts_dir, lang_dirent) { - scnprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, - lang_dirent->d_name); - lang_dir =3D opendir(lang_path); - if (!lang_dir) - continue; + while ((script_dirent =3D readdir(scripts_dir)) !=3D NULL) { + if (script_dirent->d_type !=3D DT_DIR && + (script_dirent->d_type !=3D DT_UNKNOWN || + !is_directory(scripts_path, script_dirent))) { =20 - for_each_script(lang_path, lang_dir, script_dirent) { - script_root =3D get_script_root(script_dirent, REPORT_SUFFIX); + script_root =3D get_script_root(script_dirent, ".py"); if (script_root) { desc =3D script_desc__findnew(script_root); scnprintf(script_path, MAXPATHLEN, "%s/%s", - lang_path, script_dirent->d_name); + scripts_path, script_dirent->d_name); read_script_info(desc, script_path); free(script_root); } } } + closedir(scripts_dir); =20 - fprintf(stdout, "List of available trace scripts:\n"); + fprintf(stdout, "List of available scripts:\n"); list_for_each_entry(desc, &script_descs, node) { sprintf(first_half, "%s %s", desc->name, desc->args ? desc->args : ""); @@ -3754,93 +3846,7 @@ static void free_dlarg(void) free(dlargv); } =20 -static char *get_script_path(const char *script_root, const char *suffix) -{ - struct dirent *script_dirent, *lang_dirent; - char scripts_path[MAXPATHLEN]; - char script_path[MAXPATHLEN]; - DIR *scripts_dir, *lang_dir; - char lang_path[MAXPATHLEN]; - char *__script_root; - - snprintf(scripts_path, MAXPATHLEN, "%s/scripts", get_argv_exec_path()); - - scripts_dir =3D opendir(scripts_path); - if (!scripts_dir) - return NULL; - - for_each_lang(scripts_path, scripts_dir, lang_dirent) { - scnprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, - lang_dirent->d_name); - lang_dir =3D opendir(lang_path); - if (!lang_dir) - continue; - - for_each_script(lang_path, lang_dir, script_dirent) { - __script_root =3D get_script_root(script_dirent, suffix); - if (__script_root && !strcmp(script_root, __script_root)) { - free(__script_root); - closedir(scripts_dir); - scnprintf(script_path, MAXPATHLEN, "%s/%s", - lang_path, script_dirent->d_name); - closedir(lang_dir); - return strdup(script_path); - } - free(__script_root); - } - closedir(lang_dir); - } - closedir(scripts_dir); - - return NULL; -} - -static bool is_top_script(const char *script_path) -{ - return ends_with(script_path, "top") !=3D NULL; -} - -static int has_required_arg(char *script_path) -{ - struct script_desc *desc; - int n_args =3D 0; - char *p; - - desc =3D script_desc__new(NULL); - - if (read_script_info(desc, script_path)) - goto out; - - if (!desc->args) - goto out; - - for (p =3D desc->args; *p; p++) - if (*p =3D=3D '<') - n_args++; -out: - script_desc__delete(desc); - - return n_args; -} - -static int have_cmd(int argc, const char **argv) -{ - char **__argv =3D calloc(argc, sizeof(const char *)); =20 - if (!__argv) { - pr_err("malloc failed\n"); - return -1; - } - - memcpy(__argv, argv, sizeof(const char *) * argc); - argc =3D parse_options(argc, (const char **)__argv, record_options, - NULL, PARSE_OPT_STOP_AT_NON_OPTION); - free(__argv); - - system_wide =3D (argc =3D=3D 0); - - return 0; -} =20 static void script__setup_sample_type(struct perf_script *script) { @@ -4026,17 +4032,13 @@ int cmd_script(int argc, const char **argv) bool show_full_info =3D false; bool header =3D false; bool header_only =3D false; - bool script_started =3D false; bool unsorted_dump =3D false; bool merge_deferred_callchains =3D true; - char *rec_script_path =3D NULL; - char *rep_script_path =3D NULL; struct perf_session *session; struct itrace_synth_opts itrace_synth_opts =3D { .set =3D false, .default_no_sample =3D true, }; - char *script_path =3D NULL; const char *dlfilter_file =3D NULL; const char **__argv; int i, j, err =3D 0; @@ -4057,11 +4059,10 @@ int cmd_script(int argc, const char **argv) list_available_scripts), OPT_CALLBACK_NOOPT(0, "list-dlfilters", NULL, NULL, "list available dlfil= ters", list_available_dlfilters), - OPT_CALLBACK('s', "script", NULL, "name", - "script file name (lang:script name, script name, or *)", - parse_scriptname), - OPT_STRING('g', "gen-script", &generate_script_lang, "lang", - "generate perf-script.xx script in specified language"), + { .type =3D OPTION_CALLBACK, .short_name =3D 's', .long_name =3D "script", + .value =3D NULL, .argh =3D "name", + .help =3D "script file name (lang:script name, script name, or *)", + .callback =3D parse_scriptname, .flags =3D PARSE_OPT_HIDDEN }, OPT_STRING(0, "dlfilter", &dlfilter_file, "file", "filter .so file name"), OPT_CALLBACK(0, "dlarg", NULL, "argument", "filter argument", add_dlarg), @@ -4185,22 +4186,17 @@ int cmd_script(int argc, const char **argv) OPTS_EVSWITCH(&script.evswitch), OPT_END() }; - const char * const script_subcommands[] =3D { "record", "report", NULL }; const char *script_usage[] =3D { "perf script []", - "perf script [] record