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) | ||
