Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf report: Add comm_nodigit sort key

The "comm" column allows grouping events by the process command. It is
intended to group like programs, despite having different PIDs. But some
workloads may adjust their own command, so that a unique identifier
(e.g. a PID or some other numeric value) is part of the command name.
This destroys the utility of "comm", forcing perf to place each unique
process name into its own bucket, which can contribute to a
combinatorial explosion of memory use in perf report.

Create a less strict version of this column, which ignores digits when
comparing command names. Commands whose names are the same (ignoring
digits) are sorted into the same histogram buckets, and displayed with
the placeholder value "<N>" in the place of digits. For example,
hypothetical command names "kworker/1" "kworker/2" "kworker/3" would
sort into the same bucket and be represented as "kworker/<N>".

Committer testing:

$ perf report -s comm,comm_nodigit | grep -F "<N>"
0.01% CPU 6/TCG CPU <N>/TCG
0.01% kworker/53:2-mm kworker/<N>:<N>-mm
0.01% migration/24 migration/<N>
0.01% kworker/24:1-ev kworker/<N>:<N>-ev
0.01% llvmpipe-8 llvmpipe-<N>

Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

authored by

Stephen Brennan and committed by
Namhyung Kim
e397dd81 44311ae8

+123 -1
+2 -1
tools/perf/Documentation/perf-report.txt
··· 88 88 Sort histogram entries by given key(s) - multiple keys can be specified 89 89 in CSV format. Following sort keys are available: 90 90 pid, comm, dso, symbol, parent, cpu, socket, srcline, weight, 91 - local_weight, cgroup_id, addr. 91 + local_weight, cgroup_id, addr, comm_nodigit. 92 92 93 93 Each key has following meaning: 94 94 ··· 143 143 - weight1: Average value of event specific weight (1st field of weight_struct). 144 144 - weight2: Average value of event specific weight (2nd field of weight_struct). 145 145 - weight3: Average value of event specific weight (3rd field of weight_struct). 146 + - comm_nodigit: same as comm, with numbers replaced by "<N>" 146 147 147 148 By default, overhead, comm, dso and symbol keys are used. 148 149 (i.e. --sort overhead,comm,dso,symbol).
+3
tools/perf/util/hist.c
··· 110 110 len = thread__comm_len(h->thread); 111 111 if (hists__new_col_len(hists, HISTC_COMM, len)) 112 112 hists__set_col_len(hists, HISTC_THREAD, len + 8); 113 + if (hists->hpp_list->comm_nodigit) 114 + hists__new_col_len(hists, HISTC_COMM_NODIGIT, 115 + (u16) sort__comm_nodigit_len(h)); 113 116 114 117 if (h->ms.map) { 115 118 len = dso__name_len(map__dso(h->ms.map));
+2
tools/perf/util/hist.h
··· 44 44 HISTC_THREAD, 45 45 HISTC_TGID, 46 46 HISTC_COMM, 47 + HISTC_COMM_NODIGIT, 47 48 HISTC_CGROUP_ID, 48 49 HISTC_CGROUP, 49 50 HISTC_PARENT, ··· 523 522 int socket; 524 523 int thread; 525 524 int comm; 525 + int comm_nodigit; 526 526 }; 527 527 528 528 extern struct perf_hpp_list perf_hpp_list;
+114
tools/perf/util/sort.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 + #include <ctype.h> 2 3 #include <errno.h> 3 4 #include <inttypes.h> 4 5 #include <regex.h> ··· 264 263 .se_snprintf = hist_entry__comm_snprintf, 265 264 .se_filter = hist_entry__thread_filter, 266 265 .se_width_idx = HISTC_COMM, 266 + }; 267 + 268 + /* --sort comm_nodigit */ 269 + 270 + size_t sort__comm_nodigit_len(struct hist_entry *entry) 271 + { 272 + const char *comm = comm__str(entry->comm); 273 + size_t index, len_nodigit = 0; 274 + bool in_number = false; 275 + 276 + if (!comm) 277 + return 0; 278 + 279 + for (index = 0; comm[index]; index++) { 280 + if (!isdigit((unsigned char)comm[index])) { 281 + in_number = false; 282 + len_nodigit++; 283 + } else if (!in_number) { 284 + in_number = true; 285 + len_nodigit += 3; /* <N> */ 286 + } 287 + } 288 + 289 + return len_nodigit; 290 + } 291 + 292 + static int64_t strcmp_nodigit(const char *left, const char *right) 293 + { 294 + for (;;) { 295 + while (*left && isdigit((unsigned char)*left)) 296 + left++; 297 + while (*right && isdigit((unsigned char)*right)) 298 + right++; 299 + if (*left == *right && !*left) { 300 + return 0; 301 + } else if (*left == *right) { 302 + left++; 303 + right++; 304 + } else { 305 + return (int64_t)((unsigned char)*left - (unsigned char)*right); 306 + } 307 + } 308 + } 309 + 310 + static int64_t 311 + sort__comm_nodigit_cmp(struct hist_entry *left, struct hist_entry *right) 312 + { 313 + return strcmp_nodigit(comm__str(right->comm), comm__str(left->comm)); 314 + } 315 + 316 + static int64_t 317 + sort__comm_nodigit_collapse(struct hist_entry *left, struct hist_entry *right) 318 + { 319 + return strcmp_nodigit(comm__str(right->comm), comm__str(left->comm)); 320 + } 321 + 322 + static int64_t 323 + sort__comm_nodigit_sort(struct hist_entry *left, struct hist_entry *right) 324 + { 325 + return strcmp_nodigit(comm__str(right->comm), comm__str(left->comm)); 326 + } 327 + 328 + static int hist_entry__comm_nodigit_snprintf(struct hist_entry *he, char *bf, 329 + size_t size, unsigned int width) 330 + { 331 + int ret = 0; 332 + unsigned int print_len, printed = 0, start = 0, end = 0; 333 + bool in_digit; 334 + const char *comm = comm__str(he->comm), *print; 335 + 336 + while (printed < width && printed < size && comm[start]) { 337 + in_digit = !!isdigit((unsigned char)comm[start]); 338 + end = start + 1; 339 + while (comm[end] && !!isdigit((unsigned char)comm[end]) == in_digit) 340 + end++; 341 + if (in_digit) { 342 + print_len = 3; /* <N> */ 343 + print = "<N>"; 344 + } else { 345 + print_len = end - start; 346 + print = &comm[start]; 347 + } 348 + print_len = min(print_len, width - printed); 349 + ret = repsep_snprintf(bf + printed, size - printed, "%-.*s", 350 + print_len, print); 351 + if (ret < 0) 352 + return ret; 353 + start = end; 354 + printed += ret; 355 + } 356 + /* Pad to width if necessary */ 357 + if (printed < width && printed < size) { 358 + ret = repsep_snprintf(bf + printed, size - printed, "%-*.*s", 359 + width - printed, width - printed, ""); 360 + if (ret < 0) 361 + return ret; 362 + printed += ret; 363 + } 364 + return printed; 365 + } 366 + 367 + struct sort_entry sort_comm_nodigit = { 368 + .se_header = "CommandNoDigit", 369 + .se_cmp = sort__comm_nodigit_cmp, 370 + .se_collapse = sort__comm_nodigit_collapse, 371 + .se_sort = sort__comm_nodigit_sort, 372 + .se_snprintf = hist_entry__comm_nodigit_snprintf, 373 + .se_filter = hist_entry__thread_filter, 374 + .se_width_idx = HISTC_COMM_NODIGIT, 267 375 }; 268 376 269 377 /* --sort dso */ ··· 2693 2583 DIM(SORT_PID, "pid", sort_thread), 2694 2584 DIM(SORT_TGID, "tgid", sort_tgid), 2695 2585 DIM(SORT_COMM, "comm", sort_comm), 2586 + DIM(SORT_COMM_NODIGIT, "comm_nodigit", sort_comm_nodigit), 2696 2587 DIM(SORT_DSO, "dso", sort_dso), 2697 2588 DIM(SORT_SYM, "symbol", sort_sym), 2698 2589 DIM(SORT_PARENT, "parent", sort_parent), ··· 3690 3579 list->thread = 1; 3691 3580 } else if (sd->entry == &sort_comm) { 3692 3581 list->comm = 1; 3582 + } else if (sd->entry == &sort_comm_nodigit) { 3583 + list->comm_nodigit = list->comm = 1; 3693 3584 } else if (sd->entry == &sort_type_offset) { 3694 3585 symbol_conf.annotate_data_member = true; 3695 3586 } else if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) { ··· 4153 4040 case HISTC_DSO: 4154 4041 return __get_elide(symbol_conf.dso_list, "dso", output); 4155 4042 case HISTC_COMM: 4043 + case HISTC_COMM_NODIGIT: 4156 4044 return __get_elide(symbol_conf.comm_list, "comm", output); 4157 4045 default: 4158 4046 break;
+2
tools/perf/util/sort.h
··· 43 43 /* common sort keys */ 44 44 SORT_PID, 45 45 SORT_COMM, 46 + SORT_COMM_NODIGIT, 46 47 SORT_DSO, 47 48 SORT_SYM, 48 49 SORT_PARENT, ··· 159 158 int64_t 160 159 _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r); 161 160 char *hist_entry__srcline(struct hist_entry *he); 161 + size_t sort__comm_nodigit_len(struct hist_entry *entry); 162 162 #endif /* __PERF_SORT_H */