Commit bd741d80 authored by Ian Rogers's avatar Ian Rogers Committed by Namhyung Kim
Browse files

perf parse-events: Allow the cpu term to be a PMU or CPU range



On hybrid systems, events like msr/tsc/ will aggregate counts across
all CPUs. Often metrics only want a value like msr/tsc/ for the cores
on which the metric is being computed. Listing each CPU with terms
cpu=0,cpu=1.. is laborious and would need to be encoded for all
variations of a CPU model.

Allow the cpumask from a PMU to be an argument to the cpu term. For
example in the following the cpumask of the cstate_pkg PMU selects the
CPUs to count msr/tsc/ counter upon:
```
$ cat /sys/bus/event_source/devices/cstate_pkg/cpumask
0
$ perf stat -A -e 'msr/tsc,cpu=cstate_pkg/' -a sleep 0.1

 Performance counter stats for 'system wide':

CPU0          252,621,253      msr/tsc,cpu=cstate_pkg/

       0.101184092 seconds time elapsed
```

As the cpu term is now also allowed to be a string, allow it to encode
a range of CPUs (a list can't be supported as ',' is already a special
token).

The "event qualifiers" section of the `perf list` man page is updated
to detail the additional behavior.  The man page formatting is tidied
up in this section, as it was incorrectly appearing within the
"parameterized events" section.

Reviewed-by: default avatarThomas Falcon <thomas.falcon@intel.com>
Signed-off-by: default avatarIan Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250719030517.1990983-5-irogers@google.com


Signed-off-by: default avatarNamhyung Kim <namhyung@kernel.org>
parent ced4c249
Loading
Loading
Loading
Loading
+16 −9
Original line number Diff line number Diff line
@@ -278,26 +278,33 @@ also be supplied. For example:

  perf stat -C 0 -e 'hv_gpci/dtbp_ptitc,phys_processor_idx=0x2/' ...

EVENT QUALIFIERS:
EVENT QUALIFIERS
----------------

It is also possible to add extra qualifiers to an event:

percore:

  Sums up the event counts for all hardware threads in a core, e.g.:


    perf stat -e cpu/event=0,umask=0x3,percore=1/

cpu:

Specifies the CPU to open the event upon. The value may be repeated to
specify opening the event on multiple CPUs:
  Specifies a CPU or a range of CPUs to open the event upon. It may
  also reference a PMU to copy the CPU mask from. The value may be
  repeated to specify opening the event on multiple CPUs.

  Example 1: to open the instructions event on CPUs 0 and 2, the
  cycles event on CPUs 1 and 2:
    perf stat -e instructions/cpu=0,cpu=2/,cycles/cpu=1-2/ -a sleep 1

  perf stat -e instructions/cpu=0,cpu=2/,cycles/cpu=1,cpu=2/ -a sleep 1
  Example 2: to open the data_read uncore event on CPU 0 and the
  data_write uncore event on CPU 1:
    perf stat -e data_read/cpu=0/,data_write/cpu=1/ -a sleep 1

  Example 3: to open the software msr/tsc/ event only on the CPUs
  matching those from the cpu_core PMU:
    perf stat -e msr/tsc,cpu=cpu_core/ -a sleep 1

EVENT GROUPS
------------
+38 −9
Original line number Diff line number Diff line
@@ -187,10 +187,22 @@ static struct perf_cpu_map *get_config_cpu(const struct parse_events_terms *head

	list_for_each_entry(term, &head_terms->terms, list) {
		if (term->type_term == PARSE_EVENTS__TERM_TYPE_CPU) {
			struct perf_cpu_map *cpu = perf_cpu_map__new_int(term->val.num);
			struct perf_cpu_map *term_cpus;

			perf_cpu_map__merge(&cpus, cpu);
			perf_cpu_map__put(cpu);
			if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) {
				term_cpus = perf_cpu_map__new_int(term->val.num);
			} else {
				struct perf_pmu *pmu = perf_pmus__find(term->val.str);

				if (pmu && perf_cpu_map__is_empty(pmu->cpus))
					term_cpus = pmu->is_core ? cpu_map__online() : NULL;
				else if (pmu)
					term_cpus = perf_cpu_map__get(pmu->cpus);
				else
					term_cpus = perf_cpu_map__new(term->val.str);
			}
			perf_cpu_map__merge(&cpus, term_cpus);
			perf_cpu_map__put(term_cpus);
		}
	}

@@ -1048,15 +1060,32 @@ do { \
			return -EINVAL;
		}
		break;
	case PARSE_EVENTS__TERM_TYPE_CPU:
		CHECK_TYPE_VAL(NUM);
	case PARSE_EVENTS__TERM_TYPE_CPU: {
		struct perf_cpu_map *map;

		if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) {
			if (term->val.num >= (u64)cpu__max_present_cpu().cpu) {
				parse_events_error__handle(err, term->err_val,
							strdup("too big"),
						NULL);
							/*help=*/NULL);
				return -EINVAL;
			}
			break;
		}
		assert(term->type_val == PARSE_EVENTS__TERM_TYPE_STR);
		if (perf_pmus__find(term->val.str) != NULL)
			break;

		map = perf_cpu_map__new(term->val.str);
		if (!map) {
			parse_events_error__handle(err, term->err_val,
						   strdup("not a valid PMU or CPU number"),
						   /*help=*/NULL);
			return -EINVAL;
		}
		perf_cpu_map__put(map);
		break;
	}
	case PARSE_EVENTS__TERM_TYPE_DRV_CFG:
	case PARSE_EVENTS__TERM_TYPE_USER:
	case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE: