Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf jevents: Add a new expression builtin strcmp_cpuid_str()

This will allow writing formulas that are conditional on a specific
CPU type or CPU version. It calls through to the existing
strcmp_cpuid_str() function in Perf which has a default weak version,
and an arch specific version for x86 and arm64.

The function takes an 'ID' type value, which is a string. But in this
case Arm CPU IDs are hex numbers prefixed with '0x'. metric.py
assumes strings are only used by event names, and that they can't start
with a number ('0'), so an additional change has to be made to the
regex to convert hex numbers back to 'ID' types.

Signed-off-by: James Clark <james.clark@arm.com>
Reviewed-by: John Garry <john.g.garry@oracle.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Haixin Yu <yuhaixin.yhx@linux.alibaba.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jing Zhang <renyu.zj@linux.alibaba.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kajol Jain <kjain@linux.ibm.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mike Leach <mike.leach@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Nick Forrington <nick.forrington@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rob Herring <robh@kernel.org>
Cc: Sohom Datta <sohomdatta1@gmail.com>
Cc: Will Deacon <will@kernel.org>
Cc: linux-arm-kernel@lists.infradead.org
Link: https://lore.kernel.org/r/20230816114841.1679234-5-james.clark@arm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

James Clark and committed by
Arnaldo Carvalho de Melo
9d5da30e 81f7da54

+61 -20
+1 -17
tools/perf/arch/arm64/util/pmu.c
··· 2 2 3 3 #include <internal/cpumap.h> 4 4 #include "../../../util/cpumap.h" 5 + #include "../../../util/header.h" 5 6 #include "../../../util/pmu.h" 6 7 #include "../../../util/pmus.h" 7 8 #include <api/fs/fs.h> 8 9 #include <math.h> 9 - 10 - static struct perf_pmu *pmu__find_core_pmu(void) 11 - { 12 - struct perf_pmu *pmu = NULL; 13 - 14 - while ((pmu = perf_pmus__scan_core(pmu))) { 15 - /* 16 - * The cpumap should cover all CPUs. Otherwise, some CPUs may 17 - * not support some events or have different event IDs. 18 - */ 19 - if (RC_CHK_ACCESS(pmu->cpus)->nr != cpu__max_cpu().cpu) 20 - return NULL; 21 - 22 - return pmu; 23 - } 24 - return NULL; 25 - } 26 10 27 11 const struct pmu_metrics_table *pmu_metrics_table__find(void) 28 12 {
+15 -2
tools/perf/pmu-events/metric.py
··· 413 413 # pylint: disable=invalid-name 414 414 return Function('has_event', event) 415 415 416 + def strcmp_cpuid_str(event: str) -> Function: 417 + # pylint: disable=redefined-builtin 418 + # pylint: disable=invalid-name 419 + return Function('strcmp_cpuid_str', event) 416 420 417 421 class Metric: 418 422 """An individual metric that will specifiable on the perf command line.""" ··· 545 541 """ 546 542 # pylint: disable=eval-used 547 543 py = orig.strip() 544 + # First try to convert everything that looks like a string (event name) into Event(r"EVENT_NAME"). 545 + # This isn't very selective so is followed up by converting some unwanted conversions back again 548 546 py = re.sub(r'([a-zA-Z][^-+/\* \\\(\),]*(?:\\.[^-+/\* \\\(\),]*)*)', 549 547 r'Event(r"\1")', py) 548 + # If it started with a # it should have been a literal, rather than an event name 550 549 py = re.sub(r'#Event\(r"([^"]*)"\)', r'Literal("#\1")', py) 550 + # Convert accidentally converted hex constants ("0Event(r"xDEADBEEF)"") back to a constant, 551 + # but keep it wrapped in Event(), otherwise Python drops the 0x prefix and it gets interpreted as 552 + # a double by the Bison parser 553 + py = re.sub(r'0Event\(r"[xX]([0-9a-fA-F]*)"\)', r'Event("0x\1")', py) 554 + # Convert accidentally converted scientific notation constants back 551 555 py = re.sub(r'([0-9]+)Event\(r"(e[0-9]+)"\)', r'\1\2', py) 552 - keywords = ['if', 'else', 'min', 'max', 'd_ratio', 'source_count', 'has_event'] 556 + # Convert all the known keywords back from events to just the keyword 557 + keywords = ['if', 'else', 'min', 'max', 'd_ratio', 'source_count', 'has_event', 'strcmp_cpuid_str', 558 + 'cpuid_not_more_than'] 553 559 for kw in keywords: 554 560 py = re.sub(rf'Event\(r"{kw}"\)', kw, py) 555 - 556 561 try: 557 562 parsed = ast.parse(py, mode='eval') 558 563 except SyntaxError as e:
+18
tools/perf/util/expr.c
··· 13 13 #include <util/expr-bison.h> 14 14 #include <util/expr-flex.h> 15 15 #include "util/hashmap.h" 16 + #include "util/header.h" 17 + #include "util/pmu.h" 16 18 #include "smt.h" 17 19 #include "tsc.h" 18 20 #include <api/fs/fs.h> ··· 495 493 return NAN; 496 494 ret = parse_event(tmp, id) ? 0 : 1; 497 495 evlist__delete(tmp); 496 + return ret; 497 + } 498 + 499 + double expr__strcmp_cpuid_str(const struct expr_parse_ctx *ctx __maybe_unused, 500 + bool compute_ids __maybe_unused, const char *test_id) 501 + { 502 + double ret; 503 + struct perf_pmu *pmu = pmu__find_core_pmu(); 504 + char *cpuid = perf_pmu__getcpuid(pmu); 505 + 506 + if (!cpuid) 507 + return NAN; 508 + 509 + ret = !strcmp_cpuid_str(test_id, cpuid); 510 + 511 + free(cpuid); 498 512 return ret; 499 513 }
+1
tools/perf/util/expr.h
··· 55 55 double expr_id_data__source_count(const struct expr_id_data *data); 56 56 double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx); 57 57 double expr__has_event(const struct expr_parse_ctx *ctx, bool compute_ids, const char *id); 58 + double expr__strcmp_cpuid_str(const struct expr_parse_ctx *ctx, bool compute_ids, const char *id); 58 59 59 60 #endif
+1
tools/perf/util/expr.l
··· 114 114 else { return ELSE; } 115 115 source_count { return SOURCE_COUNT; } 116 116 has_event { return HAS_EVENT; } 117 + strcmp_cpuid_str { return STRCMP_CPUID_STR; } 117 118 {literal} { return literal(yyscanner, sctx); } 118 119 {number} { return value(yyscanner); } 119 120 {symbol} { return str(yyscanner, ID, sctx->runtime); }
+7 -1
tools/perf/util/expr.y
··· 39 39 } ids; 40 40 } 41 41 42 - %token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT HAS_EVENT EXPR_ERROR 42 + %token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT HAS_EVENT STRCMP_CPUID_STR EXPR_ERROR 43 43 %left MIN MAX IF 44 44 %left '|' 45 45 %left '^' ··· 204 204 | HAS_EVENT '(' ID ')' 205 205 { 206 206 $$.val = expr__has_event(ctx, compute_ids, $3); 207 + $$.ids = NULL; 208 + free($3); 209 + } 210 + | STRCMP_CPUID_STR '(' ID ')' 211 + { 212 + $$.val = expr__strcmp_cpuid_str(ctx, compute_ids, $3); 207 213 $$.ids = NULL; 208 214 free($3); 209 215 }
+17
tools/perf/util/pmu.c
··· 1790 1790 zfree(&pmu->alias_name); 1791 1791 free(pmu); 1792 1792 } 1793 + 1794 + struct perf_pmu *pmu__find_core_pmu(void) 1795 + { 1796 + struct perf_pmu *pmu = NULL; 1797 + 1798 + while ((pmu = perf_pmus__scan_core(pmu))) { 1799 + /* 1800 + * The cpumap should cover all CPUs. Otherwise, some CPUs may 1801 + * not support some events or have different event IDs. 1802 + */ 1803 + if (RC_CHK_ACCESS(pmu->cpus)->nr != cpu__max_cpu().cpu) 1804 + return NULL; 1805 + 1806 + return pmu; 1807 + } 1808 + return NULL; 1809 + }
+1
tools/perf/util/pmu.h
··· 289 289 struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char *lookup_name); 290 290 struct perf_pmu *perf_pmu__create_placeholder_core_pmu(struct list_head *core_pmus); 291 291 void perf_pmu__delete(struct perf_pmu *pmu); 292 + struct perf_pmu *pmu__find_core_pmu(void); 292 293 293 294 #endif /* __PMU_H */