Adrian Hunter | 718c602 | 2015-04-09 18:53:42 +0300 | [diff] [blame] | 1 | /* |
| 2 | * auxtrace.c: AUX area trace support |
| 3 | * Copyright (c) 2013-2015, Intel Corporation. |
| 4 | * |
| 5 | * This program is free software; you can redistribute it and/or modify it |
| 6 | * under the terms and conditions of the GNU General Public License, |
| 7 | * version 2, as published by the Free Software Foundation. |
| 8 | * |
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT |
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 12 | * more details. |
| 13 | * |
| 14 | */ |
| 15 | |
| 16 | #include <sys/types.h> |
| 17 | #include <sys/mman.h> |
| 18 | #include <stdbool.h> |
| 19 | |
| 20 | #include <linux/kernel.h> |
| 21 | #include <linux/perf_event.h> |
| 22 | #include <linux/types.h> |
| 23 | #include <linux/bitops.h> |
| 24 | #include <linux/log2.h> |
| 25 | |
Adrian Hunter | 9e0cc4f | 2015-04-09 18:53:44 +0300 | [diff] [blame] | 26 | #include <stdlib.h> |
| 27 | #include <string.h> |
| 28 | #include <errno.h> |
| 29 | |
Adrian Hunter | 718c602 | 2015-04-09 18:53:42 +0300 | [diff] [blame] | 30 | #include "../perf.h" |
| 31 | #include "util.h" |
| 32 | #include "evlist.h" |
| 33 | #include "cpumap.h" |
| 34 | #include "thread_map.h" |
| 35 | #include "asm/bug.h" |
| 36 | #include "auxtrace.h" |
| 37 | |
Adrian Hunter | 9e0cc4f | 2015-04-09 18:53:44 +0300 | [diff] [blame] | 38 | #include "event.h" |
| 39 | #include "debug.h" |
Adrian Hunter | f6986c95 | 2015-04-09 18:53:49 +0300 | [diff] [blame^] | 40 | #include "parse-options.h" |
Adrian Hunter | 9e0cc4f | 2015-04-09 18:53:44 +0300 | [diff] [blame] | 41 | |
Adrian Hunter | 718c602 | 2015-04-09 18:53:42 +0300 | [diff] [blame] | 42 | int auxtrace_mmap__mmap(struct auxtrace_mmap *mm, |
| 43 | struct auxtrace_mmap_params *mp, |
| 44 | void *userpg, int fd) |
| 45 | { |
| 46 | struct perf_event_mmap_page *pc = userpg; |
| 47 | |
| 48 | #if BITS_PER_LONG != 64 && !defined(HAVE_SYNC_COMPARE_AND_SWAP_SUPPORT) |
| 49 | pr_err("Cannot use AUX area tracing mmaps\n"); |
| 50 | return -1; |
| 51 | #endif |
| 52 | |
| 53 | WARN_ONCE(mm->base, "Uninitialized auxtrace_mmap\n"); |
| 54 | |
| 55 | mm->userpg = userpg; |
| 56 | mm->mask = mp->mask; |
| 57 | mm->len = mp->len; |
| 58 | mm->prev = 0; |
| 59 | mm->idx = mp->idx; |
| 60 | mm->tid = mp->tid; |
| 61 | mm->cpu = mp->cpu; |
| 62 | |
| 63 | if (!mp->len) { |
| 64 | mm->base = NULL; |
| 65 | return 0; |
| 66 | } |
| 67 | |
| 68 | pc->aux_offset = mp->offset; |
| 69 | pc->aux_size = mp->len; |
| 70 | |
| 71 | mm->base = mmap(NULL, mp->len, mp->prot, MAP_SHARED, fd, mp->offset); |
| 72 | if (mm->base == MAP_FAILED) { |
| 73 | pr_debug2("failed to mmap AUX area\n"); |
| 74 | mm->base = NULL; |
| 75 | return -1; |
| 76 | } |
| 77 | |
| 78 | return 0; |
| 79 | } |
| 80 | |
| 81 | void auxtrace_mmap__munmap(struct auxtrace_mmap *mm) |
| 82 | { |
| 83 | if (mm->base) { |
| 84 | munmap(mm->base, mm->len); |
| 85 | mm->base = NULL; |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | void auxtrace_mmap_params__init(struct auxtrace_mmap_params *mp, |
| 90 | off_t auxtrace_offset, |
| 91 | unsigned int auxtrace_pages, |
| 92 | bool auxtrace_overwrite) |
| 93 | { |
| 94 | if (auxtrace_pages) { |
| 95 | mp->offset = auxtrace_offset; |
| 96 | mp->len = auxtrace_pages * (size_t)page_size; |
| 97 | mp->mask = is_power_of_2(mp->len) ? mp->len - 1 : 0; |
| 98 | mp->prot = PROT_READ | (auxtrace_overwrite ? 0 : PROT_WRITE); |
| 99 | pr_debug2("AUX area mmap length %zu\n", mp->len); |
| 100 | } else { |
| 101 | mp->len = 0; |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp, |
| 106 | struct perf_evlist *evlist, int idx, |
| 107 | bool per_cpu) |
| 108 | { |
| 109 | mp->idx = idx; |
| 110 | |
| 111 | if (per_cpu) { |
| 112 | mp->cpu = evlist->cpus->map[idx]; |
| 113 | if (evlist->threads) |
| 114 | mp->tid = evlist->threads->map[0]; |
| 115 | else |
| 116 | mp->tid = -1; |
| 117 | } else { |
| 118 | mp->cpu = -1; |
| 119 | mp->tid = evlist->threads->map[idx]; |
| 120 | } |
| 121 | } |
Adrian Hunter | 9e0cc4f | 2015-04-09 18:53:44 +0300 | [diff] [blame] | 122 | |
| 123 | size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr) |
| 124 | { |
| 125 | if (itr) |
| 126 | return itr->info_priv_size(itr); |
| 127 | return 0; |
| 128 | } |
| 129 | |
| 130 | static int auxtrace_not_supported(void) |
| 131 | { |
| 132 | pr_err("AUX area tracing is not supported on this architecture\n"); |
| 133 | return -EINVAL; |
| 134 | } |
| 135 | |
| 136 | int auxtrace_record__info_fill(struct auxtrace_record *itr, |
| 137 | struct perf_session *session, |
| 138 | struct auxtrace_info_event *auxtrace_info, |
| 139 | size_t priv_size) |
| 140 | { |
| 141 | if (itr) |
| 142 | return itr->info_fill(itr, session, auxtrace_info, priv_size); |
| 143 | return auxtrace_not_supported(); |
| 144 | } |
| 145 | |
| 146 | void auxtrace_record__free(struct auxtrace_record *itr) |
| 147 | { |
| 148 | if (itr) |
| 149 | itr->free(itr); |
| 150 | } |
| 151 | |
| 152 | int auxtrace_record__options(struct auxtrace_record *itr, |
| 153 | struct perf_evlist *evlist, |
| 154 | struct record_opts *opts) |
| 155 | { |
| 156 | if (itr) |
| 157 | return itr->recording_options(itr, evlist, opts); |
| 158 | return 0; |
| 159 | } |
| 160 | |
| 161 | u64 auxtrace_record__reference(struct auxtrace_record *itr) |
| 162 | { |
| 163 | if (itr) |
| 164 | return itr->reference(itr); |
| 165 | return 0; |
| 166 | } |
| 167 | |
| 168 | struct auxtrace_record *__weak |
| 169 | auxtrace_record__init(struct perf_evlist *evlist __maybe_unused, int *err) |
| 170 | { |
| 171 | *err = 0; |
| 172 | return NULL; |
| 173 | } |
| 174 | |
| 175 | int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, |
| 176 | struct perf_tool *tool, |
| 177 | struct perf_session *session, |
| 178 | perf_event__handler_t process) |
| 179 | { |
| 180 | union perf_event *ev; |
| 181 | size_t priv_size; |
| 182 | int err; |
| 183 | |
| 184 | pr_debug2("Synthesizing auxtrace information\n"); |
| 185 | priv_size = auxtrace_record__info_priv_size(itr); |
| 186 | ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size); |
| 187 | if (!ev) |
| 188 | return -ENOMEM; |
| 189 | |
| 190 | ev->auxtrace_info.header.type = PERF_RECORD_AUXTRACE_INFO; |
| 191 | ev->auxtrace_info.header.size = sizeof(struct auxtrace_info_event) + |
| 192 | priv_size; |
| 193 | err = auxtrace_record__info_fill(itr, session, &ev->auxtrace_info, |
| 194 | priv_size); |
| 195 | if (err) |
| 196 | goto out_free; |
| 197 | |
| 198 | err = process(tool, ev, NULL, NULL); |
| 199 | out_free: |
| 200 | free(ev); |
| 201 | return err; |
| 202 | } |
| 203 | |
Adrian Hunter | f6986c95 | 2015-04-09 18:53:49 +0300 | [diff] [blame^] | 204 | #define PERF_ITRACE_DEFAULT_PERIOD_TYPE PERF_ITRACE_PERIOD_NANOSECS |
| 205 | #define PERF_ITRACE_DEFAULT_PERIOD 100000 |
| 206 | #define PERF_ITRACE_DEFAULT_CALLCHAIN_SZ 16 |
| 207 | #define PERF_ITRACE_MAX_CALLCHAIN_SZ 1024 |
| 208 | |
| 209 | void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts) |
| 210 | { |
| 211 | synth_opts->instructions = true; |
| 212 | synth_opts->branches = true; |
| 213 | synth_opts->errors = true; |
| 214 | synth_opts->period_type = PERF_ITRACE_DEFAULT_PERIOD_TYPE; |
| 215 | synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD; |
| 216 | synth_opts->callchain_sz = PERF_ITRACE_DEFAULT_CALLCHAIN_SZ; |
| 217 | } |
| 218 | |
| 219 | /* |
| 220 | * Please check tools/perf/Documentation/perf-script.txt for information |
| 221 | * about the options parsed here, which is introduced after this cset, |
| 222 | * when support in 'perf script' for these options is introduced. |
| 223 | */ |
| 224 | int itrace_parse_synth_opts(const struct option *opt, const char *str, |
| 225 | int unset) |
| 226 | { |
| 227 | struct itrace_synth_opts *synth_opts = opt->value; |
| 228 | const char *p; |
| 229 | char *endptr; |
| 230 | |
| 231 | synth_opts->set = true; |
| 232 | |
| 233 | if (unset) { |
| 234 | synth_opts->dont_decode = true; |
| 235 | return 0; |
| 236 | } |
| 237 | |
| 238 | if (!str) { |
| 239 | itrace_synth_opts__set_default(synth_opts); |
| 240 | return 0; |
| 241 | } |
| 242 | |
| 243 | for (p = str; *p;) { |
| 244 | switch (*p++) { |
| 245 | case 'i': |
| 246 | synth_opts->instructions = true; |
| 247 | while (*p == ' ' || *p == ',') |
| 248 | p += 1; |
| 249 | if (isdigit(*p)) { |
| 250 | synth_opts->period = strtoull(p, &endptr, 10); |
| 251 | p = endptr; |
| 252 | while (*p == ' ' || *p == ',') |
| 253 | p += 1; |
| 254 | switch (*p++) { |
| 255 | case 'i': |
| 256 | synth_opts->period_type = |
| 257 | PERF_ITRACE_PERIOD_INSTRUCTIONS; |
| 258 | break; |
| 259 | case 't': |
| 260 | synth_opts->period_type = |
| 261 | PERF_ITRACE_PERIOD_TICKS; |
| 262 | break; |
| 263 | case 'm': |
| 264 | synth_opts->period *= 1000; |
| 265 | /* Fall through */ |
| 266 | case 'u': |
| 267 | synth_opts->period *= 1000; |
| 268 | /* Fall through */ |
| 269 | case 'n': |
| 270 | if (*p++ != 's') |
| 271 | goto out_err; |
| 272 | synth_opts->period_type = |
| 273 | PERF_ITRACE_PERIOD_NANOSECS; |
| 274 | break; |
| 275 | case '\0': |
| 276 | goto out; |
| 277 | default: |
| 278 | goto out_err; |
| 279 | } |
| 280 | } |
| 281 | break; |
| 282 | case 'b': |
| 283 | synth_opts->branches = true; |
| 284 | break; |
| 285 | case 'e': |
| 286 | synth_opts->errors = true; |
| 287 | break; |
| 288 | case 'd': |
| 289 | synth_opts->log = true; |
| 290 | break; |
| 291 | case 'c': |
| 292 | synth_opts->branches = true; |
| 293 | synth_opts->calls = true; |
| 294 | break; |
| 295 | case 'r': |
| 296 | synth_opts->branches = true; |
| 297 | synth_opts->returns = true; |
| 298 | break; |
| 299 | case 'g': |
| 300 | synth_opts->instructions = true; |
| 301 | synth_opts->callchain = true; |
| 302 | synth_opts->callchain_sz = |
| 303 | PERF_ITRACE_DEFAULT_CALLCHAIN_SZ; |
| 304 | while (*p == ' ' || *p == ',') |
| 305 | p += 1; |
| 306 | if (isdigit(*p)) { |
| 307 | unsigned int val; |
| 308 | |
| 309 | val = strtoul(p, &endptr, 10); |
| 310 | p = endptr; |
| 311 | if (!val || val > PERF_ITRACE_MAX_CALLCHAIN_SZ) |
| 312 | goto out_err; |
| 313 | synth_opts->callchain_sz = val; |
| 314 | } |
| 315 | break; |
| 316 | case ' ': |
| 317 | case ',': |
| 318 | break; |
| 319 | default: |
| 320 | goto out_err; |
| 321 | } |
| 322 | } |
| 323 | out: |
| 324 | if (synth_opts->instructions) { |
| 325 | if (!synth_opts->period_type) |
| 326 | synth_opts->period_type = |
| 327 | PERF_ITRACE_DEFAULT_PERIOD_TYPE; |
| 328 | if (!synth_opts->period) |
| 329 | synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD; |
| 330 | } |
| 331 | |
| 332 | return 0; |
| 333 | |
| 334 | out_err: |
| 335 | pr_err("Bad Instruction Tracing options '%s'\n", str); |
| 336 | return -EINVAL; |
| 337 | } |
| 338 | |
Adrian Hunter | 9e0cc4f | 2015-04-09 18:53:44 +0300 | [diff] [blame] | 339 | int auxtrace_mmap__read(struct auxtrace_mmap *mm, struct auxtrace_record *itr, |
| 340 | struct perf_tool *tool, process_auxtrace_t fn) |
| 341 | { |
| 342 | u64 head = auxtrace_mmap__read_head(mm); |
| 343 | u64 old = mm->prev, offset, ref; |
| 344 | unsigned char *data = mm->base; |
| 345 | size_t size, head_off, old_off, len1, len2, padding; |
| 346 | union perf_event ev; |
| 347 | void *data1, *data2; |
| 348 | |
| 349 | if (old == head) |
| 350 | return 0; |
| 351 | |
| 352 | pr_debug3("auxtrace idx %d old %#"PRIx64" head %#"PRIx64" diff %#"PRIx64"\n", |
| 353 | mm->idx, old, head, head - old); |
| 354 | |
| 355 | if (mm->mask) { |
| 356 | head_off = head & mm->mask; |
| 357 | old_off = old & mm->mask; |
| 358 | } else { |
| 359 | head_off = head % mm->len; |
| 360 | old_off = old % mm->len; |
| 361 | } |
| 362 | |
| 363 | if (head_off > old_off) |
| 364 | size = head_off - old_off; |
| 365 | else |
| 366 | size = mm->len - (old_off - head_off); |
| 367 | |
| 368 | ref = auxtrace_record__reference(itr); |
| 369 | |
| 370 | if (head > old || size <= head || mm->mask) { |
| 371 | offset = head - size; |
| 372 | } else { |
| 373 | /* |
| 374 | * When the buffer size is not a power of 2, 'head' wraps at the |
| 375 | * highest multiple of the buffer size, so we have to subtract |
| 376 | * the remainder here. |
| 377 | */ |
| 378 | u64 rem = (0ULL - mm->len) % mm->len; |
| 379 | |
| 380 | offset = head - size - rem; |
| 381 | } |
| 382 | |
| 383 | if (size > head_off) { |
| 384 | len1 = size - head_off; |
| 385 | data1 = &data[mm->len - len1]; |
| 386 | len2 = head_off; |
| 387 | data2 = &data[0]; |
| 388 | } else { |
| 389 | len1 = size; |
| 390 | data1 = &data[head_off - len1]; |
| 391 | len2 = 0; |
| 392 | data2 = NULL; |
| 393 | } |
| 394 | |
| 395 | /* padding must be written by fn() e.g. record__process_auxtrace() */ |
| 396 | padding = size & 7; |
| 397 | if (padding) |
| 398 | padding = 8 - padding; |
| 399 | |
| 400 | memset(&ev, 0, sizeof(ev)); |
| 401 | ev.auxtrace.header.type = PERF_RECORD_AUXTRACE; |
| 402 | ev.auxtrace.header.size = sizeof(ev.auxtrace); |
| 403 | ev.auxtrace.size = size + padding; |
| 404 | ev.auxtrace.offset = offset; |
| 405 | ev.auxtrace.reference = ref; |
| 406 | ev.auxtrace.idx = mm->idx; |
| 407 | ev.auxtrace.tid = mm->tid; |
| 408 | ev.auxtrace.cpu = mm->cpu; |
| 409 | |
| 410 | if (fn(tool, &ev, data1, len1, data2, len2)) |
| 411 | return -1; |
| 412 | |
| 413 | mm->prev = head; |
| 414 | |
| 415 | auxtrace_mmap__write_tail(mm, head); |
| 416 | if (itr->read_finish) { |
| 417 | int err; |
| 418 | |
| 419 | err = itr->read_finish(itr, mm->idx); |
| 420 | if (err < 0) |
| 421 | return err; |
| 422 | } |
| 423 | |
| 424 | return 1; |
| 425 | } |