diff options
| author | Adrian Hunter <adrian.hunter@intel.com> | 2015-07-17 12:33:52 -0400 |
|---|---|---|
| committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-08-24 16:45:08 -0400 |
| commit | bc9b6bf07c8b3f4e85509f9b3a552c86e567b4ae (patch) | |
| tree | f0a184f97273fcb9bb333c8c6a6a7892d9b4a3db | |
| parent | 2a21d03686881331b0af0471588674e7e896eeb2 (diff) | |
perf tools: Add Intel PT support for PSB periods
The PSB packet is a synchronization packet that provides a starting
point for decoding or recovery from errors.
This patch adds support for a new Intel PT feature that allows the
frequency of PSB packets to be specified.
Support for this feature is indicated by
/sys/bus/event_source/devices/intel_pt/caps/psb_cyc which contains "1"
if the feature is supported and "0" otherwise.
The PSB period can be specified as a PMU config term e.g. perf record -e
intel_pt/psb_period=2/u sleep 1
The default value is 3 or the nearest lower value that is supported. 0
is always supported.
Valid values are given by:
/sys/bus/event_source/devices/intel_pt/caps/psb_periods
which contains a hexadecimal value, the bits of which represent valid
values e.g. bit 2 set means value 2 is valid.
The value is converted to the approximate number of trace bytes between
PSB packets as:
2 ^ (value + 11)
e.g. value 3 means 16KiB bytes between PSBs
If an invalid value is entered, the error message will give a list of
valid values e.g.
$ perf record -e intel_pt/psb_period=15/u uname
Invalid psb_period for intel_pt. Valid values are: 0-5
tools/perf/Documentation/intel-pt.txt is updated in a later patch as
there are a number of new features being added.
For more information about PSB periods refer to the Intel 64 and IA-32
Architectures SDM Chapter 36 Intel Processor Trace from June 2015 or
later.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Link: http://lkml.kernel.org/r/1437150840-31811-18-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
| -rw-r--r-- | tools/perf/arch/x86/util/intel-pt.c | 217 |
1 files changed, 210 insertions, 7 deletions
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index da7d2c15e611..145975b003a7 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c | |||
| @@ -99,17 +99,121 @@ static int intel_pt_parse_terms(struct list_head *formats, const char *str, | |||
| 99 | return intel_pt_parse_terms_with_default(formats, str, config); | 99 | return intel_pt_parse_terms_with_default(formats, str, config); |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | static size_t intel_pt_psb_period(struct perf_pmu *intel_pt_pmu __maybe_unused, | 102 | static u64 intel_pt_masked_bits(u64 mask, u64 bits) |
| 103 | struct perf_evlist *evlist __maybe_unused) | ||
| 104 | { | 103 | { |
| 105 | return 256; | 104 | const u64 top_bit = 1ULL << 63; |
| 105 | u64 res = 0; | ||
| 106 | int i; | ||
| 107 | |||
| 108 | for (i = 0; i < 64; i++) { | ||
| 109 | if (mask & top_bit) { | ||
| 110 | res <<= 1; | ||
| 111 | if (bits & top_bit) | ||
| 112 | res |= 1; | ||
| 113 | } | ||
| 114 | mask <<= 1; | ||
| 115 | bits <<= 1; | ||
| 116 | } | ||
| 117 | |||
| 118 | return res; | ||
| 119 | } | ||
| 120 | |||
| 121 | static int intel_pt_read_config(struct perf_pmu *intel_pt_pmu, const char *str, | ||
| 122 | struct perf_evlist *evlist, u64 *res) | ||
| 123 | { | ||
| 124 | struct perf_evsel *evsel; | ||
| 125 | u64 mask; | ||
| 126 | |||
| 127 | *res = 0; | ||
| 128 | |||
| 129 | mask = perf_pmu__format_bits(&intel_pt_pmu->format, str); | ||
| 130 | if (!mask) | ||
| 131 | return -EINVAL; | ||
| 132 | |||
| 133 | evlist__for_each(evlist, evsel) { | ||
| 134 | if (evsel->attr.type == intel_pt_pmu->type) { | ||
| 135 | *res = intel_pt_masked_bits(mask, evsel->attr.config); | ||
| 136 | return 0; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | return -EINVAL; | ||
| 141 | } | ||
| 142 | |||
| 143 | static size_t intel_pt_psb_period(struct perf_pmu *intel_pt_pmu, | ||
| 144 | struct perf_evlist *evlist) | ||
| 145 | { | ||
| 146 | u64 val; | ||
| 147 | int err, topa_multiple_entries; | ||
| 148 | size_t psb_period; | ||
| 149 | |||
| 150 | if (perf_pmu__scan_file(intel_pt_pmu, "caps/topa_multiple_entries", | ||
| 151 | "%d", &topa_multiple_entries) != 1) | ||
| 152 | topa_multiple_entries = 0; | ||
| 153 | |||
| 154 | /* | ||
| 155 | * Use caps/topa_multiple_entries to indicate early hardware that had | ||
| 156 | * extra frequent PSBs. | ||
| 157 | */ | ||
| 158 | if (!topa_multiple_entries) { | ||
| 159 | psb_period = 256; | ||
| 160 | goto out; | ||
| 161 | } | ||
| 162 | |||
| 163 | err = intel_pt_read_config(intel_pt_pmu, "psb_period", evlist, &val); | ||
| 164 | if (err) | ||
| 165 | val = 0; | ||
| 166 | |||
| 167 | psb_period = 1 << (val + 11); | ||
| 168 | out: | ||
| 169 | pr_debug2("%s psb_period %zu\n", intel_pt_pmu->name, psb_period); | ||
| 170 | return psb_period; | ||
| 171 | } | ||
| 172 | |||
| 173 | static int intel_pt_pick_bit(int bits, int target) | ||
| 174 | { | ||
| 175 | int pos, pick = -1; | ||
| 176 | |||
| 177 | for (pos = 0; bits; bits >>= 1, pos++) { | ||
| 178 | if (bits & 1) { | ||
| 179 | if (pos <= target || pick < 0) | ||
| 180 | pick = pos; | ||
| 181 | if (pos >= target) | ||
| 182 | break; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | return pick; | ||
| 106 | } | 187 | } |
| 107 | 188 | ||
| 108 | static u64 intel_pt_default_config(struct perf_pmu *intel_pt_pmu) | 189 | static u64 intel_pt_default_config(struct perf_pmu *intel_pt_pmu) |
| 109 | { | 190 | { |
| 191 | char buf[256]; | ||
| 192 | int psb_cyc, psb_periods, psb_period; | ||
| 193 | int pos = 0; | ||
| 110 | u64 config; | 194 | u64 config; |
| 111 | 195 | ||
| 112 | intel_pt_parse_terms(&intel_pt_pmu->format, "tsc", &config); | 196 | pos += scnprintf(buf + pos, sizeof(buf) - pos, "tsc"); |
| 197 | |||
| 198 | if (perf_pmu__scan_file(intel_pt_pmu, "caps/psb_cyc", "%d", | ||
| 199 | &psb_cyc) != 1) | ||
| 200 | psb_cyc = 1; | ||
| 201 | |||
| 202 | if (psb_cyc) { | ||
| 203 | if (perf_pmu__scan_file(intel_pt_pmu, "caps/psb_periods", "%x", | ||
| 204 | &psb_periods) != 1) | ||
| 205 | psb_periods = 0; | ||
| 206 | if (psb_periods) { | ||
| 207 | psb_period = intel_pt_pick_bit(psb_periods, 3); | ||
| 208 | pos += scnprintf(buf + pos, sizeof(buf) - pos, | ||
| 209 | ",psb_period=%d", psb_period); | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | pr_debug2("%s default config: %s\n", intel_pt_pmu->name, buf); | ||
| 214 | |||
| 215 | intel_pt_parse_terms(&intel_pt_pmu->format, buf, &config); | ||
| 216 | |||
| 113 | return config; | 217 | return config; |
| 114 | } | 218 | } |
| 115 | 219 | ||
| @@ -239,6 +343,103 @@ static int intel_pt_track_switches(struct perf_evlist *evlist) | |||
| 239 | return 0; | 343 | return 0; |
| 240 | } | 344 | } |
| 241 | 345 | ||
| 346 | static void intel_pt_valid_str(char *str, size_t len, u64 valid) | ||
| 347 | { | ||
| 348 | unsigned int val, last = 0, state = 1; | ||
| 349 | int p = 0; | ||
| 350 | |||
| 351 | str[0] = '\0'; | ||
| 352 | |||
| 353 | for (val = 0; val <= 64; val++, valid >>= 1) { | ||
| 354 | if (valid & 1) { | ||
| 355 | last = val; | ||
| 356 | switch (state) { | ||
| 357 | case 0: | ||
| 358 | p += scnprintf(str + p, len - p, ","); | ||
| 359 | /* Fall through */ | ||
| 360 | case 1: | ||
| 361 | p += scnprintf(str + p, len - p, "%u", val); | ||
| 362 | state = 2; | ||
| 363 | break; | ||
| 364 | case 2: | ||
| 365 | state = 3; | ||
| 366 | break; | ||
| 367 | case 3: | ||
| 368 | state = 4; | ||
| 369 | break; | ||
| 370 | default: | ||
| 371 | break; | ||
| 372 | } | ||
| 373 | } else { | ||
| 374 | switch (state) { | ||
| 375 | case 3: | ||
| 376 | p += scnprintf(str + p, len - p, ",%u", last); | ||
| 377 | state = 0; | ||
| 378 | break; | ||
| 379 | case 4: | ||
| 380 | p += scnprintf(str + p, len - p, "-%u", last); | ||
| 381 | state = 0; | ||
| 382 | break; | ||
| 383 | default: | ||
| 384 | break; | ||
| 385 | } | ||
| 386 | if (state != 1) | ||
| 387 | state = 0; | ||
| 388 | } | ||
| 389 | } | ||
| 390 | } | ||
| 391 | |||
| 392 | static int intel_pt_val_config_term(struct perf_pmu *intel_pt_pmu, | ||
| 393 | const char *caps, const char *name, | ||
| 394 | const char *supported, u64 config) | ||
| 395 | { | ||
| 396 | char valid_str[256]; | ||
| 397 | unsigned int shift; | ||
| 398 | unsigned long long valid; | ||
| 399 | u64 bits; | ||
| 400 | int ok; | ||
| 401 | |||
| 402 | if (perf_pmu__scan_file(intel_pt_pmu, caps, "%llx", &valid) != 1) | ||
| 403 | valid = 0; | ||
| 404 | |||
| 405 | if (supported && | ||
| 406 | perf_pmu__scan_file(intel_pt_pmu, supported, "%d", &ok) == 1 && !ok) | ||
| 407 | valid = 0; | ||
| 408 | |||
| 409 | valid |= 1; | ||
| 410 | |||
| 411 | bits = perf_pmu__format_bits(&intel_pt_pmu->format, name); | ||
| 412 | |||
| 413 | config &= bits; | ||
| 414 | |||
| 415 | for (shift = 0; bits && !(bits & 1); shift++) | ||
| 416 | bits >>= 1; | ||
| 417 | |||
| 418 | config >>= shift; | ||
| 419 | |||
| 420 | if (config > 63) | ||
| 421 | goto out_err; | ||
| 422 | |||
| 423 | if (valid & (1 << config)) | ||
| 424 | return 0; | ||
| 425 | out_err: | ||
| 426 | intel_pt_valid_str(valid_str, sizeof(valid_str), valid); | ||
| 427 | pr_err("Invalid %s for %s. Valid values are: %s\n", | ||
| 428 | name, INTEL_PT_PMU_NAME, valid_str); | ||
| 429 | return -EINVAL; | ||
| 430 | } | ||
| 431 | |||
| 432 | static int intel_pt_validate_config(struct perf_pmu *intel_pt_pmu, | ||
| 433 | struct perf_evsel *evsel) | ||
| 434 | { | ||
| 435 | if (!evsel) | ||
| 436 | return 0; | ||
| 437 | |||
| 438 | return intel_pt_val_config_term(intel_pt_pmu, "caps/psb_periods", | ||
| 439 | "psb_period", "caps/psb_cyc", | ||
| 440 | evsel->attr.config); | ||
| 441 | } | ||
| 442 | |||
| 242 | static int intel_pt_recording_options(struct auxtrace_record *itr, | 443 | static int intel_pt_recording_options(struct auxtrace_record *itr, |
| 243 | struct perf_evlist *evlist, | 444 | struct perf_evlist *evlist, |
| 244 | struct record_opts *opts) | 445 | struct record_opts *opts) |
| @@ -251,6 +452,7 @@ static int intel_pt_recording_options(struct auxtrace_record *itr, | |||
| 251 | const struct cpu_map *cpus = evlist->cpus; | 452 | const struct cpu_map *cpus = evlist->cpus; |
| 252 | bool privileged = geteuid() == 0 || perf_event_paranoid() < 0; | 453 | bool privileged = geteuid() == 0 || perf_event_paranoid() < 0; |
| 253 | u64 tsc_bit; | 454 | u64 tsc_bit; |
| 455 | int err; | ||
| 254 | 456 | ||
| 255 | ptr->evlist = evlist; | 457 | ptr->evlist = evlist; |
| 256 | ptr->snapshot_mode = opts->auxtrace_snapshot_mode; | 458 | ptr->snapshot_mode = opts->auxtrace_snapshot_mode; |
| @@ -281,6 +483,10 @@ static int intel_pt_recording_options(struct auxtrace_record *itr, | |||
| 281 | if (!opts->full_auxtrace) | 483 | if (!opts->full_auxtrace) |
| 282 | return 0; | 484 | return 0; |
| 283 | 485 | ||
| 486 | err = intel_pt_validate_config(intel_pt_pmu, intel_pt_evsel); | ||
| 487 | if (err) | ||
| 488 | return err; | ||
| 489 | |||
| 284 | /* Set default sizes for snapshot mode */ | 490 | /* Set default sizes for snapshot mode */ |
| 285 | if (opts->auxtrace_snapshot_mode) { | 491 | if (opts->auxtrace_snapshot_mode) { |
| 286 | size_t psb_period = intel_pt_psb_period(intel_pt_pmu, evlist); | 492 | size_t psb_period = intel_pt_psb_period(intel_pt_pmu, evlist); |
| @@ -366,8 +572,6 @@ static int intel_pt_recording_options(struct auxtrace_record *itr, | |||
| 366 | * threads. | 572 | * threads. |
| 367 | */ | 573 | */ |
| 368 | if (have_timing_info && !cpu_map__empty(cpus)) { | 574 | if (have_timing_info && !cpu_map__empty(cpus)) { |
| 369 | int err; | ||
| 370 | |||
| 371 | err = intel_pt_track_switches(evlist); | 575 | err = intel_pt_track_switches(evlist); |
| 372 | if (err == -EPERM) | 576 | if (err == -EPERM) |
| 373 | pr_debug2("Unable to select sched:sched_switch\n"); | 577 | pr_debug2("Unable to select sched:sched_switch\n"); |
| @@ -394,7 +598,6 @@ static int intel_pt_recording_options(struct auxtrace_record *itr, | |||
| 394 | /* Add dummy event to keep tracking */ | 598 | /* Add dummy event to keep tracking */ |
| 395 | if (opts->full_auxtrace) { | 599 | if (opts->full_auxtrace) { |
| 396 | struct perf_evsel *tracking_evsel; | 600 | struct perf_evsel *tracking_evsel; |
| 397 | int err; | ||
| 398 | 601 | ||
| 399 | err = parse_events(evlist, "dummy:u", NULL); | 602 | err = parse_events(evlist, "dummy:u", NULL); |
| 400 | if (err) | 603 | if (err) |
