| 1 | |
| 2 | #include "sort.h"
|
| 3 |
|
| 4 | #include <ctype.h>
|
| 5 | #include <errno.h>
|
| 6 | #include <inttypes.h>
|
| 7 | #include <stdlib.h>
|
| 8 |
|
| 9 | #include <elf.h>
|
| 10 | #include <linux/kernel.h>
|
| 11 | #include <linux/mman.h>
|
| 12 | #include <linux/string.h>
|
| 13 | #include <linux/time64.h>
|
| 14 |
|
| 15 | #include <regex.h>
|
| 16 |
|
| 17 | #include "annotate-data.h"
|
| 18 | #include "annotate.h"
|
| 19 | #include "branch.h"
|
| 20 | #include "cacheline.h"
|
| 21 | #include "cgroup.h"
|
| 22 | #include "comm.h"
|
| 23 | #include "debug.h"
|
| 24 | #include "dso.h"
|
| 25 | #include "event.h"
|
| 26 | #include "evlist.h"
|
| 27 | #include "evsel.h"
|
| 28 | #include "hist.h"
|
| 29 | #include "machine.h"
|
| 30 | #include "map.h"
|
| 31 | #include "map_symbol.h"
|
| 32 | #include "maps.h"
|
| 33 | #include "mem-events.h"
|
| 34 | #include "mem-info.h"
|
| 35 | #include "session.h"
|
| 36 | #include "srcline.h"
|
| 37 | #include "strbuf.h"
|
| 38 | #include "strlist.h"
|
| 39 | #include "symbol.h"
|
| 40 | #include "thread.h"
|
| 41 | #include "time-utils.h"
|
| 42 | #include "trace-event.h"
|
| 43 |
|
| 44 | #ifdef HAVE_LIBTRACEEVENT
|
| 45 | #include <event-parse.h>
|
| 46 | #endif
|
| 47 |
|
| 48 | regex_t parent_regex;
|
| 49 | const char default_parent_pattern[] = "^sys_|^do_page_fault";
|
| 50 | const char *parent_pattern = default_parent_pattern;
|
| 51 | const char *default_sort_order = "comm,dso,symbol";
|
| 52 | static const char default_branch_sort_order[] = "comm,dso_from,symbol_from,symbol_to,cycles";
|
| 53 | const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked,blocked,local_ins_lat,local_p_stage_cyc";
|
| 54 | static const char default_top_sort_order[] = "dso,symbol";
|
| 55 | static const char default_diff_sort_order[] = "dso,symbol";
|
| 56 | static const char default_tracepoint_sort_order[] = "trace";
|
| 57 | const char *sort_order;
|
| 58 | const char *field_order;
|
| 59 | regex_t ignore_callees_regex;
|
| 60 | int have_ignore_callees = 0;
|
| 61 | enum sort_mode sort__mode = SORT_MODE__NORMAL;
|
| 62 | static const char *const dynamic_headers[] = {"local_ins_lat", "ins_lat", "local_p_stage_cyc", "p_stage_cyc"};
|
| 63 | static const char *const arch_specific_sort_keys[] = {"local_p_stage_cyc", "p_stage_cyc"};
|
| 64 |
|
| 65 | |
| 66 | * Some architectures have Adjacent Cacheline Prefetch feature, which
|
| 67 | * behaves like the cacheline size is doubled. Enable this flag to
|
| 68 | * check things in double cacheline granularity.
|
| 69 |
|
| 70 | bool chk_double_cl;
|
| 71 |
|
| 72 | |
| 73 | * Replaces all occurrences of a char used with the:
|
| 74 | *
|
| 75 | * -t, --field-separator
|
| 76 | *
|
| 77 | * option, that uses a special separator character and don't pad with spaces,
|
| 78 | * replacing all occurrences of this separator in symbol names (and other
|
| 79 | * output) with a '.' character, that thus it's the only non valid separator.
|
| 80 |
|
| 81 | static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
|
| 82 | {
|
| 83 | int n;
|
| 84 | va_list ap;
|
| 85 |
|
| 86 | va_start(ap, fmt);
|
| 87 | n = vsnprintf(bf, size, fmt, ap);
|
| 88 | if (symbol_conf.field_sep && n > 0) {
|
| 89 | char *sep = bf;
|
| 90 |
|
| 91 | while (1) {
|
| 92 | sep = strchr(sep, *symbol_conf.field_sep);
|
| 93 | if (sep == NULL)
|
| 94 | break;
|
| 95 | *sep = '.';
|
| 96 | }
|
| 97 | }
|
| 98 | va_end(ap);
|
| 99 |
|
| 100 | if (n >= (int)size)
|
| 101 | return size - 1;
|
| 102 | return n;
|
| 103 | }
|
| 104 |
|
| 105 | static int64_t cmp_null(const void *l, const void *r)
|
| 106 | {
|
| 107 | if (!l && !r)
|
| 108 | return 0;
|
| 109 | else if (!l)
|
| 110 | return -1;
|
| 111 | else
|
| 112 | return 1;
|
| 113 | }
|
| 114 |
|
| 115 |
|
| 116 |
|
| 117 | static int64_t
|
| 118 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 119 | {
|
| 120 | return thread__tid(right->thread) - thread__tid(left->thread);
|
| 121 | }
|
| 122 |
|
| 123 | static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,
|
| 124 | size_t size, unsigned int width)
|
| 125 | {
|
| 126 | const char *comm = thread__comm_str(he->thread);
|
| 127 |
|
| 128 | width = max(7U, width) - 8;
|
| 129 | return repsep_snprintf(bf, size, "%7d:%-*.*s", thread__tid(he->thread),
|
| 130 | width, width, comm ?: "");
|
| 131 | }
|
| 132 |
|
| 133 | static int hist_entry__thread_filter(struct hist_entry *he, int type, const void *arg)
|
| 134 | {
|
| 135 | const struct thread *th = arg;
|
| 136 |
|
| 137 | if (type != HIST_FILTER__THREAD)
|
| 138 | return -1;
|
| 139 |
|
| 140 | return th && !RC_CHK_EQUAL(he->thread, th);
|
| 141 | }
|
| 142 |
|
| 143 | struct sort_entry sort_thread = {
|
| 144 | .se_header = " Pid:Command",
|
| 145 | .se_cmp = sort__thread_cmp,
|
| 146 | .se_snprintf = hist_entry__thread_snprintf,
|
| 147 | .se_filter = hist_entry__thread_filter,
|
| 148 | .se_width_idx = HISTC_THREAD,
|
| 149 | };
|
| 150 |
|
| 151 |
|
| 152 |
|
| 153 | static int64_t
|
| 154 | sort__tgid_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 155 | {
|
| 156 | return thread__pid(right->thread) - thread__pid(left->thread);
|
| 157 | }
|
| 158 |
|
| 159 | static int hist_entry__tgid_snprintf(struct hist_entry *he, char *bf,
|
| 160 | size_t size, unsigned int width)
|
| 161 | {
|
| 162 | int tgid = thread__pid(he->thread);
|
| 163 | const char *comm = NULL;
|
| 164 |
|
| 165 |
|
| 166 | if (thread__pid(he->thread) == thread__tid(he->thread)) {
|
| 167 | comm = thread__comm_str(he->thread);
|
| 168 | } else {
|
| 169 | struct maps *maps = thread__maps(he->thread);
|
| 170 | struct thread *leader = machine__find_thread(maps__machine(maps),
|
| 171 | tgid, tgid);
|
| 172 | if (leader) {
|
| 173 | comm = thread__comm_str(leader);
|
| 174 | thread__put(leader);
|
| 175 | }
|
| 176 | }
|
| 177 | width = max(7U, width) - 8;
|
| 178 | return repsep_snprintf(bf, size, "%7d:%-*.*s", tgid, width, width, comm ?: "");
|
| 179 | }
|
| 180 |
|
| 181 | static struct sort_entry sort_tgid = {
|
| 182 | .se_header = " Tgid:Command",
|
| 183 | .se_cmp = sort__tgid_cmp,
|
| 184 | .se_snprintf = hist_entry__tgid_snprintf,
|
| 185 | .se_width_idx = HISTC_TGID,
|
| 186 | };
|
| 187 |
|
| 188 |
|
| 189 |
|
| 190 | static int64_t
|
| 191 | sort__simd_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 192 | {
|
| 193 | if (left->simd_flags.arch != right->simd_flags.arch)
|
| 194 | return (int64_t) left->simd_flags.arch - right->simd_flags.arch;
|
| 195 |
|
| 196 | return (int64_t) left->simd_flags.pred - right->simd_flags.pred;
|
| 197 | }
|
| 198 |
|
| 199 | static const char *hist_entry__get_simd_name(struct simd_flags *simd_flags)
|
| 200 | {
|
| 201 | u64 arch = simd_flags->arch;
|
| 202 |
|
| 203 | if (arch == SIMD_OP_FLAGS_ARCH_SVE)
|
| 204 | return "SVE";
|
| 205 | else if (arch == SIMD_OP_FLAGS_ARCH_SME)
|
| 206 | return "SME";
|
| 207 | else if (arch == SIMD_OP_FLAGS_ARCH_ASE)
|
| 208 | return "ASE";
|
| 209 | else
|
| 210 | return "n/a";
|
| 211 | }
|
| 212 |
|
| 213 | static int hist_entry__simd_snprintf(struct hist_entry *he, char *bf,
|
| 214 | size_t size, unsigned int width __maybe_unused)
|
| 215 | {
|
| 216 | const char *name;
|
| 217 | const char *pred_str = ".";
|
| 218 |
|
| 219 | if (!he->simd_flags.arch)
|
| 220 | return repsep_snprintf(bf, size, "");
|
| 221 |
|
| 222 | name = hist_entry__get_simd_name(&he->simd_flags);
|
| 223 |
|
| 224 | if (he->simd_flags.pred == SIMD_OP_FLAGS_PRED_EMPTY)
|
| 225 | pred_str = "e";
|
| 226 | else if (he->simd_flags.pred == SIMD_OP_FLAGS_PRED_PARTIAL)
|
| 227 | pred_str = "p";
|
| 228 | else if (he->simd_flags.pred == SIMD_OP_FLAGS_PRED_DISABLED)
|
| 229 | pred_str = "d";
|
| 230 | else if (he->simd_flags.pred == SIMD_OP_FLAGS_PRED_FULL)
|
| 231 | pred_str = "f";
|
| 232 |
|
| 233 | return repsep_snprintf(bf, size, "[%s] %s", pred_str, name);
|
| 234 | }
|
| 235 |
|
| 236 | static struct sort_entry sort_simd = {
|
| 237 | .se_header = "Simd ",
|
| 238 | .se_cmp = sort__simd_cmp,
|
| 239 | .se_snprintf = hist_entry__simd_snprintf,
|
| 240 | .se_width_idx = HISTC_SIMD,
|
| 241 | };
|
| 242 |
|
| 243 |
|
| 244 |
|
| 245 | |
| 246 | * We can't use pointer comparison in functions below,
|
| 247 | * because it gives different results based on pointer
|
| 248 | * values, which could break some sorting assumptions.
|
| 249 |
|
| 250 | static int64_t
|
| 251 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 252 | {
|
| 253 | return strcmp(comm__str(right->comm), comm__str(left->comm));
|
| 254 | }
|
| 255 |
|
| 256 | static int64_t
|
| 257 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
|
| 258 | {
|
| 259 | return strcmp(comm__str(right->comm), comm__str(left->comm));
|
| 260 | }
|
| 261 |
|
| 262 | static int64_t
|
| 263 | sort__comm_sort(struct hist_entry *left, struct hist_entry *right)
|
| 264 | {
|
| 265 | return strcmp(comm__str(right->comm), comm__str(left->comm));
|
| 266 | }
|
| 267 |
|
| 268 | static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
|
| 269 | size_t size, unsigned int width)
|
| 270 | {
|
| 271 | return repsep_snprintf(bf, size, "%-*.*s", width, width, comm__str(he->comm));
|
| 272 | }
|
| 273 |
|
| 274 | struct sort_entry sort_comm = {
|
| 275 | .se_header = "Command",
|
| 276 | .se_cmp = sort__comm_cmp,
|
| 277 | .se_collapse = sort__comm_collapse,
|
| 278 | .se_sort = sort__comm_sort,
|
| 279 | .se_snprintf = hist_entry__comm_snprintf,
|
| 280 | .se_filter = hist_entry__thread_filter,
|
| 281 | .se_width_idx = HISTC_COMM,
|
| 282 | };
|
| 283 |
|
| 284 |
|
| 285 |
|
| 286 | size_t sort__comm_nodigit_len(struct hist_entry *entry)
|
| 287 | {
|
| 288 | const char *comm = comm__str(entry->comm);
|
| 289 | size_t index, len_nodigit = 0;
|
| 290 | bool in_number = false;
|
| 291 |
|
| 292 | if (!comm)
|
| 293 | return 0;
|
| 294 |
|
| 295 | for (index = 0; comm[index]; index++) {
|
| 296 | if (!isdigit((unsigned char)comm[index])) {
|
| 297 | in_number = false;
|
| 298 | len_nodigit++;
|
| 299 | } else if (!in_number) {
|
| 300 | in_number = true;
|
| 301 | len_nodigit += 3;
|
| 302 | }
|
| 303 | }
|
| 304 |
|
| 305 | return len_nodigit;
|
| 306 | }
|
| 307 |
|
| 308 | static int64_t strcmp_nodigit(const char *left, const char *right)
|
| 309 | {
|
| 310 | for (;;) {
|
| 311 | while (*left && isdigit((unsigned char)*left))
|
| 312 | left++;
|
| 313 | while (*right && isdigit((unsigned char)*right))
|
| 314 | right++;
|
| 315 | if (*left == *right && !*left) {
|
| 316 | return 0;
|
| 317 | } else if (*left == *right) {
|
| 318 | left++;
|
| 319 | right++;
|
| 320 | } else {
|
| 321 | return (int64_t)((unsigned char)*left - (unsigned char)*right);
|
| 322 | }
|
| 323 | }
|
| 324 | }
|
| 325 |
|
| 326 | static int64_t
|
| 327 | sort__comm_nodigit_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 328 | {
|
| 329 | return strcmp_nodigit(comm__str(right->comm), comm__str(left->comm));
|
| 330 | }
|
| 331 |
|
| 332 | static int64_t
|
| 333 | sort__comm_nodigit_collapse(struct hist_entry *left, struct hist_entry *right)
|
| 334 | {
|
| 335 | return strcmp_nodigit(comm__str(right->comm), comm__str(left->comm));
|
| 336 | }
|
| 337 |
|
| 338 | static int64_t
|
| 339 | sort__comm_nodigit_sort(struct hist_entry *left, struct hist_entry *right)
|
| 340 | {
|
| 341 | return strcmp_nodigit(comm__str(right->comm), comm__str(left->comm));
|
| 342 | }
|
| 343 |
|
| 344 | static int hist_entry__comm_nodigit_snprintf(struct hist_entry *he, char *bf,
|
| 345 | size_t size, unsigned int width)
|
| 346 | {
|
| 347 | int ret = 0;
|
| 348 | unsigned int print_len, printed = 0, start = 0, end = 0;
|
| 349 | bool in_digit;
|
| 350 | const char *comm = comm__str(he->comm), *print;
|
| 351 |
|
| 352 | while (printed < width && printed < size && comm[start]) {
|
| 353 | in_digit = !!isdigit((unsigned char)comm[start]);
|
| 354 | end = start + 1;
|
| 355 | while (comm[end] && !!isdigit((unsigned char)comm[end]) == in_digit)
|
| 356 | end++;
|
| 357 | if (in_digit) {
|
| 358 | print_len = 3;
|
| 359 | print = "<N>";
|
| 360 | } else {
|
| 361 | print_len = end - start;
|
| 362 | print = &comm[start];
|
| 363 | }
|
| 364 | print_len = min(print_len, width - printed);
|
| 365 | ret = repsep_snprintf(bf + printed, size - printed, "%-.*s",
|
| 366 | print_len, print);
|
| 367 | if (ret < 0)
|
| 368 | return ret;
|
| 369 | start = end;
|
| 370 | printed += ret;
|
| 371 | }
|
| 372 |
|
| 373 | if (printed < width && printed < size) {
|
| 374 | ret = repsep_snprintf(bf + printed, size - printed, "%-*.*s",
|
| 375 | width - printed, width - printed, "");
|
| 376 | if (ret < 0)
|
| 377 | return ret;
|
| 378 | printed += ret;
|
| 379 | }
|
| 380 | return printed;
|
| 381 | }
|
| 382 |
|
| 383 | struct sort_entry sort_comm_nodigit = {
|
| 384 | .se_header = "CommandNoDigit",
|
| 385 | .se_cmp = sort__comm_nodigit_cmp,
|
| 386 | .se_collapse = sort__comm_nodigit_collapse,
|
| 387 | .se_sort = sort__comm_nodigit_sort,
|
| 388 | .se_snprintf = hist_entry__comm_nodigit_snprintf,
|
| 389 | .se_filter = hist_entry__thread_filter,
|
| 390 | .se_width_idx = HISTC_COMM_NODIGIT,
|
| 391 | };
|
| 392 |
|
| 393 |
|
| 394 |
|
| 395 | static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
|
| 396 | {
|
| 397 | struct dso *dso_l = map_l ? map__dso(map_l) : NULL;
|
| 398 | struct dso *dso_r = map_r ? map__dso(map_r) : NULL;
|
| 399 | const char *dso_name_l, *dso_name_r;
|
| 400 |
|
| 401 | if (!dso_l || !dso_r)
|
| 402 | return cmp_null(dso_r, dso_l);
|
| 403 |
|
| 404 | if (verbose > 0) {
|
| 405 | dso_name_l = dso__long_name(dso_l);
|
| 406 | dso_name_r = dso__long_name(dso_r);
|
| 407 | } else {
|
| 408 | dso_name_l = dso__short_name(dso_l);
|
| 409 | dso_name_r = dso__short_name(dso_r);
|
| 410 | }
|
| 411 |
|
| 412 | return strcmp(dso_name_l, dso_name_r);
|
| 413 | }
|
| 414 |
|
| 415 | static int64_t
|
| 416 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 417 | {
|
| 418 | return _sort__dso_cmp(right->ms.map, left->ms.map);
|
| 419 | }
|
| 420 |
|
| 421 | static int _hist_entry__dso_snprintf(struct map *map, char *bf,
|
| 422 | size_t size, unsigned int width)
|
| 423 | {
|
| 424 | const struct dso *dso = map ? map__dso(map) : NULL;
|
| 425 | const char *dso_name = "[unknown]";
|
| 426 |
|
| 427 | if (dso)
|
| 428 | dso_name = verbose > 0 ? dso__long_name(dso) : dso__short_name(dso);
|
| 429 |
|
| 430 | return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name);
|
| 431 | }
|
| 432 |
|
| 433 | static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,
|
| 434 | size_t size, unsigned int width)
|
| 435 | {
|
| 436 | return _hist_entry__dso_snprintf(he->ms.map, bf, size, width);
|
| 437 | }
|
| 438 |
|
| 439 | static int hist_entry__dso_filter(struct hist_entry *he, int type, const void *arg)
|
| 440 | {
|
| 441 | const struct dso *dso = arg;
|
| 442 |
|
| 443 | if (type != HIST_FILTER__DSO)
|
| 444 | return -1;
|
| 445 |
|
| 446 | return dso && (!he->ms.map || map__dso(he->ms.map) != dso);
|
| 447 | }
|
| 448 |
|
| 449 | struct sort_entry sort_dso = {
|
| 450 | .se_header = "Shared Object",
|
| 451 | .se_cmp = sort__dso_cmp,
|
| 452 | .se_snprintf = hist_entry__dso_snprintf,
|
| 453 | .se_filter = hist_entry__dso_filter,
|
| 454 | .se_width_idx = HISTC_DSO,
|
| 455 | };
|
| 456 |
|
| 457 |
|
| 458 |
|
| 459 | static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
|
| 460 | {
|
| 461 | return (int64_t)(right_ip - left_ip);
|
| 462 | }
|
| 463 |
|
| 464 | int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
|
| 465 | {
|
| 466 | if (!sym_l || !sym_r)
|
| 467 | return cmp_null(sym_l, sym_r);
|
| 468 |
|
| 469 | if (sym_l == sym_r)
|
| 470 | return 0;
|
| 471 |
|
| 472 | if (symbol__inlined(sym_l) || symbol__inlined(sym_r)) {
|
| 473 | int ret = strcmp(sym_l->name, sym_r->name);
|
| 474 |
|
| 475 | if (ret)
|
| 476 | return ret;
|
| 477 | if ((sym_l->start <= sym_r->end) && (sym_l->end >= sym_r->start))
|
| 478 | return 0;
|
| 479 | }
|
| 480 |
|
| 481 | if (sym_l->start != sym_r->start)
|
| 482 | return (int64_t)(sym_r->start - sym_l->start);
|
| 483 |
|
| 484 | return (int64_t)(sym_r->end - sym_l->end);
|
| 485 | }
|
| 486 |
|
| 487 | static int64_t
|
| 488 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 489 | {
|
| 490 | int64_t ret;
|
| 491 |
|
| 492 | if (!left->ms.sym && !right->ms.sym)
|
| 493 | return _sort__addr_cmp(left->ip, right->ip);
|
| 494 |
|
| 495 | |
| 496 | * comparing symbol address alone is not enough since it's a
|
| 497 | * relative address within a dso.
|
| 498 |
|
| 499 | if (!hists__has(left->hists, dso)) {
|
| 500 | ret = sort__dso_cmp(left, right);
|
| 501 | if (ret != 0)
|
| 502 | return ret;
|
| 503 | }
|
| 504 |
|
| 505 | return _sort__sym_cmp(left->ms.sym, right->ms.sym);
|
| 506 | }
|
| 507 |
|
| 508 | static int64_t
|
| 509 | sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
|
| 510 | {
|
| 511 | if (!left->ms.sym || !right->ms.sym)
|
| 512 | return cmp_null(left->ms.sym, right->ms.sym);
|
| 513 |
|
| 514 | return strcmp(right->ms.sym->name, left->ms.sym->name);
|
| 515 | }
|
| 516 |
|
| 517 | static int _hist_entry__sym_snprintf(struct map_symbol *ms,
|
| 518 | u64 ip, char level, char *bf, size_t size,
|
| 519 | unsigned int width)
|
| 520 | {
|
| 521 | struct symbol *sym = ms->sym;
|
| 522 | struct map *map = ms->map;
|
| 523 | size_t ret = 0;
|
| 524 |
|
| 525 | if (verbose > 0) {
|
| 526 | struct dso *dso = map ? map__dso(map) : NULL;
|
| 527 | char o = dso ? dso__symtab_origin(dso) : '!';
|
| 528 | u64 rip = ip;
|
| 529 |
|
| 530 | if (dso && dso__kernel(dso) && dso__adjust_symbols(dso))
|
| 531 | rip = map__unmap_ip(map, ip);
|
| 532 |
|
| 533 | ret += repsep_snprintf(bf, size, "%-#*llx %c ",
|
| 534 | BITS_PER_LONG / 4 + 2, rip, o);
|
| 535 | }
|
| 536 |
|
| 537 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
|
| 538 | if (sym && map) {
|
| 539 | if (symbol__type(sym) == STT_OBJECT) {
|
| 540 | ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
|
| 541 | ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
|
| 542 | ip - map__unmap_ip(map, sym->start));
|
| 543 | } else {
|
| 544 | ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
|
| 545 | width - ret,
|
| 546 | sym->name);
|
| 547 | if (symbol__inlined(sym))
|
| 548 | ret += repsep_snprintf(bf + ret, size - ret,
|
| 549 | " (inlined)");
|
| 550 | }
|
| 551 | } else {
|
| 552 | size_t len = BITS_PER_LONG / 4;
|
| 553 | ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
|
| 554 | len, ip);
|
| 555 | }
|
| 556 |
|
| 557 | return ret;
|
| 558 | }
|
| 559 |
|
| 560 | int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width)
|
| 561 | {
|
| 562 | return _hist_entry__sym_snprintf(&he->ms, he->ip,
|
| 563 | he->level, bf, size, width);
|
| 564 | }
|
| 565 |
|
| 566 | static int hist_entry__sym_filter(struct hist_entry *he, int type, const void *arg)
|
| 567 | {
|
| 568 | const char *sym = arg;
|
| 569 |
|
| 570 | if (type != HIST_FILTER__SYMBOL)
|
| 571 | return -1;
|
| 572 |
|
| 573 | return sym && (!he->ms.sym || !strstr(he->ms.sym->name, sym));
|
| 574 | }
|
| 575 |
|
| 576 | struct sort_entry sort_sym = {
|
| 577 | .se_header = "Symbol",
|
| 578 | .se_cmp = sort__sym_cmp,
|
| 579 | .se_sort = sort__sym_sort,
|
| 580 | .se_snprintf = hist_entry__sym_snprintf,
|
| 581 | .se_filter = hist_entry__sym_filter,
|
| 582 | .se_width_idx = HISTC_SYMBOL,
|
| 583 | };
|
| 584 |
|
| 585 |
|
| 586 |
|
| 587 | static int64_t
|
| 588 | sort__symoff_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 589 | {
|
| 590 | int64_t ret;
|
| 591 |
|
| 592 | ret = sort__sym_cmp(left, right);
|
| 593 | if (ret)
|
| 594 | return ret;
|
| 595 |
|
| 596 | return left->ip - right->ip;
|
| 597 | }
|
| 598 |
|
| 599 | static int64_t
|
| 600 | sort__symoff_sort(struct hist_entry *left, struct hist_entry *right)
|
| 601 | {
|
| 602 | int64_t ret;
|
| 603 |
|
| 604 | ret = sort__sym_sort(left, right);
|
| 605 | if (ret)
|
| 606 | return ret;
|
| 607 |
|
| 608 | return left->ip - right->ip;
|
| 609 | }
|
| 610 |
|
| 611 | static int
|
| 612 | hist_entry__symoff_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width)
|
| 613 | {
|
| 614 | struct symbol *sym = he->ms.sym;
|
| 615 |
|
| 616 | if (sym == NULL)
|
| 617 | return repsep_snprintf(bf, size, "[%c] %-#.*llx", he->level, width - 4, he->ip);
|
| 618 |
|
| 619 | return repsep_snprintf(bf, size, "[%c] %s+0x%llx", he->level, sym->name, he->ip - sym->start);
|
| 620 | }
|
| 621 |
|
| 622 | static struct sort_entry sort_sym_offset = {
|
| 623 | .se_header = "Symbol Offset",
|
| 624 | .se_cmp = sort__symoff_cmp,
|
| 625 | .se_sort = sort__symoff_sort,
|
| 626 | .se_snprintf = hist_entry__symoff_snprintf,
|
| 627 | .se_filter = hist_entry__sym_filter,
|
| 628 | .se_width_idx = HISTC_SYMBOL_OFFSET,
|
| 629 | };
|
| 630 |
|
| 631 |
|
| 632 |
|
| 633 | char *hist_entry__srcline(struct hist_entry *he)
|
| 634 | {
|
| 635 | return map__srcline(he->ms.map, he->ip, he->ms.sym);
|
| 636 | }
|
| 637 |
|
| 638 | static int64_t
|
| 639 | sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 640 | {
|
| 641 | int64_t ret;
|
| 642 |
|
| 643 | ret = _sort__addr_cmp(left->ip, right->ip);
|
| 644 | if (ret)
|
| 645 | return ret;
|
| 646 |
|
| 647 | return sort__dso_cmp(left, right);
|
| 648 | }
|
| 649 |
|
| 650 | static int64_t
|
| 651 | sort__srcline_collapse(struct hist_entry *left, struct hist_entry *right)
|
| 652 | {
|
| 653 | if (!left->srcline)
|
| 654 | left->srcline = hist_entry__srcline(left);
|
| 655 | if (!right->srcline)
|
| 656 | right->srcline = hist_entry__srcline(right);
|
| 657 |
|
| 658 | return strcmp(right->srcline, left->srcline);
|
| 659 | }
|
| 660 |
|
| 661 | static int64_t
|
| 662 | sort__srcline_sort(struct hist_entry *left, struct hist_entry *right)
|
| 663 | {
|
| 664 | return sort__srcline_collapse(left, right);
|
| 665 | }
|
| 666 |
|
| 667 | static void
|
| 668 | sort__srcline_init(struct hist_entry *he)
|
| 669 | {
|
| 670 | if (!he->srcline)
|
| 671 | he->srcline = hist_entry__srcline(he);
|
| 672 | }
|
| 673 |
|
| 674 | static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
|
| 675 | size_t size, unsigned int width)
|
| 676 | {
|
| 677 | return repsep_snprintf(bf, size, "%-.*s", width, he->srcline);
|
| 678 | }
|
| 679 |
|
| 680 | struct sort_entry sort_srcline = {
|
| 681 | .se_header = "Source:Line",
|
| 682 | .se_cmp = sort__srcline_cmp,
|
| 683 | .se_collapse = sort__srcline_collapse,
|
| 684 | .se_sort = sort__srcline_sort,
|
| 685 | .se_init = sort__srcline_init,
|
| 686 | .se_snprintf = hist_entry__srcline_snprintf,
|
| 687 | .se_width_idx = HISTC_SRCLINE,
|
| 688 | };
|
| 689 |
|
| 690 |
|
| 691 |
|
| 692 | static char *addr_map_symbol__srcline(struct addr_map_symbol *ams)
|
| 693 | {
|
| 694 | return map__srcline(ams->ms.map, ams->al_addr, ams->ms.sym);
|
| 695 | }
|
| 696 |
|
| 697 | static int64_t
|
| 698 | sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 699 | {
|
| 700 | return left->branch_info->from.addr - right->branch_info->from.addr;
|
| 701 | }
|
| 702 |
|
| 703 | static int64_t
|
| 704 | sort__srcline_from_collapse(struct hist_entry *left, struct hist_entry *right)
|
| 705 | {
|
| 706 | if (!left->branch_info->srcline_from)
|
| 707 | left->branch_info->srcline_from = addr_map_symbol__srcline(&left->branch_info->from);
|
| 708 |
|
| 709 | if (!right->branch_info->srcline_from)
|
| 710 | right->branch_info->srcline_from = addr_map_symbol__srcline(&right->branch_info->from);
|
| 711 |
|
| 712 | return strcmp(right->branch_info->srcline_from, left->branch_info->srcline_from);
|
| 713 | }
|
| 714 |
|
| 715 | static int64_t
|
| 716 | sort__srcline_from_sort(struct hist_entry *left, struct hist_entry *right)
|
| 717 | {
|
| 718 | return sort__srcline_from_collapse(left, right);
|
| 719 | }
|
| 720 |
|
| 721 | static void sort__srcline_from_init(struct hist_entry *he)
|
| 722 | {
|
| 723 | if (!he->branch_info->srcline_from)
|
| 724 | he->branch_info->srcline_from = addr_map_symbol__srcline(&he->branch_info->from);
|
| 725 | }
|
| 726 |
|
| 727 | static int hist_entry__srcline_from_snprintf(struct hist_entry *he, char *bf,
|
| 728 | size_t size, unsigned int width)
|
| 729 | {
|
| 730 | return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_from);
|
| 731 | }
|
| 732 |
|
| 733 | static struct sort_entry sort_srcline_from = {
|
| 734 | .se_header = "From Source:Line",
|
| 735 | .se_cmp = sort__srcline_from_cmp,
|
| 736 | .se_collapse = sort__srcline_from_collapse,
|
| 737 | .se_sort = sort__srcline_from_sort,
|
| 738 | .se_init = sort__srcline_from_init,
|
| 739 | .se_snprintf = hist_entry__srcline_from_snprintf,
|
| 740 | .se_width_idx = HISTC_SRCLINE_FROM,
|
| 741 | };
|
| 742 |
|
| 743 |
|
| 744 |
|
| 745 | static int64_t
|
| 746 | sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 747 | {
|
| 748 | return left->branch_info->to.addr - right->branch_info->to.addr;
|
| 749 | }
|
| 750 |
|
| 751 | static int64_t
|
| 752 | sort__srcline_to_collapse(struct hist_entry *left, struct hist_entry *right)
|
| 753 | {
|
| 754 | if (!left->branch_info->srcline_to)
|
| 755 | left->branch_info->srcline_to = addr_map_symbol__srcline(&left->branch_info->to);
|
| 756 |
|
| 757 | if (!right->branch_info->srcline_to)
|
| 758 | right->branch_info->srcline_to = addr_map_symbol__srcline(&right->branch_info->to);
|
| 759 |
|
| 760 | return strcmp(right->branch_info->srcline_to, left->branch_info->srcline_to);
|
| 761 | }
|
| 762 |
|
| 763 | static int64_t
|
| 764 | sort__srcline_to_sort(struct hist_entry *left, struct hist_entry *right)
|
| 765 | {
|
| 766 | return sort__srcline_to_collapse(left, right);
|
| 767 | }
|
| 768 |
|
| 769 | static void sort__srcline_to_init(struct hist_entry *he)
|
| 770 | {
|
| 771 | if (!he->branch_info->srcline_to)
|
| 772 | he->branch_info->srcline_to = addr_map_symbol__srcline(&he->branch_info->to);
|
| 773 | }
|
| 774 |
|
| 775 | static int hist_entry__srcline_to_snprintf(struct hist_entry *he, char *bf,
|
| 776 | size_t size, unsigned int width)
|
| 777 | {
|
| 778 | return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_to);
|
| 779 | }
|
| 780 |
|
| 781 | static struct sort_entry sort_srcline_to = {
|
| 782 | .se_header = "To Source:Line",
|
| 783 | .se_cmp = sort__srcline_to_cmp,
|
| 784 | .se_collapse = sort__srcline_to_collapse,
|
| 785 | .se_sort = sort__srcline_to_sort,
|
| 786 | .se_init = sort__srcline_to_init,
|
| 787 | .se_snprintf = hist_entry__srcline_to_snprintf,
|
| 788 | .se_width_idx = HISTC_SRCLINE_TO,
|
| 789 | };
|
| 790 |
|
| 791 | static int hist_entry__sym_ipc_snprintf(struct hist_entry *he, char *bf,
|
| 792 | size_t size, unsigned int width)
|
| 793 | {
|
| 794 |
|
| 795 | struct symbol *sym = he->ms.sym;
|
| 796 | struct annotated_branch *branch;
|
| 797 | double ipc = 0.0, coverage = 0.0;
|
| 798 | char tmp[64];
|
| 799 |
|
| 800 | if (!sym)
|
| 801 | return repsep_snprintf(bf, size, "%-*s", width, "-");
|
| 802 |
|
| 803 | branch = symbol__annotation(sym)->branch;
|
| 804 |
|
| 805 | if (branch && branch->hit_cycles)
|
| 806 | ipc = branch->hit_insn / ((double)branch->hit_cycles);
|
| 807 |
|
| 808 | if (branch && branch->total_insn) {
|
| 809 | coverage = branch->cover_insn * 100.0 /
|
| 810 | ((double)branch->total_insn);
|
| 811 | }
|
| 812 |
|
| 813 | snprintf(tmp, sizeof(tmp), "%-5.2f [%5.1f%%]", ipc, coverage);
|
| 814 | return repsep_snprintf(bf, size, "%-*s", width, tmp);
|
| 815 | }
|
| 816 |
|
| 817 | static struct sort_entry sort_sym_ipc = {
|
| 818 | .se_header = "IPC [IPC Coverage]",
|
| 819 | .se_cmp = sort__sym_cmp,
|
| 820 | .se_snprintf = hist_entry__sym_ipc_snprintf,
|
| 821 | .se_width_idx = HISTC_SYMBOL_IPC,
|
| 822 | };
|
| 823 |
|
| 824 | static int hist_entry__sym_ipc_null_snprintf(struct hist_entry *he
|
| 825 | __maybe_unused,
|
| 826 | char *bf, size_t size,
|
| 827 | unsigned int width)
|
| 828 | {
|
| 829 | char tmp[64];
|
| 830 |
|
| 831 | snprintf(tmp, sizeof(tmp), "%-5s %2s", "-", "-");
|
| 832 | return repsep_snprintf(bf, size, "%-*s", width, tmp);
|
| 833 | }
|
| 834 |
|
| 835 | static struct sort_entry sort_sym_ipc_null = {
|
| 836 | .se_header = "IPC [IPC Coverage]",
|
| 837 | .se_cmp = sort__sym_cmp,
|
| 838 | .se_snprintf = hist_entry__sym_ipc_null_snprintf,
|
| 839 | .se_width_idx = HISTC_SYMBOL_IPC,
|
| 840 | };
|
| 841 |
|
| 842 |
|
| 843 |
|
| 844 | static int64_t
|
| 845 | sort__callchain_branch_predicted_cmp(struct hist_entry *left __maybe_unused,
|
| 846 | struct hist_entry *right __maybe_unused)
|
| 847 | {
|
| 848 | return 0;
|
| 849 | }
|
| 850 |
|
| 851 | static int hist_entry__callchain_branch_predicted_snprintf(
|
| 852 | struct hist_entry *he, char *bf, size_t size, unsigned int width)
|
| 853 | {
|
| 854 | u64 branch_count, predicted_count;
|
| 855 | double percent = 0.0;
|
| 856 | char str[32];
|
| 857 |
|
| 858 | callchain_branch_counts(he->callchain, &branch_count,
|
| 859 | &predicted_count, NULL, NULL);
|
| 860 |
|
| 861 | if (branch_count)
|
| 862 | percent = predicted_count * 100.0 / branch_count;
|
| 863 |
|
| 864 | snprintf(str, sizeof(str), "%.1f%%", percent);
|
| 865 | return repsep_snprintf(bf, size, "%-*.*s", width, width, str);
|
| 866 | }
|
| 867 |
|
| 868 | static struct sort_entry sort_callchain_branch_predicted = {
|
| 869 | .se_header = "Predicted",
|
| 870 | .se_cmp = sort__callchain_branch_predicted_cmp,
|
| 871 | .se_snprintf = hist_entry__callchain_branch_predicted_snprintf,
|
| 872 | .se_width_idx = HISTC_CALLCHAIN_BRANCH_PREDICTED,
|
| 873 | };
|
| 874 |
|
| 875 |
|
| 876 |
|
| 877 | static int64_t
|
| 878 | sort__callchain_branch_abort_cmp(struct hist_entry *left __maybe_unused,
|
| 879 | struct hist_entry *right __maybe_unused)
|
| 880 | {
|
| 881 | return 0;
|
| 882 | }
|
| 883 |
|
| 884 | static int hist_entry__callchain_branch_abort_snprintf(struct hist_entry *he,
|
| 885 | char *bf, size_t size,
|
| 886 | unsigned int width)
|
| 887 | {
|
| 888 | u64 branch_count, abort_count;
|
| 889 | char str[32];
|
| 890 |
|
| 891 | callchain_branch_counts(he->callchain, &branch_count,
|
| 892 | NULL, &abort_count, NULL);
|
| 893 |
|
| 894 | snprintf(str, sizeof(str), "%" PRId64, abort_count);
|
| 895 | return repsep_snprintf(bf, size, "%-*.*s", width, width, str);
|
| 896 | }
|
| 897 |
|
| 898 | static struct sort_entry sort_callchain_branch_abort = {
|
| 899 | .se_header = "Abort",
|
| 900 | .se_cmp = sort__callchain_branch_abort_cmp,
|
| 901 | .se_snprintf = hist_entry__callchain_branch_abort_snprintf,
|
| 902 | .se_width_idx = HISTC_CALLCHAIN_BRANCH_ABORT,
|
| 903 | };
|
| 904 |
|
| 905 |
|
| 906 |
|
| 907 | static int64_t
|
| 908 | sort__callchain_branch_cycles_cmp(struct hist_entry *left __maybe_unused,
|
| 909 | struct hist_entry *right __maybe_unused)
|
| 910 | {
|
| 911 | return 0;
|
| 912 | }
|
| 913 |
|
| 914 | static int hist_entry__callchain_branch_cycles_snprintf(struct hist_entry *he,
|
| 915 | char *bf, size_t size,
|
| 916 | unsigned int width)
|
| 917 | {
|
| 918 | u64 branch_count, cycles_count, cycles = 0;
|
| 919 | char str[32];
|
| 920 |
|
| 921 | callchain_branch_counts(he->callchain, &branch_count,
|
| 922 | NULL, NULL, &cycles_count);
|
| 923 |
|
| 924 | if (branch_count)
|
| 925 | cycles = cycles_count / branch_count;
|
| 926 |
|
| 927 | snprintf(str, sizeof(str), "%" PRId64 "", cycles);
|
| 928 | return repsep_snprintf(bf, size, "%-*.*s", width, width, str);
|
| 929 | }
|
| 930 |
|
| 931 | static struct sort_entry sort_callchain_branch_cycles = {
|
| 932 | .se_header = "Cycles",
|
| 933 | .se_cmp = sort__callchain_branch_cycles_cmp,
|
| 934 | .se_snprintf = hist_entry__callchain_branch_cycles_snprintf,
|
| 935 | .se_width_idx = HISTC_CALLCHAIN_BRANCH_CYCLES,
|
| 936 | };
|
| 937 |
|
| 938 |
|
| 939 |
|
| 940 | static char no_srcfile[1];
|
| 941 |
|
| 942 | static char *hist_entry__get_srcfile(struct hist_entry *e)
|
| 943 | {
|
| 944 | char *sf, *p;
|
| 945 | struct map *map = e->ms.map;
|
| 946 |
|
| 947 | if (!map)
|
| 948 | return no_srcfile;
|
| 949 |
|
| 950 | sf = __get_srcline(map__dso(map), map__rip_2objdump(map, e->ip),
|
| 951 | e->ms.sym, false, true, true, e->ip);
|
| 952 | if (sf == SRCLINE_UNKNOWN)
|
| 953 | return no_srcfile;
|
| 954 | p = strchr(sf, ':');
|
| 955 | if (p && *sf) {
|
| 956 | *p = 0;
|
| 957 | return sf;
|
| 958 | }
|
| 959 | free(sf);
|
| 960 | return no_srcfile;
|
| 961 | }
|
| 962 |
|
| 963 | static int64_t
|
| 964 | sort__srcfile_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 965 | {
|
| 966 | return sort__srcline_cmp(left, right);
|
| 967 | }
|
| 968 |
|
| 969 | static int64_t
|
| 970 | sort__srcfile_collapse(struct hist_entry *left, struct hist_entry *right)
|
| 971 | {
|
| 972 | if (!left->srcfile)
|
| 973 | left->srcfile = hist_entry__get_srcfile(left);
|
| 974 | if (!right->srcfile)
|
| 975 | right->srcfile = hist_entry__get_srcfile(right);
|
| 976 |
|
| 977 | return strcmp(right->srcfile, left->srcfile);
|
| 978 | }
|
| 979 |
|
| 980 | static int64_t
|
| 981 | sort__srcfile_sort(struct hist_entry *left, struct hist_entry *right)
|
| 982 | {
|
| 983 | return sort__srcfile_collapse(left, right);
|
| 984 | }
|
| 985 |
|
| 986 | static void sort__srcfile_init(struct hist_entry *he)
|
| 987 | {
|
| 988 | if (!he->srcfile)
|
| 989 | he->srcfile = hist_entry__get_srcfile(he);
|
| 990 | }
|
| 991 |
|
| 992 | static int hist_entry__srcfile_snprintf(struct hist_entry *he, char *bf,
|
| 993 | size_t size, unsigned int width)
|
| 994 | {
|
| 995 | return repsep_snprintf(bf, size, "%-.*s", width, he->srcfile);
|
| 996 | }
|
| 997 |
|
| 998 | static struct sort_entry sort_srcfile = {
|
| 999 | .se_header = "Source File",
|
| 1000 | .se_cmp = sort__srcfile_cmp,
|
| 1001 | .se_collapse = sort__srcfile_collapse,
|
| 1002 | .se_sort = sort__srcfile_sort,
|
| 1003 | .se_init = sort__srcfile_init,
|
| 1004 | .se_snprintf = hist_entry__srcfile_snprintf,
|
| 1005 | .se_width_idx = HISTC_SRCFILE,
|
| 1006 | };
|
| 1007 |
|
| 1008 |
|
| 1009 |
|
| 1010 | static int64_t
|
| 1011 | sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1012 | {
|
| 1013 | struct symbol *sym_l = left->parent;
|
| 1014 | struct symbol *sym_r = right->parent;
|
| 1015 |
|
| 1016 | if (!sym_l || !sym_r)
|
| 1017 | return cmp_null(sym_l, sym_r);
|
| 1018 |
|
| 1019 | return strcmp(sym_r->name, sym_l->name);
|
| 1020 | }
|
| 1021 |
|
| 1022 | static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
|
| 1023 | size_t size, unsigned int width)
|
| 1024 | {
|
| 1025 | return repsep_snprintf(bf, size, "%-*.*s", width, width,
|
| 1026 | he->parent ? he->parent->name : "[other]");
|
| 1027 | }
|
| 1028 |
|
| 1029 | struct sort_entry sort_parent = {
|
| 1030 | .se_header = "Parent symbol",
|
| 1031 | .se_cmp = sort__parent_cmp,
|
| 1032 | .se_snprintf = hist_entry__parent_snprintf,
|
| 1033 | .se_width_idx = HISTC_PARENT,
|
| 1034 | };
|
| 1035 |
|
| 1036 |
|
| 1037 |
|
| 1038 | static int64_t
|
| 1039 | sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1040 | {
|
| 1041 | return right->cpu - left->cpu;
|
| 1042 | }
|
| 1043 |
|
| 1044 | static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf,
|
| 1045 | size_t size, unsigned int width)
|
| 1046 | {
|
| 1047 | return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu);
|
| 1048 | }
|
| 1049 |
|
| 1050 | static struct sort_entry sort_cpu = {
|
| 1051 | .se_header = "CPU",
|
| 1052 | .se_cmp = sort__cpu_cmp,
|
| 1053 | .se_snprintf = hist_entry__cpu_snprintf,
|
| 1054 | .se_width_idx = HISTC_CPU,
|
| 1055 | };
|
| 1056 |
|
| 1057 |
|
| 1058 |
|
| 1059 | static int64_t
|
| 1060 | sort__parallelism_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1061 | {
|
| 1062 | return right->parallelism - left->parallelism;
|
| 1063 | }
|
| 1064 |
|
| 1065 | static int hist_entry__parallelism_filter(struct hist_entry *he, int type, const void *arg)
|
| 1066 | {
|
| 1067 | const unsigned long *parallelism_filter = arg;
|
| 1068 |
|
| 1069 | if (type != HIST_FILTER__PARALLELISM)
|
| 1070 | return -1;
|
| 1071 |
|
| 1072 | return test_bit(he->parallelism, parallelism_filter);
|
| 1073 | }
|
| 1074 |
|
| 1075 | static int hist_entry__parallelism_snprintf(struct hist_entry *he, char *bf,
|
| 1076 | size_t size, unsigned int width)
|
| 1077 | {
|
| 1078 | return repsep_snprintf(bf, size, "%*d", width, he->parallelism);
|
| 1079 | }
|
| 1080 |
|
| 1081 | static struct sort_entry sort_parallelism = {
|
| 1082 | .se_header = "Parallelism",
|
| 1083 | .se_cmp = sort__parallelism_cmp,
|
| 1084 | .se_filter = hist_entry__parallelism_filter,
|
| 1085 | .se_snprintf = hist_entry__parallelism_snprintf,
|
| 1086 | .se_width_idx = HISTC_PARALLELISM,
|
| 1087 | };
|
| 1088 |
|
| 1089 |
|
| 1090 |
|
| 1091 | static int64_t _sort__cgroup_dev_cmp(u64 left_dev, u64 right_dev)
|
| 1092 | {
|
| 1093 | return (int64_t)(right_dev - left_dev);
|
| 1094 | }
|
| 1095 |
|
| 1096 | static int64_t _sort__cgroup_inode_cmp(u64 left_ino, u64 right_ino)
|
| 1097 | {
|
| 1098 | return (int64_t)(right_ino - left_ino);
|
| 1099 | }
|
| 1100 |
|
| 1101 | static int64_t
|
| 1102 | sort__cgroup_id_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1103 | {
|
| 1104 | int64_t ret;
|
| 1105 |
|
| 1106 | ret = _sort__cgroup_dev_cmp(right->cgroup_id.dev, left->cgroup_id.dev);
|
| 1107 | if (ret != 0)
|
| 1108 | return ret;
|
| 1109 |
|
| 1110 | return _sort__cgroup_inode_cmp(right->cgroup_id.ino,
|
| 1111 | left->cgroup_id.ino);
|
| 1112 | }
|
| 1113 |
|
| 1114 | static int hist_entry__cgroup_id_snprintf(struct hist_entry *he,
|
| 1115 | char *bf, size_t size,
|
| 1116 | unsigned int width __maybe_unused)
|
| 1117 | {
|
| 1118 | return repsep_snprintf(bf, size, "%lu/0x%lx", he->cgroup_id.dev,
|
| 1119 | he->cgroup_id.ino);
|
| 1120 | }
|
| 1121 |
|
| 1122 | static struct sort_entry sort_cgroup_id = {
|
| 1123 | .se_header = "cgroup id (dev/inode)",
|
| 1124 | .se_cmp = sort__cgroup_id_cmp,
|
| 1125 | .se_snprintf = hist_entry__cgroup_id_snprintf,
|
| 1126 | .se_width_idx = HISTC_CGROUP_ID,
|
| 1127 | };
|
| 1128 |
|
| 1129 |
|
| 1130 |
|
| 1131 | static int64_t
|
| 1132 | sort__cgroup_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1133 | {
|
| 1134 | return right->cgroup - left->cgroup;
|
| 1135 | }
|
| 1136 |
|
| 1137 | static int hist_entry__cgroup_snprintf(struct hist_entry *he,
|
| 1138 | char *bf, size_t size,
|
| 1139 | unsigned int width __maybe_unused)
|
| 1140 | {
|
| 1141 | const char *cgrp_name = "N/A";
|
| 1142 |
|
| 1143 | if (he->cgroup) {
|
| 1144 | struct cgroup *cgrp = cgroup__find(maps__machine(thread__maps(he->ms.thread))->env,
|
| 1145 | he->cgroup);
|
| 1146 | if (cgrp != NULL)
|
| 1147 | cgrp_name = cgrp->name;
|
| 1148 | else
|
| 1149 | cgrp_name = "unknown";
|
| 1150 | }
|
| 1151 |
|
| 1152 | return repsep_snprintf(bf, size, "%s", cgrp_name);
|
| 1153 | }
|
| 1154 |
|
| 1155 | static struct sort_entry sort_cgroup = {
|
| 1156 | .se_header = "Cgroup",
|
| 1157 | .se_cmp = sort__cgroup_cmp,
|
| 1158 | .se_snprintf = hist_entry__cgroup_snprintf,
|
| 1159 | .se_width_idx = HISTC_CGROUP,
|
| 1160 | };
|
| 1161 |
|
| 1162 |
|
| 1163 |
|
| 1164 | static int64_t
|
| 1165 | sort__socket_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1166 | {
|
| 1167 | return right->socket - left->socket;
|
| 1168 | }
|
| 1169 |
|
| 1170 | static int hist_entry__socket_snprintf(struct hist_entry *he, char *bf,
|
| 1171 | size_t size, unsigned int width)
|
| 1172 | {
|
| 1173 | return repsep_snprintf(bf, size, "%*.*d", width, width-3, he->socket);
|
| 1174 | }
|
| 1175 |
|
| 1176 | static int hist_entry__socket_filter(struct hist_entry *he, int type, const void *arg)
|
| 1177 | {
|
| 1178 | int sk = *(const int *)arg;
|
| 1179 |
|
| 1180 | if (type != HIST_FILTER__SOCKET)
|
| 1181 | return -1;
|
| 1182 |
|
| 1183 | return sk >= 0 && he->socket != sk;
|
| 1184 | }
|
| 1185 |
|
| 1186 | static struct sort_entry sort_socket = {
|
| 1187 | .se_header = "Socket",
|
| 1188 | .se_cmp = sort__socket_cmp,
|
| 1189 | .se_snprintf = hist_entry__socket_snprintf,
|
| 1190 | .se_filter = hist_entry__socket_filter,
|
| 1191 | .se_width_idx = HISTC_SOCKET,
|
| 1192 | };
|
| 1193 |
|
| 1194 |
|
| 1195 |
|
| 1196 | static int64_t
|
| 1197 | sort__time_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1198 | {
|
| 1199 | return right->time - left->time;
|
| 1200 | }
|
| 1201 |
|
| 1202 | static int hist_entry__time_snprintf(struct hist_entry *he, char *bf,
|
| 1203 | size_t size, unsigned int width)
|
| 1204 | {
|
| 1205 | char he_time[32];
|
| 1206 |
|
| 1207 | if (symbol_conf.nanosecs)
|
| 1208 | timestamp__scnprintf_nsec(he->time, he_time,
|
| 1209 | sizeof(he_time));
|
| 1210 | else
|
| 1211 | timestamp__scnprintf_usec(he->time, he_time,
|
| 1212 | sizeof(he_time));
|
| 1213 |
|
| 1214 | return repsep_snprintf(bf, size, "%-.*s", width, he_time);
|
| 1215 | }
|
| 1216 |
|
| 1217 | static struct sort_entry sort_time = {
|
| 1218 | .se_header = "Time",
|
| 1219 | .se_cmp = sort__time_cmp,
|
| 1220 | .se_snprintf = hist_entry__time_snprintf,
|
| 1221 | .se_width_idx = HISTC_TIME,
|
| 1222 | };
|
| 1223 |
|
| 1224 |
|
| 1225 |
|
| 1226 | #ifdef HAVE_LIBTRACEEVENT
|
| 1227 | static char *get_trace_output(struct hist_entry *he)
|
| 1228 | {
|
| 1229 | struct trace_seq seq;
|
| 1230 | struct evsel *evsel;
|
| 1231 | struct tep_record rec = {
|
| 1232 | .data = he->raw_data,
|
| 1233 | .size = he->raw_size,
|
| 1234 | };
|
| 1235 | struct tep_event *tp_format;
|
| 1236 |
|
| 1237 | evsel = hists_to_evsel(he->hists);
|
| 1238 |
|
| 1239 | trace_seq_init(&seq);
|
| 1240 | tp_format = evsel__tp_format(evsel);
|
| 1241 | if (tp_format) {
|
| 1242 | if (symbol_conf.raw_trace)
|
| 1243 | tep_print_fields(&seq, he->raw_data, he->raw_size, tp_format);
|
| 1244 | else
|
| 1245 | tep_print_event(tp_format->tep, &seq, &rec, "%s", TEP_PRINT_INFO);
|
| 1246 | }
|
| 1247 |
|
| 1248 | |
| 1249 | * Trim the buffer, it starts at 4KB and we're not going to
|
| 1250 | * add anything more to this buffer.
|
| 1251 |
|
| 1252 | return realloc(seq.buffer, seq.len + 1);
|
| 1253 | }
|
| 1254 |
|
| 1255 | static int64_t
|
| 1256 | sort__trace_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1257 | {
|
| 1258 | struct evsel *evsel;
|
| 1259 |
|
| 1260 | evsel = hists_to_evsel(left->hists);
|
| 1261 | if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
|
| 1262 | return 0;
|
| 1263 |
|
| 1264 | if (left->trace_output == NULL)
|
| 1265 | left->trace_output = get_trace_output(left);
|
| 1266 | if (right->trace_output == NULL)
|
| 1267 | right->trace_output = get_trace_output(right);
|
| 1268 |
|
| 1269 | return strcmp(right->trace_output, left->trace_output);
|
| 1270 | }
|
| 1271 |
|
| 1272 | static int hist_entry__trace_snprintf(struct hist_entry *he, char *bf,
|
| 1273 | size_t size, unsigned int width)
|
| 1274 | {
|
| 1275 | struct evsel *evsel;
|
| 1276 |
|
| 1277 | evsel = hists_to_evsel(he->hists);
|
| 1278 | if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
|
| 1279 | return scnprintf(bf, size, "%-.*s", width, "N/A");
|
| 1280 |
|
| 1281 | if (he->trace_output == NULL)
|
| 1282 | he->trace_output = get_trace_output(he);
|
| 1283 | return repsep_snprintf(bf, size, "%-.*s", width, he->trace_output);
|
| 1284 | }
|
| 1285 |
|
| 1286 | static struct sort_entry sort_trace = {
|
| 1287 | .se_header = "Trace output",
|
| 1288 | .se_cmp = sort__trace_cmp,
|
| 1289 | .se_snprintf = hist_entry__trace_snprintf,
|
| 1290 | .se_width_idx = HISTC_TRACE,
|
| 1291 | };
|
| 1292 | #endif
|
| 1293 |
|
| 1294 |
|
| 1295 |
|
| 1296 | static int64_t
|
| 1297 | sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1298 | {
|
| 1299 | if (!left->branch_info || !right->branch_info)
|
| 1300 | return cmp_null(left->branch_info, right->branch_info);
|
| 1301 |
|
| 1302 | return _sort__dso_cmp(left->branch_info->from.ms.map,
|
| 1303 | right->branch_info->from.ms.map);
|
| 1304 | }
|
| 1305 |
|
| 1306 | static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,
|
| 1307 | size_t size, unsigned int width)
|
| 1308 | {
|
| 1309 | if (he->branch_info)
|
| 1310 | return _hist_entry__dso_snprintf(he->branch_info->from.ms.map,
|
| 1311 | bf, size, width);
|
| 1312 | else
|
| 1313 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
|
| 1314 | }
|
| 1315 |
|
| 1316 | static int hist_entry__dso_from_filter(struct hist_entry *he, int type,
|
| 1317 | const void *arg)
|
| 1318 | {
|
| 1319 | const struct dso *dso = arg;
|
| 1320 |
|
| 1321 | if (type != HIST_FILTER__DSO)
|
| 1322 | return -1;
|
| 1323 |
|
| 1324 | return dso && (!he->branch_info || !he->branch_info->from.ms.map ||
|
| 1325 | map__dso(he->branch_info->from.ms.map) != dso);
|
| 1326 | }
|
| 1327 |
|
| 1328 | static int64_t
|
| 1329 | sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1330 | {
|
| 1331 | if (!left->branch_info || !right->branch_info)
|
| 1332 | return cmp_null(left->branch_info, right->branch_info);
|
| 1333 |
|
| 1334 | return _sort__dso_cmp(left->branch_info->to.ms.map,
|
| 1335 | right->branch_info->to.ms.map);
|
| 1336 | }
|
| 1337 |
|
| 1338 | static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,
|
| 1339 | size_t size, unsigned int width)
|
| 1340 | {
|
| 1341 | if (he->branch_info)
|
| 1342 | return _hist_entry__dso_snprintf(he->branch_info->to.ms.map,
|
| 1343 | bf, size, width);
|
| 1344 | else
|
| 1345 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
|
| 1346 | }
|
| 1347 |
|
| 1348 | static int hist_entry__dso_to_filter(struct hist_entry *he, int type,
|
| 1349 | const void *arg)
|
| 1350 | {
|
| 1351 | const struct dso *dso = arg;
|
| 1352 |
|
| 1353 | if (type != HIST_FILTER__DSO)
|
| 1354 | return -1;
|
| 1355 |
|
| 1356 | return dso && (!he->branch_info || !he->branch_info->to.ms.map ||
|
| 1357 | map__dso(he->branch_info->to.ms.map) != dso);
|
| 1358 | }
|
| 1359 |
|
| 1360 | static int64_t
|
| 1361 | sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1362 | {
|
| 1363 | struct addr_map_symbol *from_l, *from_r;
|
| 1364 |
|
| 1365 | if (!left->branch_info || !right->branch_info)
|
| 1366 | return cmp_null(left->branch_info, right->branch_info);
|
| 1367 |
|
| 1368 | from_l = &left->branch_info->from;
|
| 1369 | from_r = &right->branch_info->from;
|
| 1370 |
|
| 1371 | if (!from_l->ms.sym && !from_r->ms.sym)
|
| 1372 | return _sort__addr_cmp(from_l->addr, from_r->addr);
|
| 1373 |
|
| 1374 | return _sort__sym_cmp(from_l->ms.sym, from_r->ms.sym);
|
| 1375 | }
|
| 1376 |
|
| 1377 | static int64_t
|
| 1378 | sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1379 | {
|
| 1380 | struct addr_map_symbol *to_l, *to_r;
|
| 1381 |
|
| 1382 | if (!left->branch_info || !right->branch_info)
|
| 1383 | return cmp_null(left->branch_info, right->branch_info);
|
| 1384 |
|
| 1385 | to_l = &left->branch_info->to;
|
| 1386 | to_r = &right->branch_info->to;
|
| 1387 |
|
| 1388 | if (!to_l->ms.sym && !to_r->ms.sym)
|
| 1389 | return _sort__addr_cmp(to_l->addr, to_r->addr);
|
| 1390 |
|
| 1391 | return _sort__sym_cmp(to_l->ms.sym, to_r->ms.sym);
|
| 1392 | }
|
| 1393 |
|
| 1394 | static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
|
| 1395 | size_t size, unsigned int width)
|
| 1396 | {
|
| 1397 | if (he->branch_info) {
|
| 1398 | struct addr_map_symbol *from = &he->branch_info->from;
|
| 1399 |
|
| 1400 | return _hist_entry__sym_snprintf(&from->ms, from->al_addr,
|
| 1401 | from->al_level, bf, size, width);
|
| 1402 | }
|
| 1403 |
|
| 1404 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
|
| 1405 | }
|
| 1406 |
|
| 1407 | static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,
|
| 1408 | size_t size, unsigned int width)
|
| 1409 | {
|
| 1410 | if (he->branch_info) {
|
| 1411 | struct addr_map_symbol *to = &he->branch_info->to;
|
| 1412 |
|
| 1413 | return _hist_entry__sym_snprintf(&to->ms, to->al_addr,
|
| 1414 | to->al_level, bf, size, width);
|
| 1415 | }
|
| 1416 |
|
| 1417 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
|
| 1418 | }
|
| 1419 |
|
| 1420 | static int hist_entry__sym_from_filter(struct hist_entry *he, int type,
|
| 1421 | const void *arg)
|
| 1422 | {
|
| 1423 | const char *sym = arg;
|
| 1424 |
|
| 1425 | if (type != HIST_FILTER__SYMBOL)
|
| 1426 | return -1;
|
| 1427 |
|
| 1428 | return sym && !(he->branch_info && he->branch_info->from.ms.sym &&
|
| 1429 | strstr(he->branch_info->from.ms.sym->name, sym));
|
| 1430 | }
|
| 1431 |
|
| 1432 | static int hist_entry__sym_to_filter(struct hist_entry *he, int type,
|
| 1433 | const void *arg)
|
| 1434 | {
|
| 1435 | const char *sym = arg;
|
| 1436 |
|
| 1437 | if (type != HIST_FILTER__SYMBOL)
|
| 1438 | return -1;
|
| 1439 |
|
| 1440 | return sym && !(he->branch_info && he->branch_info->to.ms.sym &&
|
| 1441 | strstr(he->branch_info->to.ms.sym->name, sym));
|
| 1442 | }
|
| 1443 |
|
| 1444 | struct sort_entry sort_dso_from = {
|
| 1445 | .se_header = "Source Shared Object",
|
| 1446 | .se_cmp = sort__dso_from_cmp,
|
| 1447 | .se_snprintf = hist_entry__dso_from_snprintf,
|
| 1448 | .se_filter = hist_entry__dso_from_filter,
|
| 1449 | .se_width_idx = HISTC_DSO_FROM,
|
| 1450 | };
|
| 1451 |
|
| 1452 | struct sort_entry sort_dso_to = {
|
| 1453 | .se_header = "Target Shared Object",
|
| 1454 | .se_cmp = sort__dso_to_cmp,
|
| 1455 | .se_snprintf = hist_entry__dso_to_snprintf,
|
| 1456 | .se_filter = hist_entry__dso_to_filter,
|
| 1457 | .se_width_idx = HISTC_DSO_TO,
|
| 1458 | };
|
| 1459 |
|
| 1460 | struct sort_entry sort_sym_from = {
|
| 1461 | .se_header = "Source Symbol",
|
| 1462 | .se_cmp = sort__sym_from_cmp,
|
| 1463 | .se_snprintf = hist_entry__sym_from_snprintf,
|
| 1464 | .se_filter = hist_entry__sym_from_filter,
|
| 1465 | .se_width_idx = HISTC_SYMBOL_FROM,
|
| 1466 | };
|
| 1467 |
|
| 1468 | struct sort_entry sort_sym_to = {
|
| 1469 | .se_header = "Target Symbol",
|
| 1470 | .se_cmp = sort__sym_to_cmp,
|
| 1471 | .se_snprintf = hist_entry__sym_to_snprintf,
|
| 1472 | .se_filter = hist_entry__sym_to_filter,
|
| 1473 | .se_width_idx = HISTC_SYMBOL_TO,
|
| 1474 | };
|
| 1475 |
|
| 1476 | static int _hist_entry__addr_snprintf(struct map_symbol *ms,
|
| 1477 | u64 ip, char level, char *bf, size_t size,
|
| 1478 | unsigned int width)
|
| 1479 | {
|
| 1480 | struct symbol *sym = ms->sym;
|
| 1481 | struct map *map = ms->map;
|
| 1482 | size_t ret = 0, offs;
|
| 1483 |
|
| 1484 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
|
| 1485 | if (sym && map) {
|
| 1486 | if (symbol__type(sym) == STT_OBJECT) {
|
| 1487 | ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
|
| 1488 | ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
|
| 1489 | ip - map__unmap_ip(map, sym->start));
|
| 1490 | } else {
|
| 1491 | ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
|
| 1492 | width - ret,
|
| 1493 | sym->name);
|
| 1494 | offs = ip - sym->start;
|
| 1495 | if (offs)
|
| 1496 | ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", offs);
|
| 1497 | }
|
| 1498 | } else {
|
| 1499 | size_t len = BITS_PER_LONG / 4;
|
| 1500 | ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
|
| 1501 | len, ip);
|
| 1502 | }
|
| 1503 |
|
| 1504 | return ret;
|
| 1505 | }
|
| 1506 |
|
| 1507 | static int hist_entry__addr_from_snprintf(struct hist_entry *he, char *bf,
|
| 1508 | size_t size, unsigned int width)
|
| 1509 | {
|
| 1510 | if (he->branch_info) {
|
| 1511 | struct addr_map_symbol *from = &he->branch_info->from;
|
| 1512 |
|
| 1513 | return _hist_entry__addr_snprintf(&from->ms, from->al_addr,
|
| 1514 | he->level, bf, size, width);
|
| 1515 | }
|
| 1516 |
|
| 1517 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
|
| 1518 | }
|
| 1519 |
|
| 1520 | static int hist_entry__addr_to_snprintf(struct hist_entry *he, char *bf,
|
| 1521 | size_t size, unsigned int width)
|
| 1522 | {
|
| 1523 | if (he->branch_info) {
|
| 1524 | struct addr_map_symbol *to = &he->branch_info->to;
|
| 1525 |
|
| 1526 | return _hist_entry__addr_snprintf(&to->ms, to->al_addr,
|
| 1527 | he->level, bf, size, width);
|
| 1528 | }
|
| 1529 |
|
| 1530 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
|
| 1531 | }
|
| 1532 |
|
| 1533 | static int64_t
|
| 1534 | sort__addr_from_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1535 | {
|
| 1536 | struct addr_map_symbol *from_l;
|
| 1537 | struct addr_map_symbol *from_r;
|
| 1538 | int64_t ret;
|
| 1539 |
|
| 1540 | if (!left->branch_info || !right->branch_info)
|
| 1541 | return cmp_null(left->branch_info, right->branch_info);
|
| 1542 |
|
| 1543 | from_l = &left->branch_info->from;
|
| 1544 | from_r = &right->branch_info->from;
|
| 1545 |
|
| 1546 | |
| 1547 | * comparing symbol address alone is not enough since it's a
|
| 1548 | * relative address within a dso.
|
| 1549 |
|
| 1550 | ret = _sort__dso_cmp(from_l->ms.map, from_r->ms.map);
|
| 1551 | if (ret != 0)
|
| 1552 | return ret;
|
| 1553 |
|
| 1554 | return _sort__addr_cmp(from_l->addr, from_r->addr);
|
| 1555 | }
|
| 1556 |
|
| 1557 | static int64_t
|
| 1558 | sort__addr_to_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1559 | {
|
| 1560 | struct addr_map_symbol *to_l;
|
| 1561 | struct addr_map_symbol *to_r;
|
| 1562 | int64_t ret;
|
| 1563 |
|
| 1564 | if (!left->branch_info || !right->branch_info)
|
| 1565 | return cmp_null(left->branch_info, right->branch_info);
|
| 1566 |
|
| 1567 | to_l = &left->branch_info->to;
|
| 1568 | to_r = &right->branch_info->to;
|
| 1569 |
|
| 1570 | |
| 1571 | * comparing symbol address alone is not enough since it's a
|
| 1572 | * relative address within a dso.
|
| 1573 |
|
| 1574 | ret = _sort__dso_cmp(to_l->ms.map, to_r->ms.map);
|
| 1575 | if (ret != 0)
|
| 1576 | return ret;
|
| 1577 |
|
| 1578 | return _sort__addr_cmp(to_l->addr, to_r->addr);
|
| 1579 | }
|
| 1580 |
|
| 1581 | static struct sort_entry sort_addr_from = {
|
| 1582 | .se_header = "Source Address",
|
| 1583 | .se_cmp = sort__addr_from_cmp,
|
| 1584 | .se_snprintf = hist_entry__addr_from_snprintf,
|
| 1585 | .se_filter = hist_entry__sym_from_filter,
|
| 1586 | .se_width_idx = HISTC_ADDR_FROM,
|
| 1587 | };
|
| 1588 |
|
| 1589 | static struct sort_entry sort_addr_to = {
|
| 1590 | .se_header = "Target Address",
|
| 1591 | .se_cmp = sort__addr_to_cmp,
|
| 1592 | .se_snprintf = hist_entry__addr_to_snprintf,
|
| 1593 | .se_filter = hist_entry__sym_to_filter,
|
| 1594 | .se_width_idx = HISTC_ADDR_TO,
|
| 1595 | };
|
| 1596 |
|
| 1597 |
|
| 1598 | static int64_t
|
| 1599 | sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1600 | {
|
| 1601 | unsigned char mp, p;
|
| 1602 |
|
| 1603 | if (!left->branch_info || !right->branch_info)
|
| 1604 | return cmp_null(left->branch_info, right->branch_info);
|
| 1605 |
|
| 1606 | mp = left->branch_info->flags.mispred != right->branch_info->flags.mispred;
|
| 1607 | p = left->branch_info->flags.predicted != right->branch_info->flags.predicted;
|
| 1608 | return mp || p;
|
| 1609 | }
|
| 1610 |
|
| 1611 | static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,
|
| 1612 | size_t size, unsigned int width){
|
| 1613 | static const char *out = "N/A";
|
| 1614 |
|
| 1615 | if (he->branch_info) {
|
| 1616 | if (he->branch_info->flags.predicted)
|
| 1617 | out = "N";
|
| 1618 | else if (he->branch_info->flags.mispred)
|
| 1619 | out = "Y";
|
| 1620 | }
|
| 1621 |
|
| 1622 | return repsep_snprintf(bf, size, "%-*.*s", width, width, out);
|
| 1623 | }
|
| 1624 |
|
| 1625 | static int64_t
|
| 1626 | sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1627 | {
|
| 1628 | if (!left->branch_info || !right->branch_info)
|
| 1629 | return cmp_null(left->branch_info, right->branch_info);
|
| 1630 |
|
| 1631 | return left->branch_info->flags.cycles -
|
| 1632 | right->branch_info->flags.cycles;
|
| 1633 | }
|
| 1634 |
|
| 1635 | static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf,
|
| 1636 | size_t size, unsigned int width)
|
| 1637 | {
|
| 1638 | if (!he->branch_info)
|
| 1639 | return scnprintf(bf, size, "%-.*s", width, "N/A");
|
| 1640 | if (he->branch_info->flags.cycles == 0)
|
| 1641 | return repsep_snprintf(bf, size, "%-*s", width, "-");
|
| 1642 | return repsep_snprintf(bf, size, "%-*hd", width,
|
| 1643 | he->branch_info->flags.cycles);
|
| 1644 | }
|
| 1645 |
|
| 1646 | static struct sort_entry sort_cycles = {
|
| 1647 | .se_header = "Basic Block Cycles",
|
| 1648 | .se_cmp = sort__cycles_cmp,
|
| 1649 | .se_snprintf = hist_entry__cycles_snprintf,
|
| 1650 | .se_width_idx = HISTC_CYCLES,
|
| 1651 | };
|
| 1652 |
|
| 1653 |
|
| 1654 | int64_t
|
| 1655 | sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1656 | {
|
| 1657 | uint64_t l = 0, r = 0;
|
| 1658 |
|
| 1659 | if (left->mem_info)
|
| 1660 | l = mem_info__daddr(left->mem_info)->addr;
|
| 1661 | if (right->mem_info)
|
| 1662 | r = mem_info__daddr(right->mem_info)->addr;
|
| 1663 |
|
| 1664 | return (int64_t)(r - l);
|
| 1665 | }
|
| 1666 |
|
| 1667 | static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
|
| 1668 | size_t size, unsigned int width)
|
| 1669 | {
|
| 1670 | uint64_t addr = 0;
|
| 1671 | struct map_symbol *ms = NULL;
|
| 1672 |
|
| 1673 | if (he->mem_info) {
|
| 1674 | addr = mem_info__daddr(he->mem_info)->addr;
|
| 1675 | ms = &mem_info__daddr(he->mem_info)->ms;
|
| 1676 | }
|
| 1677 | return _hist_entry__sym_snprintf(ms, addr, he->level, bf, size, width);
|
| 1678 | }
|
| 1679 |
|
| 1680 | int64_t
|
| 1681 | sort__iaddr_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1682 | {
|
| 1683 | uint64_t l = 0, r = 0;
|
| 1684 |
|
| 1685 | if (left->mem_info)
|
| 1686 | l = mem_info__iaddr(left->mem_info)->addr;
|
| 1687 | if (right->mem_info)
|
| 1688 | r = mem_info__iaddr(right->mem_info)->addr;
|
| 1689 |
|
| 1690 | return (int64_t)(r - l);
|
| 1691 | }
|
| 1692 |
|
| 1693 | static int hist_entry__iaddr_snprintf(struct hist_entry *he, char *bf,
|
| 1694 | size_t size, unsigned int width)
|
| 1695 | {
|
| 1696 | uint64_t addr = 0;
|
| 1697 | struct map_symbol *ms = NULL;
|
| 1698 |
|
| 1699 | if (he->mem_info) {
|
| 1700 | addr = mem_info__iaddr(he->mem_info)->addr;
|
| 1701 | ms = &mem_info__iaddr(he->mem_info)->ms;
|
| 1702 | }
|
| 1703 | return _hist_entry__sym_snprintf(ms, addr, he->level, bf, size, width);
|
| 1704 | }
|
| 1705 |
|
| 1706 | static int64_t
|
| 1707 | sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1708 | {
|
| 1709 | struct map *map_l = NULL;
|
| 1710 | struct map *map_r = NULL;
|
| 1711 |
|
| 1712 | if (left->mem_info)
|
| 1713 | map_l = mem_info__daddr(left->mem_info)->ms.map;
|
| 1714 | if (right->mem_info)
|
| 1715 | map_r = mem_info__daddr(right->mem_info)->ms.map;
|
| 1716 |
|
| 1717 | return _sort__dso_cmp(map_l, map_r);
|
| 1718 | }
|
| 1719 |
|
| 1720 | static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,
|
| 1721 | size_t size, unsigned int width)
|
| 1722 | {
|
| 1723 | struct map *map = NULL;
|
| 1724 |
|
| 1725 | if (he->mem_info)
|
| 1726 | map = mem_info__daddr(he->mem_info)->ms.map;
|
| 1727 |
|
| 1728 | return _hist_entry__dso_snprintf(map, bf, size, width);
|
| 1729 | }
|
| 1730 |
|
| 1731 | static int64_t
|
| 1732 | sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1733 | {
|
| 1734 | union perf_mem_data_src data_src_l;
|
| 1735 | union perf_mem_data_src data_src_r;
|
| 1736 |
|
| 1737 | if (left->mem_info)
|
| 1738 | data_src_l = *mem_info__data_src(left->mem_info);
|
| 1739 | else
|
| 1740 | data_src_l.mem_lock = PERF_MEM_LOCK_NA;
|
| 1741 |
|
| 1742 | if (right->mem_info)
|
| 1743 | data_src_r = *mem_info__data_src(right->mem_info);
|
| 1744 | else
|
| 1745 | data_src_r.mem_lock = PERF_MEM_LOCK_NA;
|
| 1746 |
|
| 1747 | return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
|
| 1748 | }
|
| 1749 |
|
| 1750 | static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,
|
| 1751 | size_t size, unsigned int width)
|
| 1752 | {
|
| 1753 | char out[10];
|
| 1754 |
|
| 1755 | perf_mem__lck_scnprintf(out, sizeof(out), he->mem_info);
|
| 1756 | return repsep_snprintf(bf, size, "%.*s", width, out);
|
| 1757 | }
|
| 1758 |
|
| 1759 | static int64_t
|
| 1760 | sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1761 | {
|
| 1762 | union perf_mem_data_src data_src_l;
|
| 1763 | union perf_mem_data_src data_src_r;
|
| 1764 |
|
| 1765 | if (left->mem_info)
|
| 1766 | data_src_l = *mem_info__data_src(left->mem_info);
|
| 1767 | else
|
| 1768 | data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
|
| 1769 |
|
| 1770 | if (right->mem_info)
|
| 1771 | data_src_r = *mem_info__data_src(right->mem_info);
|
| 1772 | else
|
| 1773 | data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
|
| 1774 |
|
| 1775 | return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
|
| 1776 | }
|
| 1777 |
|
| 1778 | static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
|
| 1779 | size_t size, unsigned int width)
|
| 1780 | {
|
| 1781 | char out[64];
|
| 1782 |
|
| 1783 | perf_mem__tlb_scnprintf(out, sizeof(out), he->mem_info);
|
| 1784 | return repsep_snprintf(bf, size, "%-*s", width, out);
|
| 1785 | }
|
| 1786 |
|
| 1787 | static int64_t
|
| 1788 | sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1789 | {
|
| 1790 | union perf_mem_data_src data_src_l;
|
| 1791 | union perf_mem_data_src data_src_r;
|
| 1792 |
|
| 1793 | if (left->mem_info)
|
| 1794 | data_src_l = *mem_info__data_src(left->mem_info);
|
| 1795 | else
|
| 1796 | data_src_l.mem_lvl = PERF_MEM_LVL_NA;
|
| 1797 |
|
| 1798 | if (right->mem_info)
|
| 1799 | data_src_r = *mem_info__data_src(right->mem_info);
|
| 1800 | else
|
| 1801 | data_src_r.mem_lvl = PERF_MEM_LVL_NA;
|
| 1802 |
|
| 1803 | return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
|
| 1804 | }
|
| 1805 |
|
| 1806 | static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,
|
| 1807 | size_t size, unsigned int width)
|
| 1808 | {
|
| 1809 | char out[64];
|
| 1810 |
|
| 1811 | perf_mem__lvl_scnprintf(out, sizeof(out), he->mem_info);
|
| 1812 | return repsep_snprintf(bf, size, "%-*s", width, out);
|
| 1813 | }
|
| 1814 |
|
| 1815 | static int64_t
|
| 1816 | sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1817 | {
|
| 1818 | union perf_mem_data_src data_src_l;
|
| 1819 | union perf_mem_data_src data_src_r;
|
| 1820 |
|
| 1821 | if (left->mem_info)
|
| 1822 | data_src_l = *mem_info__data_src(left->mem_info);
|
| 1823 | else
|
| 1824 | data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
|
| 1825 |
|
| 1826 | if (right->mem_info)
|
| 1827 | data_src_r = *mem_info__data_src(right->mem_info);
|
| 1828 | else
|
| 1829 | data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
|
| 1830 |
|
| 1831 | return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
|
| 1832 | }
|
| 1833 |
|
| 1834 | static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
|
| 1835 | size_t size, unsigned int width)
|
| 1836 | {
|
| 1837 | char out[64];
|
| 1838 |
|
| 1839 | perf_mem__snp_scnprintf(out, sizeof(out), he->mem_info);
|
| 1840 | return repsep_snprintf(bf, size, "%-*s", width, out);
|
| 1841 | }
|
| 1842 |
|
| 1843 | int64_t
|
| 1844 | sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1845 | {
|
| 1846 | u64 l, r;
|
| 1847 | struct map *l_map, *r_map;
|
| 1848 | struct dso *l_dso, *r_dso;
|
| 1849 | int rc;
|
| 1850 |
|
| 1851 | if (!left->mem_info) return -1;
|
| 1852 | if (!right->mem_info) return 1;
|
| 1853 |
|
| 1854 |
|
| 1855 | if (left->cpumode > right->cpumode) return -1;
|
| 1856 | if (left->cpumode < right->cpumode) return 1;
|
| 1857 |
|
| 1858 | l_map = mem_info__daddr(left->mem_info)->ms.map;
|
| 1859 | r_map = mem_info__daddr(right->mem_info)->ms.map;
|
| 1860 |
|
| 1861 |
|
| 1862 | if (!l_map && !r_map)
|
| 1863 | goto addr;
|
| 1864 |
|
| 1865 | if (!l_map) return -1;
|
| 1866 | if (!r_map) return 1;
|
| 1867 |
|
| 1868 | l_dso = map__dso(l_map);
|
| 1869 | r_dso = map__dso(r_map);
|
| 1870 | rc = dso__cmp_id(l_dso, r_dso);
|
| 1871 | if (rc)
|
| 1872 | return rc;
|
| 1873 | |
| 1874 | * Addresses with no major/minor numbers or build ID are assumed to be
|
| 1875 | * anonymous in userspace. Sort those on pid then address.
|
| 1876 | *
|
| 1877 | * The kernel and non-zero major/minor mapped areas are
|
| 1878 | * assumed to be unity mapped. Sort those on address.
|
| 1879 |
|
| 1880 | if (left->cpumode != PERF_RECORD_MISC_KERNEL && (map__flags(l_map) & MAP_SHARED) == 0) {
|
| 1881 | const struct dso_id *dso_id = dso__id_const(l_dso);
|
| 1882 |
|
| 1883 | if (!dso_id->mmap2_valid)
|
| 1884 | dso_id = dso__id_const(r_dso);
|
| 1885 |
|
| 1886 | if (!build_id__is_defined(&dso_id->build_id) &&
|
| 1887 | (!dso_id->mmap2_valid || (dso_id->maj == 0 && dso_id->min == 0))) {
|
| 1888 |
|
| 1889 |
|
| 1890 | if (thread__pid(left->thread) > thread__pid(right->thread))
|
| 1891 | return -1;
|
| 1892 | if (thread__pid(left->thread) < thread__pid(right->thread))
|
| 1893 | return 1;
|
| 1894 | }
|
| 1895 | }
|
| 1896 |
|
| 1897 | addr:
|
| 1898 |
|
| 1899 | l = cl_address(mem_info__daddr(left->mem_info)->al_addr, chk_double_cl);
|
| 1900 | r = cl_address(mem_info__daddr(right->mem_info)->al_addr, chk_double_cl);
|
| 1901 |
|
| 1902 | if (l > r) return -1;
|
| 1903 | if (l < r) return 1;
|
| 1904 |
|
| 1905 | return 0;
|
| 1906 | }
|
| 1907 |
|
| 1908 | static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf,
|
| 1909 | size_t size, unsigned int width)
|
| 1910 | {
|
| 1911 |
|
| 1912 | uint64_t addr = 0;
|
| 1913 | struct map_symbol *ms = NULL;
|
| 1914 | char level = he->level;
|
| 1915 |
|
| 1916 | if (he->mem_info) {
|
| 1917 | struct map *map = mem_info__daddr(he->mem_info)->ms.map;
|
| 1918 | struct dso *dso = map ? map__dso(map) : NULL;
|
| 1919 | const struct dso_id *dso_id = dso ? dso__id_const(dso) : &dso_id_empty;
|
| 1920 |
|
| 1921 | addr = cl_address(mem_info__daddr(he->mem_info)->al_addr, chk_double_cl);
|
| 1922 | ms = &mem_info__daddr(he->mem_info)->ms;
|
| 1923 |
|
| 1924 |
|
| 1925 | if ((he->cpumode != PERF_RECORD_MISC_KERNEL) &&
|
| 1926 | map && !(map__prot(map) & PROT_EXEC) &&
|
| 1927 | (map__flags(map) & MAP_SHARED) &&
|
| 1928 | (!dso_id->mmap2_valid || (dso_id->maj == 0 && dso_id->min == 0)))
|
| 1929 | level = 's';
|
| 1930 | else if (!map)
|
| 1931 | level = 'X';
|
| 1932 | }
|
| 1933 | return _hist_entry__sym_snprintf(ms, addr, level, bf, size, width);
|
| 1934 | }
|
| 1935 |
|
| 1936 | static struct sort_entry sort_mispredict = {
|
| 1937 | .se_header = "Branch Mispredicted",
|
| 1938 | .se_cmp = sort__mispredict_cmp,
|
| 1939 | .se_snprintf = hist_entry__mispredict_snprintf,
|
| 1940 | .se_width_idx = HISTC_MISPREDICT,
|
| 1941 | };
|
| 1942 |
|
| 1943 | static int64_t
|
| 1944 | sort__weight_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1945 | {
|
| 1946 | return left->weight - right->weight;
|
| 1947 | }
|
| 1948 |
|
| 1949 | static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,
|
| 1950 | size_t size, unsigned int width)
|
| 1951 | {
|
| 1952 | return repsep_snprintf(bf, size, "%-*llu", width, he->weight);
|
| 1953 | }
|
| 1954 |
|
| 1955 | static struct sort_entry sort_local_weight = {
|
| 1956 | .se_header = "Local Weight",
|
| 1957 | .se_cmp = sort__weight_cmp,
|
| 1958 | .se_snprintf = hist_entry__local_weight_snprintf,
|
| 1959 | .se_width_idx = HISTC_LOCAL_WEIGHT,
|
| 1960 | };
|
| 1961 |
|
| 1962 | static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,
|
| 1963 | size_t size, unsigned int width)
|
| 1964 | {
|
| 1965 | return repsep_snprintf(bf, size, "%-*llu", width,
|
| 1966 | he->weight * he->stat.nr_events);
|
| 1967 | }
|
| 1968 |
|
| 1969 | static struct sort_entry sort_global_weight = {
|
| 1970 | .se_header = "Weight",
|
| 1971 | .se_cmp = sort__weight_cmp,
|
| 1972 | .se_snprintf = hist_entry__global_weight_snprintf,
|
| 1973 | .se_width_idx = HISTC_GLOBAL_WEIGHT,
|
| 1974 | };
|
| 1975 |
|
| 1976 | static int64_t
|
| 1977 | sort__ins_lat_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 1978 | {
|
| 1979 | return left->ins_lat - right->ins_lat;
|
| 1980 | }
|
| 1981 |
|
| 1982 | static int hist_entry__local_ins_lat_snprintf(struct hist_entry *he, char *bf,
|
| 1983 | size_t size, unsigned int width)
|
| 1984 | {
|
| 1985 | return repsep_snprintf(bf, size, "%-*u", width, he->ins_lat);
|
| 1986 | }
|
| 1987 |
|
| 1988 | static struct sort_entry sort_local_ins_lat = {
|
| 1989 | .se_header = "Local INSTR Latency",
|
| 1990 | .se_cmp = sort__ins_lat_cmp,
|
| 1991 | .se_snprintf = hist_entry__local_ins_lat_snprintf,
|
| 1992 | .se_width_idx = HISTC_LOCAL_INS_LAT,
|
| 1993 | };
|
| 1994 |
|
| 1995 | static int hist_entry__global_ins_lat_snprintf(struct hist_entry *he, char *bf,
|
| 1996 | size_t size, unsigned int width)
|
| 1997 | {
|
| 1998 | return repsep_snprintf(bf, size, "%-*u", width,
|
| 1999 | he->ins_lat * he->stat.nr_events);
|
| 2000 | }
|
| 2001 |
|
| 2002 | static struct sort_entry sort_global_ins_lat = {
|
| 2003 | .se_header = "INSTR Latency",
|
| 2004 | .se_cmp = sort__ins_lat_cmp,
|
| 2005 | .se_snprintf = hist_entry__global_ins_lat_snprintf,
|
| 2006 | .se_width_idx = HISTC_GLOBAL_INS_LAT,
|
| 2007 | };
|
| 2008 |
|
| 2009 | static int64_t
|
| 2010 | sort__p_stage_cyc_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 2011 | {
|
| 2012 | return left->weight3 - right->weight3;
|
| 2013 | }
|
| 2014 |
|
| 2015 | static int hist_entry__global_p_stage_cyc_snprintf(struct hist_entry *he, char *bf,
|
| 2016 | size_t size, unsigned int width)
|
| 2017 | {
|
| 2018 | return repsep_snprintf(bf, size, "%-*u", width, he->weight3 * he->stat.nr_events);
|
| 2019 | }
|
| 2020 |
|
| 2021 |
|
| 2022 | static int hist_entry__p_stage_cyc_snprintf(struct hist_entry *he, char *bf,
|
| 2023 | size_t size, unsigned int width)
|
| 2024 | {
|
| 2025 | return repsep_snprintf(bf, size, "%-*u", width, he->weight3);
|
| 2026 | }
|
| 2027 |
|
| 2028 | static struct sort_entry sort_local_p_stage_cyc = {
|
| 2029 | .se_header = "Local Pipeline Stage Cycle",
|
| 2030 | .se_cmp = sort__p_stage_cyc_cmp,
|
| 2031 | .se_snprintf = hist_entry__p_stage_cyc_snprintf,
|
| 2032 | .se_width_idx = HISTC_LOCAL_P_STAGE_CYC,
|
| 2033 | };
|
| 2034 |
|
| 2035 | static struct sort_entry sort_global_p_stage_cyc = {
|
| 2036 | .se_header = "Pipeline Stage Cycle",
|
| 2037 | .se_cmp = sort__p_stage_cyc_cmp,
|
| 2038 | .se_snprintf = hist_entry__global_p_stage_cyc_snprintf,
|
| 2039 | .se_width_idx = HISTC_GLOBAL_P_STAGE_CYC,
|
| 2040 | };
|
| 2041 |
|
| 2042 | static struct sort_entry sort_mem_daddr_sym = {
|
| 2043 | .se_header = "Data Symbol",
|
| 2044 | .se_cmp = sort__daddr_cmp,
|
| 2045 | .se_snprintf = hist_entry__daddr_snprintf,
|
| 2046 | .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
|
| 2047 | };
|
| 2048 |
|
| 2049 | static struct sort_entry sort_mem_iaddr_sym = {
|
| 2050 | .se_header = "Code Symbol",
|
| 2051 | .se_cmp = sort__iaddr_cmp,
|
| 2052 | .se_snprintf = hist_entry__iaddr_snprintf,
|
| 2053 | .se_width_idx = HISTC_MEM_IADDR_SYMBOL,
|
| 2054 | };
|
| 2055 |
|
| 2056 | static struct sort_entry sort_mem_daddr_dso = {
|
| 2057 | .se_header = "Data Object",
|
| 2058 | .se_cmp = sort__dso_daddr_cmp,
|
| 2059 | .se_snprintf = hist_entry__dso_daddr_snprintf,
|
| 2060 | .se_width_idx = HISTC_MEM_DADDR_DSO,
|
| 2061 | };
|
| 2062 |
|
| 2063 | static struct sort_entry sort_mem_locked = {
|
| 2064 | .se_header = "Locked",
|
| 2065 | .se_cmp = sort__locked_cmp,
|
| 2066 | .se_snprintf = hist_entry__locked_snprintf,
|
| 2067 | .se_width_idx = HISTC_MEM_LOCKED,
|
| 2068 | };
|
| 2069 |
|
| 2070 | static struct sort_entry sort_mem_tlb = {
|
| 2071 | .se_header = "TLB access",
|
| 2072 | .se_cmp = sort__tlb_cmp,
|
| 2073 | .se_snprintf = hist_entry__tlb_snprintf,
|
| 2074 | .se_width_idx = HISTC_MEM_TLB,
|
| 2075 | };
|
| 2076 |
|
| 2077 | static struct sort_entry sort_mem_lvl = {
|
| 2078 | .se_header = "Memory access",
|
| 2079 | .se_cmp = sort__lvl_cmp,
|
| 2080 | .se_snprintf = hist_entry__lvl_snprintf,
|
| 2081 | .se_width_idx = HISTC_MEM_LVL,
|
| 2082 | };
|
| 2083 |
|
| 2084 | static struct sort_entry sort_mem_snoop = {
|
| 2085 | .se_header = "Snoop",
|
| 2086 | .se_cmp = sort__snoop_cmp,
|
| 2087 | .se_snprintf = hist_entry__snoop_snprintf,
|
| 2088 | .se_width_idx = HISTC_MEM_SNOOP,
|
| 2089 | };
|
| 2090 |
|
| 2091 | static struct sort_entry sort_mem_dcacheline = {
|
| 2092 | .se_header = "Data Cacheline",
|
| 2093 | .se_cmp = sort__dcacheline_cmp,
|
| 2094 | .se_snprintf = hist_entry__dcacheline_snprintf,
|
| 2095 | .se_width_idx = HISTC_MEM_DCACHELINE,
|
| 2096 | };
|
| 2097 |
|
| 2098 | static int64_t
|
| 2099 | sort__blocked_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 2100 | {
|
| 2101 | union perf_mem_data_src data_src_l;
|
| 2102 | union perf_mem_data_src data_src_r;
|
| 2103 |
|
| 2104 | if (left->mem_info)
|
| 2105 | data_src_l = *mem_info__data_src(left->mem_info);
|
| 2106 | else
|
| 2107 | data_src_l.mem_blk = PERF_MEM_BLK_NA;
|
| 2108 |
|
| 2109 | if (right->mem_info)
|
| 2110 | data_src_r = *mem_info__data_src(right->mem_info);
|
| 2111 | else
|
| 2112 | data_src_r.mem_blk = PERF_MEM_BLK_NA;
|
| 2113 |
|
| 2114 | return (int64_t)(data_src_r.mem_blk - data_src_l.mem_blk);
|
| 2115 | }
|
| 2116 |
|
| 2117 | static int hist_entry__blocked_snprintf(struct hist_entry *he, char *bf,
|
| 2118 | size_t size, unsigned int width)
|
| 2119 | {
|
| 2120 | char out[16];
|
| 2121 |
|
| 2122 | perf_mem__blk_scnprintf(out, sizeof(out), he->mem_info);
|
| 2123 | return repsep_snprintf(bf, size, "%.*s", width, out);
|
| 2124 | }
|
| 2125 |
|
| 2126 | static struct sort_entry sort_mem_blocked = {
|
| 2127 | .se_header = "Blocked",
|
| 2128 | .se_cmp = sort__blocked_cmp,
|
| 2129 | .se_snprintf = hist_entry__blocked_snprintf,
|
| 2130 | .se_width_idx = HISTC_MEM_BLOCKED,
|
| 2131 | };
|
| 2132 |
|
| 2133 | static int64_t
|
| 2134 | sort__phys_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 2135 | {
|
| 2136 | uint64_t l = 0, r = 0;
|
| 2137 |
|
| 2138 | if (left->mem_info)
|
| 2139 | l = mem_info__daddr(left->mem_info)->phys_addr;
|
| 2140 | if (right->mem_info)
|
| 2141 | r = mem_info__daddr(right->mem_info)->phys_addr;
|
| 2142 |
|
| 2143 | return (int64_t)(r - l);
|
| 2144 | }
|
| 2145 |
|
| 2146 | static int hist_entry__phys_daddr_snprintf(struct hist_entry *he, char *bf,
|
| 2147 | size_t size, unsigned int width)
|
| 2148 | {
|
| 2149 | uint64_t addr = 0;
|
| 2150 | size_t ret = 0;
|
| 2151 | size_t len = BITS_PER_LONG / 4;
|
| 2152 |
|
| 2153 | addr = mem_info__daddr(he->mem_info)->phys_addr;
|
| 2154 |
|
| 2155 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", he->level);
|
| 2156 |
|
| 2157 | ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", len, addr);
|
| 2158 |
|
| 2159 | ret += repsep_snprintf(bf + ret, size - ret, "%-*s", width - ret, "");
|
| 2160 |
|
| 2161 | if (ret > width)
|
| 2162 | bf[width] = '\0';
|
| 2163 |
|
| 2164 | return width;
|
| 2165 | }
|
| 2166 |
|
| 2167 | static struct sort_entry sort_mem_phys_daddr = {
|
| 2168 | .se_header = "Data Physical Address",
|
| 2169 | .se_cmp = sort__phys_daddr_cmp,
|
| 2170 | .se_snprintf = hist_entry__phys_daddr_snprintf,
|
| 2171 | .se_width_idx = HISTC_MEM_PHYS_DADDR,
|
| 2172 | };
|
| 2173 |
|
| 2174 | static int64_t
|
| 2175 | sort__data_page_size_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 2176 | {
|
| 2177 | uint64_t l = 0, r = 0;
|
| 2178 |
|
| 2179 | if (left->mem_info)
|
| 2180 | l = mem_info__daddr(left->mem_info)->data_page_size;
|
| 2181 | if (right->mem_info)
|
| 2182 | r = mem_info__daddr(right->mem_info)->data_page_size;
|
| 2183 |
|
| 2184 | return (int64_t)(r - l);
|
| 2185 | }
|
| 2186 |
|
| 2187 | static int hist_entry__data_page_size_snprintf(struct hist_entry *he, char *bf,
|
| 2188 | size_t size, unsigned int width)
|
| 2189 | {
|
| 2190 | char str[PAGE_SIZE_NAME_LEN];
|
| 2191 |
|
| 2192 | return repsep_snprintf(bf, size, "%-*s", width,
|
| 2193 | get_page_size_name(mem_info__daddr(he->mem_info)->data_page_size, str));
|
| 2194 | }
|
| 2195 |
|
| 2196 | static struct sort_entry sort_mem_data_page_size = {
|
| 2197 | .se_header = "Data Page Size",
|
| 2198 | .se_cmp = sort__data_page_size_cmp,
|
| 2199 | .se_snprintf = hist_entry__data_page_size_snprintf,
|
| 2200 | .se_width_idx = HISTC_MEM_DATA_PAGE_SIZE,
|
| 2201 | };
|
| 2202 |
|
| 2203 | static int64_t
|
| 2204 | sort__code_page_size_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 2205 | {
|
| 2206 | uint64_t l = left->code_page_size;
|
| 2207 | uint64_t r = right->code_page_size;
|
| 2208 |
|
| 2209 | return (int64_t)(r - l);
|
| 2210 | }
|
| 2211 |
|
| 2212 | static int hist_entry__code_page_size_snprintf(struct hist_entry *he, char *bf,
|
| 2213 | size_t size, unsigned int width)
|
| 2214 | {
|
| 2215 | char str[PAGE_SIZE_NAME_LEN];
|
| 2216 |
|
| 2217 | return repsep_snprintf(bf, size, "%-*s", width,
|
| 2218 | get_page_size_name(he->code_page_size, str));
|
| 2219 | }
|
| 2220 |
|
| 2221 | static struct sort_entry sort_code_page_size = {
|
| 2222 | .se_header = "Code Page Size",
|
| 2223 | .se_cmp = sort__code_page_size_cmp,
|
| 2224 | .se_snprintf = hist_entry__code_page_size_snprintf,
|
| 2225 | .se_width_idx = HISTC_CODE_PAGE_SIZE,
|
| 2226 | };
|
| 2227 |
|
| 2228 | static int64_t
|
| 2229 | sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 2230 | {
|
| 2231 | if (!left->branch_info || !right->branch_info)
|
| 2232 | return cmp_null(left->branch_info, right->branch_info);
|
| 2233 |
|
| 2234 | return left->branch_info->flags.abort !=
|
| 2235 | right->branch_info->flags.abort;
|
| 2236 | }
|
| 2237 |
|
| 2238 | static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf,
|
| 2239 | size_t size, unsigned int width)
|
| 2240 | {
|
| 2241 | static const char *out = "N/A";
|
| 2242 |
|
| 2243 | if (he->branch_info) {
|
| 2244 | if (he->branch_info->flags.abort)
|
| 2245 | out = "A";
|
| 2246 | else
|
| 2247 | out = ".";
|
| 2248 | }
|
| 2249 |
|
| 2250 | return repsep_snprintf(bf, size, "%-*s", width, out);
|
| 2251 | }
|
| 2252 |
|
| 2253 | static struct sort_entry sort_abort = {
|
| 2254 | .se_header = "Transaction abort",
|
| 2255 | .se_cmp = sort__abort_cmp,
|
| 2256 | .se_snprintf = hist_entry__abort_snprintf,
|
| 2257 | .se_width_idx = HISTC_ABORT,
|
| 2258 | };
|
| 2259 |
|
| 2260 | static int64_t
|
| 2261 | sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 2262 | {
|
| 2263 | if (!left->branch_info || !right->branch_info)
|
| 2264 | return cmp_null(left->branch_info, right->branch_info);
|
| 2265 |
|
| 2266 | return left->branch_info->flags.in_tx !=
|
| 2267 | right->branch_info->flags.in_tx;
|
| 2268 | }
|
| 2269 |
|
| 2270 | static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf,
|
| 2271 | size_t size, unsigned int width)
|
| 2272 | {
|
| 2273 | static const char *out = "N/A";
|
| 2274 |
|
| 2275 | if (he->branch_info) {
|
| 2276 | if (he->branch_info->flags.in_tx)
|
| 2277 | out = "T";
|
| 2278 | else
|
| 2279 | out = ".";
|
| 2280 | }
|
| 2281 |
|
| 2282 | return repsep_snprintf(bf, size, "%-*s", width, out);
|
| 2283 | }
|
| 2284 |
|
| 2285 | static struct sort_entry sort_in_tx = {
|
| 2286 | .se_header = "Branch in transaction",
|
| 2287 | .se_cmp = sort__in_tx_cmp,
|
| 2288 | .se_snprintf = hist_entry__in_tx_snprintf,
|
| 2289 | .se_width_idx = HISTC_IN_TX,
|
| 2290 | };
|
| 2291 |
|
| 2292 | static int64_t
|
| 2293 | sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 2294 | {
|
| 2295 | return left->transaction - right->transaction;
|
| 2296 | }
|
| 2297 |
|
| 2298 | static inline char *add_str(char *p, const char *str)
|
| 2299 | {
|
| 2300 | strcpy(p, str);
|
| 2301 | return p + strlen(str);
|
| 2302 | }
|
| 2303 |
|
| 2304 | static struct txbit {
|
| 2305 | unsigned flag;
|
| 2306 | const char *name;
|
| 2307 | int skip_for_len;
|
| 2308 | } txbits[] = {
|
| 2309 | { PERF_TXN_ELISION, "EL ", 0 },
|
| 2310 | { PERF_TXN_TRANSACTION, "TX ", 1 },
|
| 2311 | { PERF_TXN_SYNC, "SYNC ", 1 },
|
| 2312 | { PERF_TXN_ASYNC, "ASYNC ", 0 },
|
| 2313 | { PERF_TXN_RETRY, "RETRY ", 0 },
|
| 2314 | { PERF_TXN_CONFLICT, "CON ", 0 },
|
| 2315 | { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 },
|
| 2316 | { PERF_TXN_CAPACITY_READ, "CAP-READ ", 0 },
|
| 2317 | { 0, NULL, 0 }
|
| 2318 | };
|
| 2319 |
|
| 2320 | int hist_entry__transaction_len(void)
|
| 2321 | {
|
| 2322 | int i;
|
| 2323 | int len = 0;
|
| 2324 |
|
| 2325 | for (i = 0; txbits[i].name; i++) {
|
| 2326 | if (!txbits[i].skip_for_len)
|
| 2327 | len += strlen(txbits[i].name);
|
| 2328 | }
|
| 2329 | len += 4;
|
| 2330 | return len;
|
| 2331 | }
|
| 2332 |
|
| 2333 | static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf,
|
| 2334 | size_t size, unsigned int width)
|
| 2335 | {
|
| 2336 | u64 t = he->transaction;
|
| 2337 | char buf[128];
|
| 2338 | char *p = buf;
|
| 2339 | int i;
|
| 2340 |
|
| 2341 | buf[0] = 0;
|
| 2342 | for (i = 0; txbits[i].name; i++)
|
| 2343 | if (txbits[i].flag & t)
|
| 2344 | p = add_str(p, txbits[i].name);
|
| 2345 | if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC)))
|
| 2346 | p = add_str(p, "NEITHER ");
|
| 2347 | if (t & PERF_TXN_ABORT_MASK) {
|
| 2348 | sprintf(p, ":%" PRIx64,
|
| 2349 | (t & PERF_TXN_ABORT_MASK) >>
|
| 2350 | PERF_TXN_ABORT_SHIFT);
|
| 2351 | p += strlen(p);
|
| 2352 | }
|
| 2353 |
|
| 2354 | return repsep_snprintf(bf, size, "%-*s", width, buf);
|
| 2355 | }
|
| 2356 |
|
| 2357 | static struct sort_entry sort_transaction = {
|
| 2358 | .se_header = "Transaction ",
|
| 2359 | .se_cmp = sort__transaction_cmp,
|
| 2360 | .se_snprintf = hist_entry__transaction_snprintf,
|
| 2361 | .se_width_idx = HISTC_TRANSACTION,
|
| 2362 | };
|
| 2363 |
|
| 2364 |
|
| 2365 |
|
| 2366 | static int64_t _sort__sym_size_cmp(struct symbol *sym_l, struct symbol *sym_r)
|
| 2367 | {
|
| 2368 | int64_t size_l = sym_l != NULL ? symbol__size(sym_l) : 0;
|
| 2369 | int64_t size_r = sym_r != NULL ? symbol__size(sym_r) : 0;
|
| 2370 |
|
| 2371 | return size_l < size_r ? -1 :
|
| 2372 | size_l == size_r ? 0 : 1;
|
| 2373 | }
|
| 2374 |
|
| 2375 | static int64_t
|
| 2376 | sort__sym_size_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 2377 | {
|
| 2378 | return _sort__sym_size_cmp(right->ms.sym, left->ms.sym);
|
| 2379 | }
|
| 2380 |
|
| 2381 | static int _hist_entry__sym_size_snprintf(struct symbol *sym, char *bf,
|
| 2382 | size_t bf_size, unsigned int width)
|
| 2383 | {
|
| 2384 | if (sym)
|
| 2385 | return repsep_snprintf(bf, bf_size, "%*d", width, symbol__size(sym));
|
| 2386 |
|
| 2387 | return repsep_snprintf(bf, bf_size, "%*s", width, "unknown");
|
| 2388 | }
|
| 2389 |
|
| 2390 | static int hist_entry__sym_size_snprintf(struct hist_entry *he, char *bf,
|
| 2391 | size_t size, unsigned int width)
|
| 2392 | {
|
| 2393 | return _hist_entry__sym_size_snprintf(he->ms.sym, bf, size, width);
|
| 2394 | }
|
| 2395 |
|
| 2396 | static struct sort_entry sort_sym_size = {
|
| 2397 | .se_header = "Symbol size",
|
| 2398 | .se_cmp = sort__sym_size_cmp,
|
| 2399 | .se_snprintf = hist_entry__sym_size_snprintf,
|
| 2400 | .se_width_idx = HISTC_SYM_SIZE,
|
| 2401 | };
|
| 2402 |
|
| 2403 |
|
| 2404 |
|
| 2405 | static int64_t _sort__dso_size_cmp(struct map *map_l, struct map *map_r)
|
| 2406 | {
|
| 2407 | int64_t size_l = map_l != NULL ? map__size(map_l) : 0;
|
| 2408 | int64_t size_r = map_r != NULL ? map__size(map_r) : 0;
|
| 2409 |
|
| 2410 | return size_l < size_r ? -1 :
|
| 2411 | size_l == size_r ? 0 : 1;
|
| 2412 | }
|
| 2413 |
|
| 2414 | static int64_t
|
| 2415 | sort__dso_size_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 2416 | {
|
| 2417 | return _sort__dso_size_cmp(right->ms.map, left->ms.map);
|
| 2418 | }
|
| 2419 |
|
| 2420 | static int _hist_entry__dso_size_snprintf(struct map *map, char *bf,
|
| 2421 | size_t bf_size, unsigned int width)
|
| 2422 | {
|
| 2423 | if (map && map__dso(map))
|
| 2424 | return repsep_snprintf(bf, bf_size, "%*d", width, map__size(map));
|
| 2425 |
|
| 2426 | return repsep_snprintf(bf, bf_size, "%*s", width, "unknown");
|
| 2427 | }
|
| 2428 |
|
| 2429 | static int hist_entry__dso_size_snprintf(struct hist_entry *he, char *bf,
|
| 2430 | size_t size, unsigned int width)
|
| 2431 | {
|
| 2432 | return _hist_entry__dso_size_snprintf(he->ms.map, bf, size, width);
|
| 2433 | }
|
| 2434 |
|
| 2435 | static struct sort_entry sort_dso_size = {
|
| 2436 | .se_header = "DSO size",
|
| 2437 | .se_cmp = sort__dso_size_cmp,
|
| 2438 | .se_snprintf = hist_entry__dso_size_snprintf,
|
| 2439 | .se_width_idx = HISTC_DSO_SIZE,
|
| 2440 | };
|
| 2441 |
|
| 2442 |
|
| 2443 |
|
| 2444 | static int64_t
|
| 2445 | sort__addr_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 2446 | {
|
| 2447 | u64 left_ip = left->ip;
|
| 2448 | u64 right_ip = right->ip;
|
| 2449 | struct map *left_map = left->ms.map;
|
| 2450 | struct map *right_map = right->ms.map;
|
| 2451 |
|
| 2452 | if (left_map)
|
| 2453 | left_ip = map__unmap_ip(left_map, left_ip);
|
| 2454 | if (right_map)
|
| 2455 | right_ip = map__unmap_ip(right_map, right_ip);
|
| 2456 |
|
| 2457 | return _sort__addr_cmp(left_ip, right_ip);
|
| 2458 | }
|
| 2459 |
|
| 2460 | static int hist_entry__addr_snprintf(struct hist_entry *he, char *bf,
|
| 2461 | size_t size, unsigned int width)
|
| 2462 | {
|
| 2463 | u64 ip = he->ip;
|
| 2464 | struct map *map = he->ms.map;
|
| 2465 |
|
| 2466 | if (map)
|
| 2467 | ip = map__unmap_ip(map, ip);
|
| 2468 |
|
| 2469 | return repsep_snprintf(bf, size, "%-#*llx", width, ip);
|
| 2470 | }
|
| 2471 |
|
| 2472 | static struct sort_entry sort_addr = {
|
| 2473 | .se_header = "Address",
|
| 2474 | .se_cmp = sort__addr_cmp,
|
| 2475 | .se_snprintf = hist_entry__addr_snprintf,
|
| 2476 | .se_width_idx = HISTC_ADDR,
|
| 2477 | };
|
| 2478 |
|
| 2479 |
|
| 2480 |
|
| 2481 | struct annotated_data_type unknown_type = {
|
| 2482 | .self = {
|
| 2483 | .type_name = (char *)"(unknown)",
|
| 2484 | .children = LIST_HEAD_INIT(unknown_type.self.children),
|
| 2485 | },
|
| 2486 | };
|
| 2487 |
|
| 2488 | static int64_t
|
| 2489 | sort__type_cmp(struct hist_entry *left, struct hist_entry *right)
|
| 2490 | {
|
| 2491 | return sort__addr_cmp(left, right);
|
| 2492 | }
|
| 2493 |
|
| 2494 | static void sort__type_init(struct hist_entry *he)
|
| 2495 | {
|
| 2496 | if (he->mem_type)
|
| 2497 | return;
|
| 2498 |
|
| 2499 | he->mem_type = hist_entry__get_data_type(he);
|
| 2500 | if (he->mem_type == NULL) {
|
| 2501 | he->mem_type = &unknown_type;
|
| 2502 | he->mem_type_off = 0;
|
| 2503 | }
|
| 2504 | }
|
| 2505 |
|
| 2506 | static int64_t
|
| 2507 | sort__type_collapse(struct hist_entry *left, struct hist_entry *right)
|
| 2508 | {
|
| 2509 | struct annotated_data_type *left_type = left->mem_type;
|
| 2510 | struct annotated_data_type *right_type = right->mem_type;
|
| 2511 |
|
| 2512 | if (!left_type) {
|
| 2513 | sort__type_init(left);
|
| 2514 | left_type = left->mem_type;
|
| 2515 | }
|
| 2516 |
|
| 2517 | if (!right_type) {
|
| 2518 | sort__type_init(right);
|
| 2519 | right_type = right->mem_type;
|
| 2520 | }
|
| 2521 |
|
| 2522 | return strcmp(left_type->self.type_name, right_type->self.type_name);
|
| 2523 | }
|
| 2524 |
|
| 2525 | static int64_t
|
| 2526 | sort__type_sort(struct hist_entry *left, struct hist_entry *right)
|
| 2527 | {
|
| 2528 | return sort__type_collapse(left, right);
|
| 2529 | }
|
| 2530 |
|
| 2531 | static int hist_entry__type_snprintf(struct hist_entry *he, char *bf,
|
| 2532 | size_t size, unsigned int width)
|
| 2533 | {
|
| 2534 | return repsep_snprintf(bf, size, "%-*s", width, he->mem_type->self.type_name);
|
| 2535 | }
|
| 2536 |
|
| 2537 | struct sort_entry sort_type = {
|
| 2538 | .se_header = "Data Type",
|
| 2539 | .se_cmp = sort__type_cmp,
|
| 2540 | .se_collapse = sort__type_collapse,
|
| 2541 | .se_sort = sort__type_sort,
|
| 2542 | .se_init = sort__type_init,
|
| 2543 | .se_snprintf = hist_entry__type_snprintf,
|
| 2544 | .se_width_idx = HISTC_TYPE,
|
| 2545 | };
|
| 2546 |
|
| 2547 |
|
| 2548 |
|
| 2549 | static int64_t
|
| 2550 | sort__typeoff_sort(struct hist_entry *left, struct hist_entry *right)
|
| 2551 | {
|
| 2552 | struct annotated_data_type *left_type = left->mem_type;
|
| 2553 | struct annotated_data_type *right_type = right->mem_type;
|
| 2554 | int64_t ret;
|
| 2555 |
|
| 2556 | if (!left_type) {
|
| 2557 | sort__type_init(left);
|
| 2558 | left_type = left->mem_type;
|
| 2559 | }
|
| 2560 |
|
| 2561 | if (!right_type) {
|
| 2562 | sort__type_init(right);
|
| 2563 | right_type = right->mem_type;
|
| 2564 | }
|
| 2565 |
|
| 2566 | ret = strcmp(left_type->self.type_name, right_type->self.type_name);
|
| 2567 | if (ret)
|
| 2568 | return ret;
|
| 2569 | return left->mem_type_off - right->mem_type_off;
|
| 2570 | }
|
| 2571 |
|
| 2572 | static int hist_entry__typeoff_snprintf(struct hist_entry *he, char *bf,
|
| 2573 | size_t size, unsigned int width __maybe_unused)
|
| 2574 | {
|
| 2575 | struct annotated_data_type *he_type = he->mem_type;
|
| 2576 | char buf[4096];
|
| 2577 |
|
| 2578 | if (he_type == &unknown_type || he_type == &stackop_type ||
|
| 2579 | he_type == &canary_type)
|
| 2580 | return repsep_snprintf(bf, size, "%s", he_type->self.type_name);
|
| 2581 |
|
| 2582 | if (!annotated_data_type__get_member_name(he_type, buf, sizeof(buf),
|
| 2583 | he->mem_type_off))
|
| 2584 | scnprintf(buf, sizeof(buf), "no field");
|
| 2585 |
|
| 2586 | return repsep_snprintf(bf, size, "%s +%#x (%s)", he_type->self.type_name,
|
| 2587 | he->mem_type_off, buf);
|
| 2588 | }
|
| 2589 |
|
| 2590 | static struct sort_entry sort_type_offset = {
|
| 2591 | .se_header = "Data Type Offset",
|
| 2592 | .se_cmp = sort__type_cmp,
|
| 2593 | .se_collapse = sort__typeoff_sort,
|
| 2594 | .se_sort = sort__typeoff_sort,
|
| 2595 | .se_init = sort__type_init,
|
| 2596 | .se_snprintf = hist_entry__typeoff_snprintf,
|
| 2597 | .se_width_idx = HISTC_TYPE_OFFSET,
|
| 2598 | };
|
| 2599 |
|
| 2600 |
|
| 2601 |
|
| 2602 | static int
|
| 2603 | hist_entry__cln_size(struct hist_entry *he)
|
| 2604 | {
|
| 2605 | int ret = 0;
|
| 2606 |
|
| 2607 | if (he && he->hists) {
|
| 2608 | struct evsel *evsel = hists_to_evsel(he->hists);
|
| 2609 |
|
| 2610 | if (evsel) {
|
| 2611 | struct perf_session *session = evsel__session(evsel);
|
| 2612 |
|
| 2613 | ret = session->header.env.cln_size;
|
| 2614 | }
|
| 2615 | }
|
| 2616 |
|
| 2617 | if (ret < 1)
|
| 2618 | ret = DEFAULT_CACHELINE_SIZE; |
| 2619 |
|
| 2620 | return ret;
|
| 2621 | }
|
| 2622 |
|
| 2623 | static int64_t
|
| 2624 | sort__typecln_sort(struct hist_entry *left, struct hist_entry *right)
|
| 2625 | {
|
| 2626 | struct annotated_data_type *left_type = left->mem_type;
|
| 2627 | struct annotated_data_type *right_type = right->mem_type;
|
| 2628 | int64_t left_cln, right_cln;
|
| 2629 | int64_t cln_size_left = hist_entry__cln_size(left);
|
| 2630 | int64_t cln_size_right = hist_entry__cln_size(right);
|
| 2631 | int64_t ret;
|
| 2632 |
|
| 2633 | if (!left_type) {
|
| 2634 | sort__type_init(left);
|
| 2635 | left_type = left->mem_type;
|
| 2636 | }
|
| 2637 |
|
| 2638 | if (!right_type) {
|
| 2639 | sort__type_init(right);
|
| 2640 | right_type = right->mem_type;
|
| 2641 | }
|
| 2642 |
|
| 2643 | ret = strcmp(left_type->self.type_name, right_type->self.type_name);
|
| 2644 | if (ret)
|
| 2645 | return ret;
|
| 2646 |
|
| 2647 | left_cln = left->mem_type_off / cln_size_left;
|
| 2648 | right_cln = right->mem_type_off / cln_size_right;
|
| 2649 | return left_cln - right_cln;
|
| 2650 | }
|
| 2651 |
|
| 2652 | static int hist_entry__typecln_snprintf(struct hist_entry *he, char *bf,
|
| 2653 | size_t size, unsigned int width __maybe_unused)
|
| 2654 | {
|
| 2655 | struct annotated_data_type *he_type = he->mem_type;
|
| 2656 | int cln_size = hist_entry__cln_size(he);
|
| 2657 |
|
| 2658 | return repsep_snprintf(bf, size, "%s: cache-line %d", he_type->self.type_name,
|
| 2659 | he->mem_type_off / cln_size);
|
| 2660 | }
|
| 2661 |
|
| 2662 | static struct sort_entry sort_type_cacheline = {
|
| 2663 | .se_header = "Data Type Cacheline",
|
| 2664 | .se_cmp = sort__type_cmp,
|
| 2665 | .se_collapse = sort__typecln_sort,
|
| 2666 | .se_sort = sort__typecln_sort,
|
| 2667 | .se_init = sort__type_init,
|
| 2668 | .se_snprintf = hist_entry__typecln_snprintf,
|
| 2669 | .se_width_idx = HISTC_TYPE_CACHELINE,
|
| 2670 | };
|
| 2671 |
|
| 2672 |
|
| 2673 | struct sort_dimension {
|
| 2674 | const char *name;
|
| 2675 | struct sort_entry *entry;
|
| 2676 | int taken;
|
| 2677 | };
|
| 2678 |
|
| 2679 | static int arch_support_sort_key(const char *sort_key, struct perf_env *env)
|
| 2680 | {
|
| 2681 | uint16_t e_machine = perf_env__e_machine(env, NULL);
|
| 2682 |
|
| 2683 | if (e_machine == EM_X86_64 || e_machine == EM_386 || e_machine == EM_PPC64 ||
|
| 2684 | e_machine == EM_PPC) {
|
| 2685 | if (!strcmp(sort_key, "p_stage_cyc"))
|
| 2686 | return 1;
|
| 2687 | if (!strcmp(sort_key, "local_p_stage_cyc"))
|
| 2688 | return 1;
|
| 2689 | }
|
| 2690 | return 0;
|
| 2691 | }
|
| 2692 |
|
| 2693 | static const char *arch_perf_header_entry(const char *se_header, struct perf_env *env)
|
| 2694 | {
|
| 2695 | uint16_t e_machine = perf_env__e_machine(env, NULL);
|
| 2696 |
|
| 2697 | if (e_machine == EM_X86_64 || e_machine == EM_386) {
|
| 2698 | if (!strcmp(se_header, "Local Pipeline Stage Cycle"))
|
| 2699 | return "Local Retire Latency";
|
| 2700 | else if (!strcmp(se_header, "Pipeline Stage Cycle"))
|
| 2701 | return "Retire Latency";
|
| 2702 | } else if (e_machine == EM_PPC64 || e_machine == EM_PPC) {
|
| 2703 | if (!strcmp(se_header, "Local INSTR Latency"))
|
| 2704 | return "Finish Cyc";
|
| 2705 | else if (!strcmp(se_header, "INSTR Latency"))
|
| 2706 | return "Global Finish_cyc";
|
| 2707 | else if (!strcmp(se_header, "Local Pipeline Stage Cycle"))
|
| 2708 | return "Dispatch Cyc";
|
| 2709 | else if (!strcmp(se_header, "Pipeline Stage Cycle"))
|
| 2710 | return "Global Dispatch_cyc";
|
| 2711 | }
|
| 2712 | return se_header;
|
| 2713 | }
|
| 2714 |
|
| 2715 | static void sort_dimension_add_dynamic_header(struct sort_dimension *sd, struct perf_env *env)
|
| 2716 | {
|
| 2717 | sd->entry->se_header = arch_perf_header_entry(sd->entry->se_header, env);
|
| 2718 | }
|
| 2719 |
|
| 2720 | #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
|
| 2721 |
|
| 2722 | static struct sort_dimension common_sort_dimensions[] = {
|
| 2723 | DIM(SORT_PID, "pid", sort_thread),
|
| 2724 | DIM(SORT_TGID, "tgid", sort_tgid),
|
| 2725 | DIM(SORT_COMM, "comm", sort_comm),
|
| 2726 | DIM(SORT_COMM_NODIGIT, "comm_nodigit", sort_comm_nodigit),
|
| 2727 | DIM(SORT_DSO, "dso", sort_dso),
|
| 2728 | DIM(SORT_SYM, "symbol", sort_sym),
|
| 2729 | DIM(SORT_PARENT, "parent", sort_parent),
|
| 2730 | DIM(SORT_CPU, "cpu", sort_cpu),
|
| 2731 | DIM(SORT_SOCKET, "socket", sort_socket),
|
| 2732 | DIM(SORT_SRCLINE, "srcline", sort_srcline),
|
| 2733 | DIM(SORT_SRCFILE, "srcfile", sort_srcfile),
|
| 2734 | DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
|
| 2735 | DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
|
| 2736 | DIM(SORT_TRANSACTION, "transaction", sort_transaction),
|
| 2737 | #ifdef HAVE_LIBTRACEEVENT
|
| 2738 | DIM(SORT_TRACE, "trace", sort_trace),
|
| 2739 | #endif
|
| 2740 | DIM(SORT_SYM_SIZE, "symbol_size", sort_sym_size),
|
| 2741 | DIM(SORT_DSO_SIZE, "dso_size", sort_dso_size),
|
| 2742 | DIM(SORT_CGROUP, "cgroup", sort_cgroup),
|
| 2743 | DIM(SORT_CGROUP_ID, "cgroup_id", sort_cgroup_id),
|
| 2744 | DIM(SORT_SYM_IPC_NULL, "ipc_null", sort_sym_ipc_null),
|
| 2745 | DIM(SORT_TIME, "time", sort_time),
|
| 2746 | DIM(SORT_CODE_PAGE_SIZE, "code_page_size", sort_code_page_size),
|
| 2747 | DIM(SORT_LOCAL_INS_LAT, "local_ins_lat", sort_local_ins_lat),
|
| 2748 | DIM(SORT_GLOBAL_INS_LAT, "ins_lat", sort_global_ins_lat),
|
| 2749 | DIM(SORT_LOCAL_PIPELINE_STAGE_CYC, "local_p_stage_cyc", sort_local_p_stage_cyc),
|
| 2750 | DIM(SORT_GLOBAL_PIPELINE_STAGE_CYC, "p_stage_cyc", sort_global_p_stage_cyc),
|
| 2751 | DIM(SORT_ADDR, "addr", sort_addr),
|
| 2752 | DIM(SORT_LOCAL_RETIRE_LAT, "local_retire_lat", sort_local_p_stage_cyc),
|
| 2753 | DIM(SORT_GLOBAL_RETIRE_LAT, "retire_lat", sort_global_p_stage_cyc),
|
| 2754 | DIM(SORT_SIMD, "simd", sort_simd),
|
| 2755 | DIM(SORT_ANNOTATE_DATA_TYPE, "type", sort_type),
|
| 2756 | DIM(SORT_ANNOTATE_DATA_TYPE_OFFSET, "typeoff", sort_type_offset),
|
| 2757 | DIM(SORT_SYM_OFFSET, "symoff", sort_sym_offset),
|
| 2758 | DIM(SORT_ANNOTATE_DATA_TYPE_CACHELINE, "typecln", sort_type_cacheline),
|
| 2759 | DIM(SORT_PARALLELISM, "parallelism", sort_parallelism),
|
| 2760 | };
|
| 2761 |
|
| 2762 | #undef DIM
|
| 2763 |
|
| 2764 | #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
|
| 2765 |
|
| 2766 | static struct sort_dimension bstack_sort_dimensions[] = {
|
| 2767 | DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
|
| 2768 | DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
|
| 2769 | DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
|
| 2770 | DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
|
| 2771 | DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
|
| 2772 | DIM(SORT_IN_TX, "in_tx", sort_in_tx),
|
| 2773 | DIM(SORT_ABORT, "abort", sort_abort),
|
| 2774 | DIM(SORT_CYCLES, "cycles", sort_cycles),
|
| 2775 | DIM(SORT_SRCLINE_FROM, "srcline_from", sort_srcline_from),
|
| 2776 | DIM(SORT_SRCLINE_TO, "srcline_to", sort_srcline_to),
|
| 2777 | DIM(SORT_SYM_IPC, "ipc_lbr", sort_sym_ipc),
|
| 2778 | DIM(SORT_ADDR_FROM, "addr_from", sort_addr_from),
|
| 2779 | DIM(SORT_ADDR_TO, "addr_to", sort_addr_to),
|
| 2780 | DIM(SORT_CALLCHAIN_BRANCH_PREDICTED,
|
| 2781 | "callchain_branch_predicted",
|
| 2782 | sort_callchain_branch_predicted),
|
| 2783 | DIM(SORT_CALLCHAIN_BRANCH_ABORT,
|
| 2784 | "callchain_branch_abort",
|
| 2785 | sort_callchain_branch_abort),
|
| 2786 | DIM(SORT_CALLCHAIN_BRANCH_CYCLES,
|
| 2787 | "callchain_branch_cycles",
|
| 2788 | sort_callchain_branch_cycles)
|
| 2789 | };
|
| 2790 |
|
| 2791 | #undef DIM
|
| 2792 |
|
| 2793 | #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
|
| 2794 |
|
| 2795 | static struct sort_dimension memory_sort_dimensions[] = {
|
| 2796 | DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
|
| 2797 | DIM(SORT_MEM_IADDR_SYMBOL, "symbol_iaddr", sort_mem_iaddr_sym),
|
| 2798 | DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
|
| 2799 | DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
|
| 2800 | DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
|
| 2801 | DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
|
| 2802 | DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
|
| 2803 | DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline),
|
| 2804 | DIM(SORT_MEM_PHYS_DADDR, "phys_daddr", sort_mem_phys_daddr),
|
| 2805 | DIM(SORT_MEM_DATA_PAGE_SIZE, "data_page_size", sort_mem_data_page_size),
|
| 2806 | DIM(SORT_MEM_BLOCKED, "blocked", sort_mem_blocked),
|
| 2807 | };
|
| 2808 |
|
| 2809 | #undef DIM
|
| 2810 |
|
| 2811 | struct hpp_dimension {
|
| 2812 | const char *name;
|
| 2813 | struct perf_hpp_fmt *fmt;
|
| 2814 | int taken;
|
| 2815 | int was_taken;
|
| 2816 | int mem_mode;
|
| 2817 | };
|
| 2818 |
|
| 2819 | #define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
|
| 2820 | #define DIM_MEM(d, n) { .name = n, .fmt = &perf_hpp__format[d], .mem_mode = 1, }
|
| 2821 |
|
| 2822 | static struct hpp_dimension hpp_sort_dimensions[] = {
|
| 2823 | DIM(PERF_HPP__OVERHEAD, "overhead"),
|
| 2824 | DIM(PERF_HPP__LATENCY, "latency"),
|
| 2825 | DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
|
| 2826 | DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
|
| 2827 | DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
|
| 2828 | DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
|
| 2829 | DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"),
|
| 2830 | DIM(PERF_HPP__LATENCY_ACC, "latency_children"),
|
| 2831 | DIM(PERF_HPP__SAMPLES, "sample"),
|
| 2832 | DIM(PERF_HPP__PERIOD, "period"),
|
| 2833 | DIM(PERF_HPP__WEIGHT1, "weight1"),
|
| 2834 | DIM(PERF_HPP__WEIGHT2, "weight2"),
|
| 2835 | DIM(PERF_HPP__WEIGHT3, "weight3"),
|
| 2836 |
|
| 2837 | DIM(PERF_HPP__WEIGHT2, "ins_lat"),
|
| 2838 | DIM(PERF_HPP__WEIGHT3, "retire_lat"),
|
| 2839 | DIM(PERF_HPP__WEIGHT3, "p_stage_cyc"),
|
| 2840 |
|
| 2841 | DIM_MEM(PERF_HPP__MEM_STAT_OP, "op"),
|
| 2842 | DIM_MEM(PERF_HPP__MEM_STAT_CACHE, "cache"),
|
| 2843 | DIM_MEM(PERF_HPP__MEM_STAT_MEMORY, "memory"),
|
| 2844 | DIM_MEM(PERF_HPP__MEM_STAT_SNOOP, "snoop"),
|
| 2845 | DIM_MEM(PERF_HPP__MEM_STAT_DTLB, "dtlb"),
|
| 2846 | };
|
| 2847 |
|
| 2848 | #undef DIM_MEM
|
| 2849 | #undef DIM
|
| 2850 |
|
| 2851 | struct hpp_sort_entry {
|
| 2852 | struct perf_hpp_fmt hpp;
|
| 2853 | struct sort_entry *se;
|
| 2854 | };
|
| 2855 |
|
| 2856 | void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists)
|
| 2857 | {
|
| 2858 | struct hpp_sort_entry *hse;
|
| 2859 |
|
| 2860 | if (!perf_hpp__is_sort_entry(fmt))
|
| 2861 | return;
|
| 2862 |
|
| 2863 | hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
| 2864 | hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name));
|
| 2865 | }
|
| 2866 |
|
| 2867 | static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
| 2868 | struct hists *hists, int line,
|
| 2869 | int *span __maybe_unused)
|
| 2870 | {
|
| 2871 | struct hpp_sort_entry *hse;
|
| 2872 | size_t len = fmt->user_len;
|
| 2873 | const char *hdr = "";
|
| 2874 |
|
| 2875 | if (line == hists->hpp_list->nr_header_lines - 1)
|
| 2876 | hdr = fmt->name;
|
| 2877 |
|
| 2878 | hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
| 2879 |
|
| 2880 | if (!len)
|
| 2881 | len = hists__col_len(hists, hse->se->se_width_idx);
|
| 2882 |
|
| 2883 | return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, hdr);
|
| 2884 | }
|
| 2885 |
|
| 2886 | static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
|
| 2887 | struct perf_hpp *hpp __maybe_unused,
|
| 2888 | struct hists *hists)
|
| 2889 | {
|
| 2890 | struct hpp_sort_entry *hse;
|
| 2891 | size_t len = fmt->user_len;
|
| 2892 |
|
| 2893 | hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
| 2894 |
|
| 2895 | if (!len)
|
| 2896 | len = hists__col_len(hists, hse->se->se_width_idx);
|
| 2897 |
|
| 2898 | return len;
|
| 2899 | }
|
| 2900 |
|
| 2901 | static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
| 2902 | struct hist_entry *he)
|
| 2903 | {
|
| 2904 | struct hpp_sort_entry *hse;
|
| 2905 | size_t len = fmt->user_len;
|
| 2906 |
|
| 2907 | hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
| 2908 |
|
| 2909 | if (!len)
|
| 2910 | len = hists__col_len(he->hists, hse->se->se_width_idx);
|
| 2911 |
|
| 2912 | return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
|
| 2913 | }
|
| 2914 |
|
| 2915 | static int64_t __sort__hpp_cmp(struct perf_hpp_fmt *fmt,
|
| 2916 | struct hist_entry *a, struct hist_entry *b)
|
| 2917 | {
|
| 2918 | struct hpp_sort_entry *hse;
|
| 2919 |
|
| 2920 | hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
| 2921 | return hse->se->se_cmp(a, b);
|
| 2922 | }
|
| 2923 |
|
| 2924 | static int64_t __sort__hpp_collapse(struct perf_hpp_fmt *fmt,
|
| 2925 | struct hist_entry *a, struct hist_entry *b)
|
| 2926 | {
|
| 2927 | struct hpp_sort_entry *hse;
|
| 2928 | int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
|
| 2929 |
|
| 2930 | hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
| 2931 | collapse_fn = hse->se->se_collapse ?: hse->se->se_cmp;
|
| 2932 | return collapse_fn(a, b);
|
| 2933 | }
|
| 2934 |
|
| 2935 | static int64_t __sort__hpp_sort(struct perf_hpp_fmt *fmt,
|
| 2936 | struct hist_entry *a, struct hist_entry *b)
|
| 2937 | {
|
| 2938 | struct hpp_sort_entry *hse;
|
| 2939 | int64_t (*sort_fn)(struct hist_entry *, struct hist_entry *);
|
| 2940 |
|
| 2941 | hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
| 2942 | sort_fn = hse->se->se_sort ?: hse->se->se_cmp;
|
| 2943 | return sort_fn(a, b);
|
| 2944 | }
|
| 2945 |
|
| 2946 | bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
|
| 2947 | {
|
| 2948 | return format->header == __sort__hpp_header;
|
| 2949 | }
|
| 2950 |
|
| 2951 | #define MK_SORT_ENTRY_CHK(key) \
|
| 2952 | bool perf_hpp__is_ ## key ## _entry(struct perf_hpp_fmt *fmt) \
|
| 2953 | { \
|
| 2954 | struct hpp_sort_entry *hse; \
|
| 2955 | \
|
| 2956 | if (!perf_hpp__is_sort_entry(fmt)) \
|
| 2957 | return false; \
|
| 2958 | \
|
| 2959 | hse = container_of(fmt, struct hpp_sort_entry, hpp); \
|
| 2960 | return hse->se == &sort_ ## key ; \
|
| 2961 | }
|
| 2962 |
|
| 2963 | #ifdef HAVE_LIBTRACEEVENT
|
| 2964 | MK_SORT_ENTRY_CHK(trace)
|
| 2965 | #else
|
| 2966 | bool perf_hpp__is_trace_entry(struct perf_hpp_fmt *fmt __maybe_unused)
|
| 2967 | {
|
| 2968 | return false;
|
| 2969 | }
|
| 2970 | #endif
|
| 2971 | MK_SORT_ENTRY_CHK(srcline)
|
| 2972 | MK_SORT_ENTRY_CHK(srcfile)
|
| 2973 | MK_SORT_ENTRY_CHK(thread)
|
| 2974 | MK_SORT_ENTRY_CHK(comm)
|
| 2975 | MK_SORT_ENTRY_CHK(dso)
|
| 2976 | MK_SORT_ENTRY_CHK(sym)
|
| 2977 | MK_SORT_ENTRY_CHK(parallelism)
|
| 2978 |
|
| 2979 |
|
| 2980 | static bool __sort__hpp_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
|
| 2981 | {
|
| 2982 | struct hpp_sort_entry *hse_a;
|
| 2983 | struct hpp_sort_entry *hse_b;
|
| 2984 |
|
| 2985 | if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
|
| 2986 | return false;
|
| 2987 |
|
| 2988 | hse_a = container_of(a, struct hpp_sort_entry, hpp);
|
| 2989 | hse_b = container_of(b, struct hpp_sort_entry, hpp);
|
| 2990 |
|
| 2991 | return hse_a->se == hse_b->se;
|
| 2992 | }
|
| 2993 |
|
| 2994 | static void hse_free(struct perf_hpp_fmt *fmt)
|
| 2995 | {
|
| 2996 | struct hpp_sort_entry *hse;
|
| 2997 |
|
| 2998 | hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
| 2999 | free(hse);
|
| 3000 | }
|
| 3001 |
|
| 3002 | static void hse_init(struct perf_hpp_fmt *fmt, struct hist_entry *he)
|
| 3003 | {
|
| 3004 | struct hpp_sort_entry *hse;
|
| 3005 |
|
| 3006 | if (!perf_hpp__is_sort_entry(fmt))
|
| 3007 | return;
|
| 3008 |
|
| 3009 | hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
| 3010 |
|
| 3011 | if (hse->se->se_init)
|
| 3012 | hse->se->se_init(he);
|
| 3013 | }
|
| 3014 |
|
| 3015 | static struct hpp_sort_entry *
|
| 3016 | __sort_dimension__alloc_hpp(struct sort_dimension *sd, int level)
|
| 3017 | {
|
| 3018 | struct hpp_sort_entry *hse;
|
| 3019 |
|
| 3020 | hse = malloc(sizeof(*hse));
|
| 3021 | if (hse == NULL) {
|
| 3022 | pr_err("Memory allocation failed\n");
|
| 3023 | return NULL;
|
| 3024 | }
|
| 3025 |
|
| 3026 | hse->se = sd->entry;
|
| 3027 | hse->hpp.name = sd->entry->se_header;
|
| 3028 | hse->hpp.header = __sort__hpp_header;
|
| 3029 | hse->hpp.width = __sort__hpp_width;
|
| 3030 | hse->hpp.entry = __sort__hpp_entry;
|
| 3031 | hse->hpp.color = NULL;
|
| 3032 |
|
| 3033 | hse->hpp.cmp = __sort__hpp_cmp;
|
| 3034 | hse->hpp.collapse = __sort__hpp_collapse;
|
| 3035 | hse->hpp.sort = __sort__hpp_sort;
|
| 3036 | hse->hpp.equal = __sort__hpp_equal;
|
| 3037 | hse->hpp.free = hse_free;
|
| 3038 | hse->hpp.init = hse_init;
|
| 3039 |
|
| 3040 | INIT_LIST_HEAD(&hse->hpp.list);
|
| 3041 | INIT_LIST_HEAD(&hse->hpp.sort_list);
|
| 3042 | hse->hpp.elide = false;
|
| 3043 | hse->hpp.len = 0;
|
| 3044 | hse->hpp.user_len = 0;
|
| 3045 | hse->hpp.level = level;
|
| 3046 |
|
| 3047 | return hse;
|
| 3048 | }
|
| 3049 |
|
| 3050 | static void hpp_free(struct perf_hpp_fmt *fmt)
|
| 3051 | {
|
| 3052 | free(fmt);
|
| 3053 | }
|
| 3054 |
|
| 3055 | static struct perf_hpp_fmt *__hpp_dimension__alloc_hpp(struct hpp_dimension *hd,
|
| 3056 | int level)
|
| 3057 | {
|
| 3058 | struct perf_hpp_fmt *fmt;
|
| 3059 |
|
| 3060 | fmt = memdup(hd->fmt, sizeof(*fmt));
|
| 3061 | if (fmt) {
|
| 3062 | INIT_LIST_HEAD(&fmt->list);
|
| 3063 | INIT_LIST_HEAD(&fmt->sort_list);
|
| 3064 | fmt->free = hpp_free;
|
| 3065 | fmt->level = level;
|
| 3066 | }
|
| 3067 |
|
| 3068 | return fmt;
|
| 3069 | }
|
| 3070 |
|
| 3071 | int hist_entry__filter(struct hist_entry *he, int type, const void *arg)
|
| 3072 | {
|
| 3073 | struct perf_hpp_fmt *fmt;
|
| 3074 | struct hpp_sort_entry *hse;
|
| 3075 | int ret = -1;
|
| 3076 | int r;
|
| 3077 |
|
| 3078 | perf_hpp_list__for_each_format(he->hpp_list, fmt) {
|
| 3079 | if (!perf_hpp__is_sort_entry(fmt))
|
| 3080 | continue;
|
| 3081 |
|
| 3082 | hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
| 3083 | if (hse->se->se_filter == NULL)
|
| 3084 | continue;
|
| 3085 |
|
| 3086 | |
| 3087 | * hist entry is filtered if any of sort key in the hpp list
|
| 3088 | * is applied. But it should skip non-matched filter types.
|
| 3089 |
|
| 3090 | r = hse->se->se_filter(he, type, arg);
|
| 3091 | if (r >= 0) {
|
| 3092 | if (ret < 0)
|
| 3093 | ret = 0;
|
| 3094 | ret |= r;
|
| 3095 | }
|
| 3096 | }
|
| 3097 |
|
| 3098 | return ret;
|
| 3099 | }
|
| 3100 |
|
| 3101 | static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd,
|
| 3102 | struct perf_hpp_list *list,
|
| 3103 | int level)
|
| 3104 | {
|
| 3105 | struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, level);
|
| 3106 |
|
| 3107 | if (hse == NULL)
|
| 3108 | return -ENOMEM;
|
| 3109 |
|
| 3110 | perf_hpp_list__register_sort_field(list, &hse->hpp);
|
| 3111 | return 0;
|
| 3112 | }
|
| 3113 |
|
| 3114 | static int __sort_dimension__add_hpp_output(struct sort_dimension *sd,
|
| 3115 | struct perf_hpp_list *list,
|
| 3116 | int level)
|
| 3117 | {
|
| 3118 | struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, level);
|
| 3119 |
|
| 3120 | if (hse == NULL)
|
| 3121 | return -ENOMEM;
|
| 3122 |
|
| 3123 | perf_hpp_list__column_register(list, &hse->hpp);
|
| 3124 | return 0;
|
| 3125 | }
|
| 3126 |
|
| 3127 | #ifndef HAVE_LIBTRACEEVENT
|
| 3128 | bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt __maybe_unused)
|
| 3129 | {
|
| 3130 | return false;
|
| 3131 | }
|
| 3132 | bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt __maybe_unused,
|
| 3133 | struct hists *hists __maybe_unused)
|
| 3134 | {
|
| 3135 | return false;
|
| 3136 | }
|
| 3137 | #else
|
| 3138 | struct hpp_dynamic_entry {
|
| 3139 | struct perf_hpp_fmt hpp;
|
| 3140 | struct evsel *evsel;
|
| 3141 | struct tep_format_field *field;
|
| 3142 | unsigned dynamic_len;
|
| 3143 | bool raw_trace;
|
| 3144 | };
|
| 3145 |
|
| 3146 | static int hde_width(struct hpp_dynamic_entry *hde)
|
| 3147 | {
|
| 3148 | if (!hde->hpp.len) {
|
| 3149 | int len = hde->dynamic_len;
|
| 3150 | int namelen = strlen(hde->field->name);
|
| 3151 | int fieldlen = hde->field->size;
|
| 3152 |
|
| 3153 | if (namelen > len)
|
| 3154 | len = namelen;
|
| 3155 |
|
| 3156 | if (!(hde->field->flags & TEP_FIELD_IS_STRING)) {
|
| 3157 |
|
| 3158 | fieldlen = hde->field->size * 2 + 2;
|
| 3159 | }
|
| 3160 | if (fieldlen > len)
|
| 3161 | len = fieldlen;
|
| 3162 |
|
| 3163 | hde->hpp.len = len;
|
| 3164 | }
|
| 3165 | return hde->hpp.len;
|
| 3166 | }
|
| 3167 |
|
| 3168 | static void update_dynamic_len(struct hpp_dynamic_entry *hde,
|
| 3169 | struct hist_entry *he)
|
| 3170 | {
|
| 3171 | char *str, *pos;
|
| 3172 | struct tep_format_field *field = hde->field;
|
| 3173 | size_t namelen;
|
| 3174 | bool last = false;
|
| 3175 |
|
| 3176 | if (hde->raw_trace)
|
| 3177 | return;
|
| 3178 |
|
| 3179 |
|
| 3180 | if (!he->trace_output)
|
| 3181 | he->trace_output = get_trace_output(he);
|
| 3182 |
|
| 3183 | namelen = strlen(field->name);
|
| 3184 | str = he->trace_output;
|
| 3185 |
|
| 3186 | while (str) {
|
| 3187 | pos = strchr(str, ' ');
|
| 3188 | if (pos == NULL) {
|
| 3189 | last = true;
|
| 3190 | pos = str + strlen(str);
|
| 3191 | }
|
| 3192 |
|
| 3193 | if (!strncmp(str, field->name, namelen)) {
|
| 3194 | size_t len;
|
| 3195 |
|
| 3196 | str += namelen + 1;
|
| 3197 | len = pos - str;
|
| 3198 |
|
| 3199 | if (len > hde->dynamic_len)
|
| 3200 | hde->dynamic_len = len;
|
| 3201 | break;
|
| 3202 | }
|
| 3203 |
|
| 3204 | if (last)
|
| 3205 | str = NULL;
|
| 3206 | else
|
| 3207 | str = pos + 1;
|
| 3208 | }
|
| 3209 | }
|
| 3210 |
|
| 3211 | static int __sort__hde_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
| 3212 | struct hists *hists __maybe_unused,
|
| 3213 | int line __maybe_unused,
|
| 3214 | int *span __maybe_unused)
|
| 3215 | {
|
| 3216 | struct hpp_dynamic_entry *hde;
|
| 3217 | size_t len = fmt->user_len;
|
| 3218 |
|
| 3219 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
|
| 3220 |
|
| 3221 | if (!len)
|
| 3222 | len = hde_width(hde);
|
| 3223 |
|
| 3224 | return scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, hde->field->name);
|
| 3225 | }
|
| 3226 |
|
| 3227 | static int __sort__hde_width(struct perf_hpp_fmt *fmt,
|
| 3228 | struct perf_hpp *hpp __maybe_unused,
|
| 3229 | struct hists *hists __maybe_unused)
|
| 3230 | {
|
| 3231 | struct hpp_dynamic_entry *hde;
|
| 3232 | size_t len = fmt->user_len;
|
| 3233 |
|
| 3234 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
|
| 3235 |
|
| 3236 | if (!len)
|
| 3237 | len = hde_width(hde);
|
| 3238 |
|
| 3239 | return len;
|
| 3240 | }
|
| 3241 |
|
| 3242 | bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *hists)
|
| 3243 | {
|
| 3244 | struct hpp_dynamic_entry *hde;
|
| 3245 |
|
| 3246 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
|
| 3247 |
|
| 3248 | return hists_to_evsel(hists) == hde->evsel;
|
| 3249 | }
|
| 3250 |
|
| 3251 | static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
| 3252 | struct hist_entry *he)
|
| 3253 | {
|
| 3254 | struct hpp_dynamic_entry *hde;
|
| 3255 | size_t len = fmt->user_len;
|
| 3256 | char *str, *pos;
|
| 3257 | struct tep_format_field *field;
|
| 3258 | size_t namelen;
|
| 3259 | bool last = false;
|
| 3260 | int ret;
|
| 3261 |
|
| 3262 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
|
| 3263 |
|
| 3264 | if (!len)
|
| 3265 | len = hde_width(hde);
|
| 3266 |
|
| 3267 | if (hde->raw_trace)
|
| 3268 | goto raw_field;
|
| 3269 |
|
| 3270 | if (!he->trace_output)
|
| 3271 | he->trace_output = get_trace_output(he);
|
| 3272 |
|
| 3273 | field = hde->field;
|
| 3274 | namelen = strlen(field->name);
|
| 3275 | str = he->trace_output;
|
| 3276 |
|
| 3277 | while (str) {
|
| 3278 | pos = strchr(str, ' ');
|
| 3279 | if (pos == NULL) {
|
| 3280 | last = true;
|
| 3281 | pos = str + strlen(str);
|
| 3282 | }
|
| 3283 |
|
| 3284 | if (!strncmp(str, field->name, namelen)) {
|
| 3285 | str += namelen + 1;
|
| 3286 | str = strndup(str, pos - str);
|
| 3287 |
|
| 3288 | if (str == NULL)
|
| 3289 | return scnprintf(hpp->buf, hpp->size,
|
| 3290 | "%*.*s", len, len, "ERROR");
|
| 3291 | break;
|
| 3292 | }
|
| 3293 |
|
| 3294 | if (last)
|
| 3295 | str = NULL;
|
| 3296 | else
|
| 3297 | str = pos + 1;
|
| 3298 | }
|
| 3299 |
|
| 3300 | if (str == NULL) {
|
| 3301 | struct trace_seq seq;
|
| 3302 | raw_field:
|
| 3303 | trace_seq_init(&seq);
|
| 3304 | tep_print_field(&seq, he->raw_data, hde->field);
|
| 3305 | str = seq.buffer;
|
| 3306 | }
|
| 3307 |
|
| 3308 | ret = scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, str);
|
| 3309 | free(str);
|
| 3310 | return ret;
|
| 3311 | }
|
| 3312 |
|
| 3313 | static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt,
|
| 3314 | struct hist_entry *a, struct hist_entry *b)
|
| 3315 | {
|
| 3316 | struct hpp_dynamic_entry *hde;
|
| 3317 | struct tep_format_field *field;
|
| 3318 | unsigned offset, size;
|
| 3319 |
|
| 3320 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
|
| 3321 |
|
| 3322 | field = hde->field;
|
| 3323 | if (field->flags & TEP_FIELD_IS_DYNAMIC) {
|
| 3324 | unsigned long long dyn;
|
| 3325 |
|
| 3326 | tep_read_number_field(field, a->raw_data, &dyn);
|
| 3327 | offset = dyn & 0xffff;
|
| 3328 | size = (dyn >> 16) & 0xffff;
|
| 3329 | if (tep_field_is_relative(field->flags))
|
| 3330 | offset += field->offset + field->size;
|
| 3331 |
|
| 3332 | if (size > hde->dynamic_len)
|
| 3333 | hde->dynamic_len = size;
|
| 3334 | } else {
|
| 3335 | offset = field->offset;
|
| 3336 | size = field->size;
|
| 3337 | }
|
| 3338 |
|
| 3339 | return memcmp(a->raw_data + offset, b->raw_data + offset, size);
|
| 3340 | }
|
| 3341 |
|
| 3342 | bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt)
|
| 3343 | {
|
| 3344 | return fmt->cmp == __sort__hde_cmp;
|
| 3345 | }
|
| 3346 |
|
| 3347 | static bool __sort__hde_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
|
| 3348 | {
|
| 3349 | struct hpp_dynamic_entry *hde_a;
|
| 3350 | struct hpp_dynamic_entry *hde_b;
|
| 3351 |
|
| 3352 | if (!perf_hpp__is_dynamic_entry(a) || !perf_hpp__is_dynamic_entry(b))
|
| 3353 | return false;
|
| 3354 |
|
| 3355 | hde_a = container_of(a, struct hpp_dynamic_entry, hpp);
|
| 3356 | hde_b = container_of(b, struct hpp_dynamic_entry, hpp);
|
| 3357 |
|
| 3358 | return hde_a->field == hde_b->field;
|
| 3359 | }
|
| 3360 |
|
| 3361 | static void hde_free(struct perf_hpp_fmt *fmt)
|
| 3362 | {
|
| 3363 | struct hpp_dynamic_entry *hde;
|
| 3364 |
|
| 3365 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
|
| 3366 | free(hde);
|
| 3367 | }
|
| 3368 |
|
| 3369 | static void __sort__hde_init(struct perf_hpp_fmt *fmt, struct hist_entry *he)
|
| 3370 | {
|
| 3371 | struct hpp_dynamic_entry *hde;
|
| 3372 |
|
| 3373 | if (!perf_hpp__is_dynamic_entry(fmt))
|
| 3374 | return;
|
| 3375 |
|
| 3376 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
|
| 3377 | update_dynamic_len(hde, he);
|
| 3378 | }
|
| 3379 |
|
| 3380 | static struct hpp_dynamic_entry *
|
| 3381 | __alloc_dynamic_entry(struct evsel *evsel, struct tep_format_field *field,
|
| 3382 | int level)
|
| 3383 | {
|
| 3384 | struct hpp_dynamic_entry *hde;
|
| 3385 |
|
| 3386 | hde = malloc(sizeof(*hde));
|
| 3387 | if (hde == NULL) {
|
| 3388 | pr_debug("Memory allocation failed\n");
|
| 3389 | return NULL;
|
| 3390 | }
|
| 3391 |
|
| 3392 | hde->evsel = evsel;
|
| 3393 | hde->field = field;
|
| 3394 | hde->dynamic_len = 0;
|
| 3395 |
|
| 3396 | hde->hpp.name = field->name;
|
| 3397 | hde->hpp.header = __sort__hde_header;
|
| 3398 | hde->hpp.width = __sort__hde_width;
|
| 3399 | hde->hpp.entry = __sort__hde_entry;
|
| 3400 | hde->hpp.color = NULL;
|
| 3401 |
|
| 3402 | hde->hpp.init = __sort__hde_init;
|
| 3403 | hde->hpp.cmp = __sort__hde_cmp;
|
| 3404 | hde->hpp.collapse = __sort__hde_cmp;
|
| 3405 | hde->hpp.sort = __sort__hde_cmp;
|
| 3406 | hde->hpp.equal = __sort__hde_equal;
|
| 3407 | hde->hpp.free = hde_free;
|
| 3408 |
|
| 3409 | INIT_LIST_HEAD(&hde->hpp.list);
|
| 3410 | INIT_LIST_HEAD(&hde->hpp.sort_list);
|
| 3411 | hde->hpp.elide = false;
|
| 3412 | hde->hpp.len = 0;
|
| 3413 | hde->hpp.user_len = 0;
|
| 3414 | hde->hpp.level = level;
|
| 3415 |
|
| 3416 | return hde;
|
| 3417 | }
|
| 3418 | #endif
|
| 3419 |
|
| 3420 | struct perf_hpp_fmt *perf_hpp_fmt__dup(struct perf_hpp_fmt *fmt)
|
| 3421 | {
|
| 3422 | struct perf_hpp_fmt *new_fmt = NULL;
|
| 3423 |
|
| 3424 | if (perf_hpp__is_sort_entry(fmt)) {
|
| 3425 | struct hpp_sort_entry *hse, *new_hse;
|
| 3426 |
|
| 3427 | hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
| 3428 | new_hse = memdup(hse, sizeof(*hse));
|
| 3429 | if (new_hse)
|
| 3430 | new_fmt = &new_hse->hpp;
|
| 3431 | #ifdef HAVE_LIBTRACEEVENT
|
| 3432 | } else if (perf_hpp__is_dynamic_entry(fmt)) {
|
| 3433 | struct hpp_dynamic_entry *hde, *new_hde;
|
| 3434 |
|
| 3435 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
|
| 3436 | new_hde = memdup(hde, sizeof(*hde));
|
| 3437 | if (new_hde)
|
| 3438 | new_fmt = &new_hde->hpp;
|
| 3439 | #endif
|
| 3440 | } else {
|
| 3441 | new_fmt = memdup(fmt, sizeof(*fmt));
|
| 3442 | }
|
| 3443 |
|
| 3444 | INIT_LIST_HEAD(&new_fmt->list);
|
| 3445 | INIT_LIST_HEAD(&new_fmt->sort_list);
|
| 3446 |
|
| 3447 | return new_fmt;
|
| 3448 | }
|
| 3449 |
|
| 3450 | static int parse_field_name(char *str, char **event, char **field, char **opt)
|
| 3451 | {
|
| 3452 | char *event_name, *field_name, *opt_name;
|
| 3453 |
|
| 3454 | event_name = str;
|
| 3455 | field_name = strchr(str, '.');
|
| 3456 |
|
| 3457 | if (field_name) {
|
| 3458 | *field_name++ = '\0';
|
| 3459 | } else {
|
| 3460 | event_name = NULL;
|
| 3461 | field_name = str;
|
| 3462 | }
|
| 3463 |
|
| 3464 | opt_name = strchr(field_name, '/');
|
| 3465 | if (opt_name)
|
| 3466 | *opt_name++ = '\0';
|
| 3467 |
|
| 3468 | *event = event_name;
|
| 3469 | *field = field_name;
|
| 3470 | *opt = opt_name;
|
| 3471 |
|
| 3472 | return 0;
|
| 3473 | }
|
| 3474 |
|
| 3475 | |
| 3476 | * 1. '%' + event index (e.g. '%1' for first event)
|
| 3477 | * 2. full event name (e.g. sched:sched_switch)
|
| 3478 | * 3. partial event name (should not contain ':')
|
| 3479 |
|
| 3480 | static struct evsel *find_evsel(struct evlist *evlist, char *event_name)
|
| 3481 | {
|
| 3482 | struct evsel *evsel = NULL;
|
| 3483 | struct evsel *pos;
|
| 3484 | bool full_name;
|
| 3485 |
|
| 3486 |
|
| 3487 | if (event_name[0] == '%') {
|
| 3488 | int nr = strtol(event_name+1, NULL, 0);
|
| 3489 |
|
| 3490 | if (nr > evlist__nr_entries(evlist))
|
| 3491 | return NULL;
|
| 3492 |
|
| 3493 | evsel = evlist__first(evlist);
|
| 3494 | while (--nr > 0)
|
| 3495 | evsel = evsel__next(evsel);
|
| 3496 |
|
| 3497 | return evsel;
|
| 3498 | }
|
| 3499 |
|
| 3500 | full_name = !!strchr(event_name, ':');
|
| 3501 | evlist__for_each_entry(evlist, pos) {
|
| 3502 |
|
| 3503 | if (full_name && evsel__name_is(pos, event_name))
|
| 3504 | return pos;
|
| 3505 |
|
| 3506 | if (!full_name && strstr(pos->name, event_name)) {
|
| 3507 | if (evsel) {
|
| 3508 | pr_debug("'%s' event is ambiguous: it can be %s or %s\n",
|
| 3509 | event_name, evsel->name, pos->name);
|
| 3510 | return NULL;
|
| 3511 | }
|
| 3512 | evsel = pos;
|
| 3513 | }
|
| 3514 | }
|
| 3515 |
|
| 3516 | return evsel;
|
| 3517 | }
|
| 3518 |
|
| 3519 | #ifdef HAVE_LIBTRACEEVENT
|
| 3520 | static int __dynamic_dimension__add(struct evsel *evsel,
|
| 3521 | struct tep_format_field *field,
|
| 3522 | bool raw_trace, int level)
|
| 3523 | {
|
| 3524 | struct hpp_dynamic_entry *hde;
|
| 3525 |
|
| 3526 | hde = __alloc_dynamic_entry(evsel, field, level);
|
| 3527 | if (hde == NULL)
|
| 3528 | return -ENOMEM;
|
| 3529 |
|
| 3530 | hde->raw_trace = raw_trace;
|
| 3531 |
|
| 3532 | perf_hpp__register_sort_field(&hde->hpp);
|
| 3533 | return 0;
|
| 3534 | }
|
| 3535 |
|
| 3536 | static int add_evsel_fields(struct evsel *evsel, bool raw_trace, int level)
|
| 3537 | {
|
| 3538 | int ret;
|
| 3539 | struct tep_event *tp_format = evsel__tp_format(evsel);
|
| 3540 | struct tep_format_field *field = tp_format ? tp_format->format.fields : NULL;
|
| 3541 | while (field) {
|
| 3542 | ret = __dynamic_dimension__add(evsel, field, raw_trace, level);
|
| 3543 | if (ret < 0)
|
| 3544 | return ret;
|
| 3545 |
|
| 3546 | field = field->next;
|
| 3547 | }
|
| 3548 | return 0;
|
| 3549 | }
|
| 3550 |
|
| 3551 | static int add_all_dynamic_fields(struct evlist *evlist, bool raw_trace,
|
| 3552 | int level)
|
| 3553 | {
|
| 3554 | int ret;
|
| 3555 | struct evsel *evsel;
|
| 3556 |
|
| 3557 | evlist__for_each_entry(evlist, evsel) {
|
| 3558 | if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
|
| 3559 | continue;
|
| 3560 |
|
| 3561 | ret = add_evsel_fields(evsel, raw_trace, level);
|
| 3562 | if (ret < 0)
|
| 3563 | return ret;
|
| 3564 | }
|
| 3565 | return 0;
|
| 3566 | }
|
| 3567 |
|
| 3568 | static int add_all_matching_fields(struct evlist *evlist,
|
| 3569 | char *field_name, bool raw_trace, int level)
|
| 3570 | {
|
| 3571 | int ret = -ESRCH;
|
| 3572 | struct evsel *evsel;
|
| 3573 |
|
| 3574 | evlist__for_each_entry(evlist, evsel) {
|
| 3575 | struct tep_event *tp_format;
|
| 3576 | struct tep_format_field *field;
|
| 3577 |
|
| 3578 | if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
|
| 3579 | continue;
|
| 3580 |
|
| 3581 | tp_format = evsel__tp_format(evsel);
|
| 3582 | if (tp_format == NULL)
|
| 3583 | continue;
|
| 3584 |
|
| 3585 | field = tep_find_any_field(tp_format, field_name);
|
| 3586 | if (field == NULL)
|
| 3587 | continue;
|
| 3588 |
|
| 3589 | ret = __dynamic_dimension__add(evsel, field, raw_trace, level);
|
| 3590 | if (ret < 0)
|
| 3591 | break;
|
| 3592 | }
|
| 3593 | return ret;
|
| 3594 | }
|
| 3595 | #endif
|
| 3596 |
|
| 3597 | static int add_dynamic_entry(struct evlist *evlist, const char *tok,
|
| 3598 | int level)
|
| 3599 | {
|
| 3600 | char *str, *event_name, *field_name, *opt_name;
|
| 3601 | struct evsel *evsel;
|
| 3602 | bool raw_trace = symbol_conf.raw_trace;
|
| 3603 | int ret = 0;
|
| 3604 |
|
| 3605 | if (evlist == NULL)
|
| 3606 | return -ENOENT;
|
| 3607 |
|
| 3608 | str = strdup(tok);
|
| 3609 | if (str == NULL)
|
| 3610 | return -ENOMEM;
|
| 3611 |
|
| 3612 | if (parse_field_name(str, &event_name, &field_name, &opt_name) < 0) {
|
| 3613 | ret = -EINVAL;
|
| 3614 | goto out;
|
| 3615 | }
|
| 3616 |
|
| 3617 | if (opt_name) {
|
| 3618 | if (strcmp(opt_name, "raw")) {
|
| 3619 | pr_debug("unsupported field option %s\n", opt_name);
|
| 3620 | ret = -EINVAL;
|
| 3621 | goto out;
|
| 3622 | }
|
| 3623 | raw_trace = true;
|
| 3624 | }
|
| 3625 |
|
| 3626 | #ifdef HAVE_LIBTRACEEVENT
|
| 3627 | if (!strcmp(field_name, "trace_fields")) {
|
| 3628 | ret = add_all_dynamic_fields(evlist, raw_trace, level);
|
| 3629 | goto out;
|
| 3630 | }
|
| 3631 |
|
| 3632 | if (event_name == NULL) {
|
| 3633 | ret = add_all_matching_fields(evlist, field_name, raw_trace, level);
|
| 3634 | goto out;
|
| 3635 | }
|
| 3636 | #else
|
| 3637 | evlist__for_each_entry(evlist, evsel) {
|
| 3638 | if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT) {
|
| 3639 | pr_err("%s %s", ret ? "," : "This perf binary isn't linked with libtraceevent, can't process", evsel__name(evsel));
|
| 3640 | ret = -ENOTSUP;
|
| 3641 | }
|
| 3642 | }
|
| 3643 |
|
| 3644 | if (ret) {
|
| 3645 | pr_err("\n");
|
| 3646 | goto out;
|
| 3647 | }
|
| 3648 | #endif
|
| 3649 |
|
| 3650 | evsel = find_evsel(evlist, event_name);
|
| 3651 | if (evsel == NULL) {
|
| 3652 | pr_debug("Cannot find event: %s\n", event_name);
|
| 3653 | ret = -ENOENT;
|
| 3654 | goto out;
|
| 3655 | }
|
| 3656 |
|
| 3657 | if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) {
|
| 3658 | pr_debug("%s is not a tracepoint event\n", event_name);
|
| 3659 | ret = -EINVAL;
|
| 3660 | goto out;
|
| 3661 | }
|
| 3662 |
|
| 3663 | #ifdef HAVE_LIBTRACEEVENT
|
| 3664 | if (!strcmp(field_name, "*")) {
|
| 3665 | ret = add_evsel_fields(evsel, raw_trace, level);
|
| 3666 | } else {
|
| 3667 | struct tep_event *tp_format = evsel__tp_format(evsel);
|
| 3668 | struct tep_format_field *field =
|
| 3669 | tp_format ? tep_find_any_field(tp_format, field_name) : NULL;
|
| 3670 |
|
| 3671 | if (field == NULL) {
|
| 3672 | pr_debug("Cannot find event field for %s.%s\n",
|
| 3673 | event_name, field_name);
|
| 3674 | return -ENOENT;
|
| 3675 | }
|
| 3676 |
|
| 3677 | ret = __dynamic_dimension__add(evsel, field, raw_trace, level);
|
| 3678 | }
|
| 3679 | #else
|
| 3680 | (void)level;
|
| 3681 | (void)raw_trace;
|
| 3682 | #endif
|
| 3683 |
|
| 3684 | out:
|
| 3685 | free(str);
|
| 3686 | return ret;
|
| 3687 | }
|
| 3688 |
|
| 3689 | static int __sort_dimension__update(struct sort_dimension *sd,
|
| 3690 | struct perf_hpp_list *list)
|
| 3691 | {
|
| 3692 | if (sd->entry == &sort_parent && parent_pattern) {
|
| 3693 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
|
| 3694 | if (ret) {
|
| 3695 | char err[BUFSIZ];
|
| 3696 |
|
| 3697 | regerror(ret, &parent_regex, err, sizeof(err));
|
| 3698 | pr_err("Invalid regex: %s\n%s", parent_pattern, err);
|
| 3699 | return -EINVAL;
|
| 3700 | }
|
| 3701 | list->parent = 1;
|
| 3702 | } else if (sd->entry == &sort_sym) {
|
| 3703 | list->sym = 1;
|
| 3704 | |
| 3705 | * perf diff displays the performance difference amongst
|
| 3706 | * two or more perf.data files. Those files could come
|
| 3707 | * from different binaries. So we should not compare
|
| 3708 | * their ips, but the name of symbol.
|
| 3709 |
|
| 3710 | if (sort__mode == SORT_MODE__DIFF)
|
| 3711 | sd->entry->se_collapse = sort__sym_sort;
|
| 3712 |
|
| 3713 | } else if (sd->entry == &sort_sym_offset) {
|
| 3714 | list->sym = 1;
|
| 3715 | } else if (sd->entry == &sort_dso) {
|
| 3716 | list->dso = 1;
|
| 3717 | } else if (sd->entry == &sort_socket) {
|
| 3718 | list->socket = 1;
|
| 3719 | } else if (sd->entry == &sort_thread) {
|
| 3720 | list->thread = 1;
|
| 3721 | } else if (sd->entry == &sort_comm) {
|
| 3722 | list->comm = 1;
|
| 3723 | } else if (sd->entry == &sort_comm_nodigit) {
|
| 3724 | list->comm_nodigit = list->comm = 1;
|
| 3725 | } else if (sd->entry == &sort_type_offset) {
|
| 3726 | symbol_conf.annotate_data_member = true;
|
| 3727 | } else if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) {
|
| 3728 | list->sym = 1;
|
| 3729 | } else if (sd->entry == &sort_mem_dcacheline && cacheline_size() == 0) {
|
| 3730 | return -EINVAL;
|
| 3731 | } else if (sd->entry == &sort_mem_daddr_sym) {
|
| 3732 | list->sym = 1;
|
| 3733 | }
|
| 3734 |
|
| 3735 | if (sd->entry->se_collapse)
|
| 3736 | list->need_collapse = 1;
|
| 3737 |
|
| 3738 | return 0;
|
| 3739 | }
|
| 3740 |
|
| 3741 | static int __sort_dimension__add(struct sort_dimension *sd,
|
| 3742 | struct perf_hpp_list *list,
|
| 3743 | int level)
|
| 3744 | {
|
| 3745 | int ret;
|
| 3746 |
|
| 3747 | if (sd->taken)
|
| 3748 | return 0;
|
| 3749 |
|
| 3750 | ret = __sort_dimension__add_hpp_sort(sd, list, level);
|
| 3751 | if (ret < 0)
|
| 3752 | return ret;
|
| 3753 |
|
| 3754 | ret = __sort_dimension__update(sd, list);
|
| 3755 | if (ret < 0)
|
| 3756 | return ret;
|
| 3757 |
|
| 3758 | sd->taken = 1;
|
| 3759 |
|
| 3760 | return 0;
|
| 3761 | }
|
| 3762 |
|
| 3763 | static int __hpp_dimension__add(struct hpp_dimension *hd,
|
| 3764 | struct perf_hpp_list *list,
|
| 3765 | int level)
|
| 3766 | {
|
| 3767 | struct perf_hpp_fmt *fmt;
|
| 3768 |
|
| 3769 | if (hd->taken)
|
| 3770 | return 0;
|
| 3771 |
|
| 3772 | fmt = __hpp_dimension__alloc_hpp(hd, level);
|
| 3773 | if (!fmt)
|
| 3774 | return -ENOMEM;
|
| 3775 |
|
| 3776 | hd->taken = 1;
|
| 3777 | hd->was_taken = 1;
|
| 3778 | perf_hpp_list__register_sort_field(list, fmt);
|
| 3779 | return 0;
|
| 3780 | }
|
| 3781 |
|
| 3782 | static int __sort_dimension__add_output(struct perf_hpp_list *list,
|
| 3783 | struct sort_dimension *sd,
|
| 3784 | int level)
|
| 3785 | {
|
| 3786 | int ret;
|
| 3787 |
|
| 3788 | if (sd->taken)
|
| 3789 | return 0;
|
| 3790 |
|
| 3791 | ret = __sort_dimension__add_hpp_output(sd, list, level);
|
| 3792 | if (ret < 0)
|
| 3793 | return ret;
|
| 3794 |
|
| 3795 | ret = __sort_dimension__update(sd, list);
|
| 3796 | if (ret < 0)
|
| 3797 | return ret;
|
| 3798 |
|
| 3799 | sd->taken = 1;
|
| 3800 | return 0;
|
| 3801 | }
|
| 3802 |
|
| 3803 | static int __hpp_dimension__add_output(struct perf_hpp_list *list,
|
| 3804 | struct hpp_dimension *hd,
|
| 3805 | int level)
|
| 3806 | {
|
| 3807 | struct perf_hpp_fmt *fmt;
|
| 3808 |
|
| 3809 | if (hd->taken)
|
| 3810 | return 0;
|
| 3811 |
|
| 3812 | fmt = __hpp_dimension__alloc_hpp(hd, level);
|
| 3813 | if (!fmt)
|
| 3814 | return -ENOMEM;
|
| 3815 |
|
| 3816 | hd->taken = 1;
|
| 3817 | perf_hpp_list__column_register(list, fmt);
|
| 3818 | return 0;
|
| 3819 | }
|
| 3820 |
|
| 3821 | int hpp_dimension__add_output(unsigned col, bool implicit)
|
| 3822 | {
|
| 3823 | struct hpp_dimension *hd;
|
| 3824 |
|
| 3825 | BUG_ON(col >= PERF_HPP__MAX_INDEX);
|
| 3826 | hd = &hpp_sort_dimensions[col];
|
| 3827 | if (implicit && !hd->was_taken)
|
| 3828 | return 0;
|
| 3829 | return __hpp_dimension__add_output(&perf_hpp_list, hd, 0);
|
| 3830 | }
|
| 3831 |
|
| 3832 | int sort_dimension__add(struct perf_hpp_list *list, const char *tok,
|
| 3833 | struct evlist *evlist, struct perf_env *env,
|
| 3834 | int level)
|
| 3835 | {
|
| 3836 | unsigned int i, j;
|
| 3837 |
|
| 3838 | |
| 3839 | * Check to see if there are any arch specific
|
| 3840 | * sort dimensions not applicable for the current
|
| 3841 | * architecture. If so, Skip that sort key since
|
| 3842 | * we don't want to display it in the output fields.
|
| 3843 |
|
| 3844 | for (j = 0; j < ARRAY_SIZE(arch_specific_sort_keys); j++) {
|
| 3845 | if (!strcmp(arch_specific_sort_keys[j], tok) &&
|
| 3846 | !arch_support_sort_key(tok, env)) {
|
| 3847 | return 0;
|
| 3848 | }
|
| 3849 | }
|
| 3850 |
|
| 3851 | for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
|
| 3852 | struct sort_dimension *sd = &common_sort_dimensions[i];
|
| 3853 |
|
| 3854 | if (!sd->name || strncasecmp(tok, sd->name, strlen(tok)))
|
| 3855 | continue;
|
| 3856 |
|
| 3857 | for (j = 0; j < ARRAY_SIZE(dynamic_headers); j++) {
|
| 3858 | if (sd->name && !strcmp(dynamic_headers[j], sd->name))
|
| 3859 | sort_dimension_add_dynamic_header(sd, env);
|
| 3860 | }
|
| 3861 |
|
| 3862 | return __sort_dimension__add(sd, list, level);
|
| 3863 | }
|
| 3864 |
|
| 3865 | for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
|
| 3866 | struct sort_dimension *sd = &bstack_sort_dimensions[i];
|
| 3867 |
|
| 3868 | if (!sd->name || strncasecmp(tok, sd->name, strlen(tok)))
|
| 3869 | continue;
|
| 3870 |
|
| 3871 | if ((sort__mode != SORT_MODE__BRANCH) &&
|
| 3872 | strncasecmp(tok, "callchain_branch_predicted",
|
| 3873 | strlen(tok)) &&
|
| 3874 | strncasecmp(tok, "callchain_branch_abort",
|
| 3875 | strlen(tok)) &&
|
| 3876 | strncasecmp(tok, "callchain_branch_cycles",
|
| 3877 | strlen(tok)))
|
| 3878 | return -EINVAL;
|
| 3879 |
|
| 3880 | return __sort_dimension__add(sd, list, level);
|
| 3881 | }
|
| 3882 |
|
| 3883 | for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
|
| 3884 | struct sort_dimension *sd = &memory_sort_dimensions[i];
|
| 3885 |
|
| 3886 | if (!sd->name || strncasecmp(tok, sd->name, strlen(tok)))
|
| 3887 | continue;
|
| 3888 |
|
| 3889 | if (sort__mode != SORT_MODE__MEMORY)
|
| 3890 | return -EINVAL;
|
| 3891 |
|
| 3892 | return __sort_dimension__add(sd, list, level);
|
| 3893 | }
|
| 3894 |
|
| 3895 | for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
|
| 3896 | struct hpp_dimension *hd = &hpp_sort_dimensions[i];
|
| 3897 |
|
| 3898 | if (strncasecmp(tok, hd->name, strlen(tok)))
|
| 3899 | continue;
|
| 3900 |
|
| 3901 | return __hpp_dimension__add(hd, list, level);
|
| 3902 | }
|
| 3903 |
|
| 3904 | if (!add_dynamic_entry(evlist, tok, level))
|
| 3905 | return 0;
|
| 3906 |
|
| 3907 | return -ESRCH;
|
| 3908 | }
|
| 3909 |
|
| 3910 |
|
| 3911 | static bool is_hpp_sort_key(const char *key, struct perf_env *env)
|
| 3912 | {
|
| 3913 | unsigned i;
|
| 3914 |
|
| 3915 | for (i = 0; i < ARRAY_SIZE(arch_specific_sort_keys); i++) {
|
| 3916 | if (!strcmp(arch_specific_sort_keys[i], key) &&
|
| 3917 | !arch_support_sort_key(key, env)) {
|
| 3918 | return false;
|
| 3919 | }
|
| 3920 | }
|
| 3921 |
|
| 3922 | for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
|
| 3923 | struct sort_dimension *sd = &common_sort_dimensions[i];
|
| 3924 |
|
| 3925 | if (sd->name && !strncasecmp(key, sd->name, strlen(key)))
|
| 3926 | return false;
|
| 3927 | }
|
| 3928 |
|
| 3929 | for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
|
| 3930 | struct hpp_dimension *hd = &hpp_sort_dimensions[i];
|
| 3931 |
|
| 3932 | if (!strncasecmp(key, hd->name, strlen(key)))
|
| 3933 | return true;
|
| 3934 | }
|
| 3935 | return false;
|
| 3936 | }
|
| 3937 |
|
| 3938 | static int setup_sort_list(struct perf_hpp_list *list, char *str,
|
| 3939 | struct evlist *evlist, struct perf_env *env)
|
| 3940 | {
|
| 3941 | char *tmp, *tok;
|
| 3942 | int ret = 0;
|
| 3943 | int level = 0;
|
| 3944 | int next_level = 1;
|
| 3945 | int prev_level = 0;
|
| 3946 | bool in_group = false;
|
| 3947 | bool prev_was_hpp = false;
|
| 3948 |
|
| 3949 | do {
|
| 3950 | tok = str;
|
| 3951 | tmp = strpbrk(str, "{}, ");
|
| 3952 | if (tmp) {
|
| 3953 | if (in_group)
|
| 3954 | next_level = level;
|
| 3955 | else
|
| 3956 | next_level = level + 1;
|
| 3957 |
|
| 3958 | if (*tmp == '{')
|
| 3959 | in_group = true;
|
| 3960 | else if (*tmp == '}')
|
| 3961 | in_group = false;
|
| 3962 |
|
| 3963 | *tmp = '\0';
|
| 3964 | str = tmp + 1;
|
| 3965 | }
|
| 3966 |
|
| 3967 | if (*tok) {
|
| 3968 | if (is_hpp_sort_key(tok, env)) {
|
| 3969 |
|
| 3970 | if (prev_was_hpp) {
|
| 3971 | bool next_same = (level == next_level);
|
| 3972 |
|
| 3973 | level = prev_level;
|
| 3974 | next_level = next_same ? level : level+1;
|
| 3975 | }
|
| 3976 | prev_was_hpp = true;
|
| 3977 | } else {
|
| 3978 | prev_was_hpp = false;
|
| 3979 | }
|
| 3980 |
|
| 3981 | ret = sort_dimension__add(list, tok, evlist, env, level);
|
| 3982 | switch (ret) {
|
| 3983 | case 0:
|
| 3984 | break;
|
| 3985 | case -EINVAL:
|
| 3986 | if (!cacheline_size() && !strncasecmp(tok, "dcacheline", strlen(tok)))
|
| 3987 | ui__error("The \"dcacheline\" --sort key needs to know the cacheline size and it couldn't be determined on this system");
|
| 3988 | else
|
| 3989 | ui__error("Invalid --sort key: `%s'", tok);
|
| 3990 | goto out;
|
| 3991 | case -ESRCH:
|
| 3992 | ui__error("Unknown --sort key: `%s'", tok);
|
| 3993 | goto out;
|
| 3994 | default: {
|
| 3995 | char buf[STRERR_BUFSIZE];
|
| 3996 |
|
| 3997 | ui__error("%s for --sort key: `%s'",
|
| 3998 | str_error_r(-ret, buf, sizeof(buf)), tok);
|
| 3999 | goto out;
|
| 4000 | }
|
| 4001 | }
|
| 4002 | prev_level = level;
|
| 4003 | }
|
| 4004 |
|
| 4005 | level = next_level;
|
| 4006 | } while (tmp);
|
| 4007 |
|
| 4008 | out:
|
| 4009 | return ret;
|
| 4010 | }
|
| 4011 |
|
| 4012 | static const char *get_default_sort_order(struct evlist *evlist)
|
| 4013 | {
|
| 4014 | const char *default_sort_orders[] = {
|
| 4015 | default_sort_order,
|
| 4016 | default_branch_sort_order,
|
| 4017 | default_mem_sort_order,
|
| 4018 | default_top_sort_order,
|
| 4019 | default_diff_sort_order,
|
| 4020 | default_tracepoint_sort_order,
|
| 4021 | };
|
| 4022 | bool use_trace = true;
|
| 4023 | struct evsel *evsel;
|
| 4024 |
|
| 4025 | BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
|
| 4026 |
|
| 4027 | if (evlist == NULL || evlist__empty(evlist))
|
| 4028 | goto out_no_evlist;
|
| 4029 |
|
| 4030 | evlist__for_each_entry(evlist, evsel) {
|
| 4031 | if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) {
|
| 4032 | use_trace = false;
|
| 4033 | break;
|
| 4034 | }
|
| 4035 | }
|
| 4036 |
|
| 4037 | if (use_trace) {
|
| 4038 | sort__mode = SORT_MODE__TRACEPOINT;
|
| 4039 | if (symbol_conf.raw_trace)
|
| 4040 | return "trace_fields";
|
| 4041 | }
|
| 4042 | out_no_evlist:
|
| 4043 | return default_sort_orders[sort__mode];
|
| 4044 | }
|
| 4045 |
|
| 4046 | static int setup_sort_order(struct evlist *evlist)
|
| 4047 | {
|
| 4048 | char *new_sort_order;
|
| 4049 |
|
| 4050 | |
| 4051 | * Append '+'-prefixed sort order to the default sort
|
| 4052 | * order string.
|
| 4053 |
|
| 4054 | if (!sort_order || is_strict_order(sort_order))
|
| 4055 | return 0;
|
| 4056 |
|
| 4057 | if (sort_order[1] == '\0') {
|
| 4058 | ui__error("Invalid --sort key: `+'");
|
| 4059 | return -EINVAL;
|
| 4060 | }
|
| 4061 |
|
| 4062 | |
| 4063 | * We allocate new sort_order string, but we never free it,
|
| 4064 | * because it's checked over the rest of the code.
|
| 4065 |
|
| 4066 | if (asprintf(&new_sort_order, "%s,%s",
|
| 4067 | get_default_sort_order(evlist), sort_order + 1) < 0) {
|
| 4068 | pr_err("Not enough memory to set up --sort");
|
| 4069 | return -ENOMEM;
|
| 4070 | }
|
| 4071 |
|
| 4072 | sort_order = new_sort_order;
|
| 4073 | return 0;
|
| 4074 | }
|
| 4075 |
|
| 4076 | |
| 4077 | * Adds 'pre,' prefix into 'str' is 'pre' is
|
| 4078 | * not already part of 'str'.
|
| 4079 |
|
| 4080 | static char *prefix_if_not_in(const char *pre, char *str)
|
| 4081 | {
|
| 4082 | char *n;
|
| 4083 |
|
| 4084 | if (!str || strstr(str, pre))
|
| 4085 | return str;
|
| 4086 |
|
| 4087 | if (asprintf(&n, "%s,%s", pre, str) < 0)
|
| 4088 | n = NULL;
|
| 4089 |
|
| 4090 | free(str);
|
| 4091 | return n;
|
| 4092 | }
|
| 4093 |
|
| 4094 | static char *setup_overhead(char *keys)
|
| 4095 | {
|
| 4096 | if (sort__mode == SORT_MODE__DIFF)
|
| 4097 | return keys;
|
| 4098 |
|
| 4099 | if (symbol_conf.prefer_latency) {
|
| 4100 | keys = prefix_if_not_in("overhead", keys);
|
| 4101 | keys = prefix_if_not_in("latency", keys);
|
| 4102 | if (symbol_conf.cumulate_callchain) {
|
| 4103 | keys = prefix_if_not_in("overhead_children", keys);
|
| 4104 | keys = prefix_if_not_in("latency_children", keys);
|
| 4105 | }
|
| 4106 | } else if (!keys || (!strstr(keys, "overhead") &&
|
| 4107 | !strstr(keys, "latency"))) {
|
| 4108 | if (symbol_conf.enable_latency)
|
| 4109 | keys = prefix_if_not_in("latency", keys);
|
| 4110 | keys = prefix_if_not_in("overhead", keys);
|
| 4111 | if (symbol_conf.cumulate_callchain) {
|
| 4112 | if (symbol_conf.enable_latency)
|
| 4113 | keys = prefix_if_not_in("latency_children", keys);
|
| 4114 | keys = prefix_if_not_in("overhead_children", keys);
|
| 4115 | }
|
| 4116 | }
|
| 4117 |
|
| 4118 | return keys;
|
| 4119 | }
|
| 4120 |
|
| 4121 | static int __setup_sorting(struct evlist *evlist, struct perf_env *env)
|
| 4122 | {
|
| 4123 | char *str;
|
| 4124 | const char *sort_keys;
|
| 4125 | int ret = 0;
|
| 4126 |
|
| 4127 | ret = setup_sort_order(evlist);
|
| 4128 | if (ret)
|
| 4129 | return ret;
|
| 4130 |
|
| 4131 | sort_keys = sort_order;
|
| 4132 | if (sort_keys == NULL) {
|
| 4133 | if (is_strict_order(field_order)) {
|
| 4134 | |
| 4135 | * If user specified field order but no sort order,
|
| 4136 | * we'll honor it and not add default sort orders.
|
| 4137 |
|
| 4138 | return 0;
|
| 4139 | }
|
| 4140 |
|
| 4141 | sort_keys = get_default_sort_order(evlist);
|
| 4142 | }
|
| 4143 |
|
| 4144 | str = strdup(sort_keys);
|
| 4145 | if (str == NULL) {
|
| 4146 | pr_err("Not enough memory to setup sort keys");
|
| 4147 | return -ENOMEM;
|
| 4148 | }
|
| 4149 |
|
| 4150 | |
| 4151 | * Prepend overhead fields for backward compatibility.
|
| 4152 |
|
| 4153 | if (!is_strict_order(field_order)) {
|
| 4154 | str = setup_overhead(str);
|
| 4155 | if (str == NULL) {
|
| 4156 | pr_err("Not enough memory to setup overhead keys");
|
| 4157 | return -ENOMEM;
|
| 4158 | }
|
| 4159 | }
|
| 4160 |
|
| 4161 | ret = setup_sort_list(&perf_hpp_list, str, evlist, env);
|
| 4162 |
|
| 4163 | free(str);
|
| 4164 | return ret;
|
| 4165 | }
|
| 4166 |
|
| 4167 | void perf_hpp__set_elide(int idx, bool elide)
|
| 4168 | {
|
| 4169 | struct perf_hpp_fmt *fmt;
|
| 4170 | struct hpp_sort_entry *hse;
|
| 4171 |
|
| 4172 | perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
|
| 4173 | if (!perf_hpp__is_sort_entry(fmt))
|
| 4174 | continue;
|
| 4175 |
|
| 4176 | hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
| 4177 | if (hse->se->se_width_idx == idx) {
|
| 4178 | fmt->elide = elide;
|
| 4179 | break;
|
| 4180 | }
|
| 4181 | }
|
| 4182 | }
|
| 4183 |
|
| 4184 | static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)
|
| 4185 | {
|
| 4186 | if (list && strlist__nr_entries(list) == 1) {
|
| 4187 | if (fp != NULL)
|
| 4188 | fprintf(fp, "# %s: %s\n", list_name,
|
| 4189 | strlist__entry(list, 0)->s);
|
| 4190 | return true;
|
| 4191 | }
|
| 4192 | return false;
|
| 4193 | }
|
| 4194 |
|
| 4195 | static bool get_elide(int idx, FILE *output)
|
| 4196 | {
|
| 4197 | switch (idx) {
|
| 4198 | case HISTC_SYMBOL:
|
| 4199 | return __get_elide(symbol_conf.sym_list, "symbol", output);
|
| 4200 | case HISTC_DSO:
|
| 4201 | return __get_elide(symbol_conf.dso_list, "dso", output);
|
| 4202 | case HISTC_COMM:
|
| 4203 | case HISTC_COMM_NODIGIT:
|
| 4204 | return __get_elide(symbol_conf.comm_list, "comm", output);
|
| 4205 | default:
|
| 4206 | break;
|
| 4207 | }
|
| 4208 |
|
| 4209 | if (sort__mode != SORT_MODE__BRANCH)
|
| 4210 | return false;
|
| 4211 |
|
| 4212 | switch (idx) {
|
| 4213 | case HISTC_SYMBOL_FROM:
|
| 4214 | return __get_elide(symbol_conf.sym_from_list, "sym_from", output);
|
| 4215 | case HISTC_SYMBOL_TO:
|
| 4216 | return __get_elide(symbol_conf.sym_to_list, "sym_to", output);
|
| 4217 | case HISTC_DSO_FROM:
|
| 4218 | return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
|
| 4219 | case HISTC_DSO_TO:
|
| 4220 | return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
|
| 4221 | case HISTC_ADDR_FROM:
|
| 4222 | return __get_elide(symbol_conf.sym_from_list, "addr_from", output);
|
| 4223 | case HISTC_ADDR_TO:
|
| 4224 | return __get_elide(symbol_conf.sym_to_list, "addr_to", output);
|
| 4225 | default:
|
| 4226 | break;
|
| 4227 | }
|
| 4228 |
|
| 4229 | return false;
|
| 4230 | }
|
| 4231 |
|
| 4232 | void sort__setup_elide(FILE *output)
|
| 4233 | {
|
| 4234 | struct perf_hpp_fmt *fmt;
|
| 4235 | struct hpp_sort_entry *hse;
|
| 4236 |
|
| 4237 | perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
|
| 4238 | if (!perf_hpp__is_sort_entry(fmt))
|
| 4239 | continue;
|
| 4240 |
|
| 4241 | hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
| 4242 | fmt->elide = get_elide(hse->se->se_width_idx, output);
|
| 4243 | }
|
| 4244 |
|
| 4245 | |
| 4246 | * It makes no sense to elide all of sort entries.
|
| 4247 | * Just revert them to show up again.
|
| 4248 |
|
| 4249 | perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
|
| 4250 | if (!perf_hpp__is_sort_entry(fmt))
|
| 4251 | continue;
|
| 4252 |
|
| 4253 | if (!fmt->elide)
|
| 4254 | return;
|
| 4255 | }
|
| 4256 |
|
| 4257 | perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
|
| 4258 | if (!perf_hpp__is_sort_entry(fmt))
|
| 4259 | continue;
|
| 4260 |
|
| 4261 | fmt->elide = false;
|
| 4262 | }
|
| 4263 | }
|
| 4264 |
|
| 4265 | int output_field_add(struct perf_hpp_list *list, const char *tok, int *level)
|
| 4266 | {
|
| 4267 | unsigned int i;
|
| 4268 |
|
| 4269 | for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
|
| 4270 | struct hpp_dimension *hd = &hpp_sort_dimensions[i];
|
| 4271 |
|
| 4272 | if (strncasecmp(tok, hd->name, strlen(tok)))
|
| 4273 | continue;
|
| 4274 |
|
| 4275 | if (!strcasecmp(tok, "weight"))
|
| 4276 | ui__warning("--fields weight shows the average value unlike in the --sort key.\n");
|
| 4277 |
|
| 4278 | if (hd->mem_mode && sort__mode != SORT_MODE__MEMORY)
|
| 4279 | continue;
|
| 4280 |
|
| 4281 | return __hpp_dimension__add_output(list, hd, *level);
|
| 4282 | }
|
| 4283 |
|
| 4284 | |
| 4285 | * A non-output field will increase level so that it can be in a
|
| 4286 | * different hierarchy.
|
| 4287 |
|
| 4288 | (*level)++;
|
| 4289 |
|
| 4290 | for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
|
| 4291 | struct sort_dimension *sd = &common_sort_dimensions[i];
|
| 4292 |
|
| 4293 | if (!sd->name || strncasecmp(tok, sd->name, strlen(tok)))
|
| 4294 | continue;
|
| 4295 |
|
| 4296 | return __sort_dimension__add_output(list, sd, *level);
|
| 4297 | }
|
| 4298 |
|
| 4299 | for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
|
| 4300 | struct sort_dimension *sd = &bstack_sort_dimensions[i];
|
| 4301 |
|
| 4302 | if (!sd->name || strncasecmp(tok, sd->name, strlen(tok)))
|
| 4303 | continue;
|
| 4304 |
|
| 4305 | if (sort__mode != SORT_MODE__BRANCH)
|
| 4306 | return -EINVAL;
|
| 4307 |
|
| 4308 | return __sort_dimension__add_output(list, sd, *level);
|
| 4309 | }
|
| 4310 |
|
| 4311 | for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
|
| 4312 | struct sort_dimension *sd = &memory_sort_dimensions[i];
|
| 4313 |
|
| 4314 | if (!sd->name || strncasecmp(tok, sd->name, strlen(tok)))
|
| 4315 | continue;
|
| 4316 |
|
| 4317 | if (sort__mode != SORT_MODE__MEMORY)
|
| 4318 | return -EINVAL;
|
| 4319 |
|
| 4320 | return __sort_dimension__add_output(list, sd, *level);
|
| 4321 | }
|
| 4322 |
|
| 4323 | return -ESRCH;
|
| 4324 | }
|
| 4325 |
|
| 4326 | static int setup_output_list(struct perf_hpp_list *list, char *str)
|
| 4327 | {
|
| 4328 | char *tmp, *tok;
|
| 4329 | int ret = 0;
|
| 4330 | int level = 0;
|
| 4331 |
|
| 4332 | for (tok = strtok_r(str, ", ", &tmp);
|
| 4333 | tok; tok = strtok_r(NULL, ", ", &tmp)) {
|
| 4334 | ret = output_field_add(list, tok, &level);
|
| 4335 | switch (ret) {
|
| 4336 | case 0:
|
| 4337 | break;
|
| 4338 | case -EINVAL:
|
| 4339 | ui__error("Invalid --fields key: `%s'", tok);
|
| 4340 | goto out;
|
| 4341 | case -ESRCH:
|
| 4342 | ui__error("Unknown --fields key: `%s'", tok);
|
| 4343 | goto out;
|
| 4344 | default: {
|
| 4345 | char buf[STRERR_BUFSIZE];
|
| 4346 |
|
| 4347 | ui__error("%s for --fields key: `%s'",
|
| 4348 | str_error_r(-ret, buf, sizeof(buf)), tok);
|
| 4349 | goto out;
|
| 4350 | }
|
| 4351 | }
|
| 4352 | }
|
| 4353 |
|
| 4354 | out:
|
| 4355 | return ret;
|
| 4356 | }
|
| 4357 |
|
| 4358 | void reset_dimensions(void)
|
| 4359 | {
|
| 4360 | unsigned int i;
|
| 4361 |
|
| 4362 | for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
|
| 4363 | common_sort_dimensions[i].taken = 0;
|
| 4364 |
|
| 4365 | for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
|
| 4366 | hpp_sort_dimensions[i].taken = 0;
|
| 4367 |
|
| 4368 | for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
|
| 4369 | bstack_sort_dimensions[i].taken = 0;
|
| 4370 |
|
| 4371 | for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
|
| 4372 | memory_sort_dimensions[i].taken = 0;
|
| 4373 | }
|
| 4374 |
|
| 4375 | bool is_strict_order(const char *order)
|
| 4376 | {
|
| 4377 | return order && (*order != '+');
|
| 4378 | }
|
| 4379 |
|
| 4380 | static int __setup_output_field(void)
|
| 4381 | {
|
| 4382 | char *str, *strp;
|
| 4383 | int ret = -EINVAL;
|
| 4384 |
|
| 4385 | if (field_order == NULL)
|
| 4386 | return 0;
|
| 4387 |
|
| 4388 | strp = str = strdup(field_order);
|
| 4389 | if (str == NULL) {
|
| 4390 | pr_err("Not enough memory to setup output fields");
|
| 4391 | return -ENOMEM;
|
| 4392 | }
|
| 4393 |
|
| 4394 | if (!is_strict_order(field_order))
|
| 4395 | strp++;
|
| 4396 |
|
| 4397 | if (!strlen(strp)) {
|
| 4398 | ui__error("Invalid --fields key: `+'");
|
| 4399 | goto out;
|
| 4400 | }
|
| 4401 |
|
| 4402 | ret = setup_output_list(&perf_hpp_list, strp);
|
| 4403 |
|
| 4404 | out:
|
| 4405 | free(str);
|
| 4406 | return ret;
|
| 4407 | }
|
| 4408 |
|
| 4409 | int setup_sorting(struct evlist *evlist, struct perf_env *env)
|
| 4410 | {
|
| 4411 | int err;
|
| 4412 |
|
| 4413 | err = __setup_sorting(evlist, env);
|
| 4414 | if (err < 0)
|
| 4415 | return err;
|
| 4416 |
|
| 4417 | if (parent_pattern != default_parent_pattern) {
|
| 4418 | err = sort_dimension__add(&perf_hpp_list, "parent", evlist, env, -1);
|
| 4419 | if (err < 0)
|
| 4420 | return err;
|
| 4421 | }
|
| 4422 |
|
| 4423 | reset_dimensions();
|
| 4424 |
|
| 4425 | |
| 4426 | * perf diff doesn't use default hpp output fields.
|
| 4427 |
|
| 4428 | if (sort__mode != SORT_MODE__DIFF)
|
| 4429 | perf_hpp__init();
|
| 4430 |
|
| 4431 | err = __setup_output_field();
|
| 4432 | if (err < 0)
|
| 4433 | return err;
|
| 4434 |
|
| 4435 | err = perf_hpp__alloc_mem_stats(&perf_hpp_list, evlist);
|
| 4436 | if (err < 0)
|
| 4437 | return err;
|
| 4438 |
|
| 4439 |
|
| 4440 | perf_hpp__setup_output_field(&perf_hpp_list);
|
| 4441 |
|
| 4442 | perf_hpp__append_sort_keys(&perf_hpp_list);
|
| 4443 |
|
| 4444 |
|
| 4445 | if (perf_hpp__setup_hists_formats(&perf_hpp_list, evlist) < 0)
|
| 4446 | return -1;
|
| 4447 |
|
| 4448 | return 0;
|
| 4449 | }
|
| 4450 |
|
| 4451 | void reset_output_field(void)
|
| 4452 | {
|
| 4453 | perf_hpp_list.need_collapse = 0;
|
| 4454 | perf_hpp_list.parent = 0;
|
| 4455 | perf_hpp_list.sym = 0;
|
| 4456 | perf_hpp_list.dso = 0;
|
| 4457 |
|
| 4458 | field_order = NULL;
|
| 4459 | sort_order = NULL;
|
| 4460 |
|
| 4461 | reset_dimensions();
|
| 4462 | perf_hpp__reset_output_field(&perf_hpp_list);
|
| 4463 | }
|
| 4464 |
|
| 4465 | #define INDENT (3*8 + 1)
|
| 4466 |
|
| 4467 | static void add_key(struct strbuf *sb, const char *str, int *llen)
|
| 4468 | {
|
| 4469 | if (!str)
|
| 4470 | return;
|
| 4471 |
|
| 4472 | if (*llen >= 75) {
|
| 4473 | strbuf_addstr(sb, "\n\t\t\t ");
|
| 4474 | *llen = INDENT;
|
| 4475 | }
|
| 4476 | strbuf_addf(sb, " %s", str);
|
| 4477 | *llen += strlen(str) + 1;
|
| 4478 | }
|
| 4479 |
|
| 4480 | static void add_sort_string(struct strbuf *sb, struct sort_dimension *s, int n,
|
| 4481 | int *llen)
|
| 4482 | {
|
| 4483 | int i;
|
| 4484 |
|
| 4485 | for (i = 0; i < n; i++)
|
| 4486 | add_key(sb, s[i].name, llen);
|
| 4487 | }
|
| 4488 |
|
| 4489 | static void add_hpp_sort_string(struct strbuf *sb, struct hpp_dimension *s, int n,
|
| 4490 | int *llen)
|
| 4491 | {
|
| 4492 | int i;
|
| 4493 |
|
| 4494 | for (i = 0; i < n; i++)
|
| 4495 | add_key(sb, s[i].name, llen);
|
| 4496 | }
|
| 4497 |
|
| 4498 | char *sort_help(const char *prefix, enum sort_mode mode)
|
| 4499 | {
|
| 4500 | struct strbuf sb;
|
| 4501 | char *s;
|
| 4502 | int len = strlen(prefix) + INDENT;
|
| 4503 |
|
| 4504 | strbuf_init(&sb, 300);
|
| 4505 | strbuf_addstr(&sb, prefix);
|
| 4506 | add_hpp_sort_string(&sb, hpp_sort_dimensions,
|
| 4507 | ARRAY_SIZE(hpp_sort_dimensions), &len);
|
| 4508 | add_sort_string(&sb, common_sort_dimensions,
|
| 4509 | ARRAY_SIZE(common_sort_dimensions), &len);
|
| 4510 | if (mode == SORT_MODE__NORMAL || mode == SORT_MODE__BRANCH)
|
| 4511 | add_sort_string(&sb, bstack_sort_dimensions,
|
| 4512 | ARRAY_SIZE(bstack_sort_dimensions), &len);
|
| 4513 | if (mode == SORT_MODE__NORMAL || mode == SORT_MODE__MEMORY)
|
| 4514 | add_sort_string(&sb, memory_sort_dimensions,
|
| 4515 | ARRAY_SIZE(memory_sort_dimensions), &len);
|
| 4516 | s = strbuf_detach(&sb, NULL);
|
| 4517 | strbuf_release(&sb);
|
| 4518 | return s;
|
| 4519 | }
|