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