1 #include <math.h> 2 3 #include "../util/hist.h" 4 #include "../util/util.h" 5 #include "../util/sort.h" 6 #include "../util/evsel.h" 7 8 /* hist period print (hpp) functions */ 9 10 typedef int (*hpp_snprint_fn)(char *buf, size_t size, const char *fmt, ...); 11 12 static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, 13 u64 (*get_field)(struct hist_entry *), 14 const char *fmt, hpp_snprint_fn print_fn, 15 bool fmt_percent) 16 { 17 int ret; 18 struct hists *hists = he->hists; 19 20 if (fmt_percent) { 21 double percent = 0.0; 22 23 if (hists->stats.total_period) 24 percent = 100.0 * get_field(he) / 25 hists->stats.total_period; 26 27 ret = print_fn(hpp->buf, hpp->size, fmt, percent); 28 } else 29 ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he)); 30 31 if (symbol_conf.event_group) { 32 int prev_idx, idx_delta; 33 struct perf_evsel *evsel = hists_to_evsel(hists); 34 struct hist_entry *pair; 35 int nr_members = evsel->nr_members; 36 37 if (nr_members <= 1) 38 return ret; 39 40 prev_idx = perf_evsel__group_idx(evsel); 41 42 list_for_each_entry(pair, &he->pairs.head, pairs.node) { 43 u64 period = get_field(pair); 44 u64 total = pair->hists->stats.total_period; 45 46 if (!total) 47 continue; 48 49 evsel = hists_to_evsel(pair->hists); 50 idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1; 51 52 while (idx_delta--) { 53 /* 54 * zero-fill group members in the middle which 55 * have no sample 56 */ 57 ret += print_fn(hpp->buf + ret, hpp->size - ret, 58 fmt, 0); 59 } 60 61 if (fmt_percent) 62 ret += print_fn(hpp->buf + ret, hpp->size - ret, 63 fmt, 100.0 * period / total); 64 else 65 ret += print_fn(hpp->buf + ret, hpp->size - ret, 66 fmt, period); 67 68 prev_idx = perf_evsel__group_idx(evsel); 69 } 70 71 idx_delta = nr_members - prev_idx - 1; 72 73 while (idx_delta--) { 74 /* 75 * zero-fill group members at last which have no sample 76 */ 77 ret += print_fn(hpp->buf + ret, hpp->size - ret, 78 fmt, 0); 79 } 80 } 81 return ret; 82 } 83 84 #define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ 85 static int hpp__header_##_type(struct perf_hpp *hpp) \ 86 { \ 87 int len = _min_width; \ 88 \ 89 if (symbol_conf.event_group) { \ 90 struct perf_evsel *evsel = hpp->ptr; \ 91 \ 92 len = max(len, evsel->nr_members * _unit_width); \ 93 } \ 94 return scnprintf(hpp->buf, hpp->size, "%*s", len, _str); \ 95 } 96 97 #define __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ 98 static int hpp__width_##_type(struct perf_hpp *hpp __maybe_unused) \ 99 { \ 100 int len = _min_width; \ 101 \ 102 if (symbol_conf.event_group) { \ 103 struct perf_evsel *evsel = hpp->ptr; \ 104 \ 105 len = max(len, evsel->nr_members * _unit_width); \ 106 } \ 107 return len; \ 108 } 109 110 #define __HPP_COLOR_PERCENT_FN(_type, _field) \ 111 static u64 he_get_##_field(struct hist_entry *he) \ 112 { \ 113 return he->stat._field; \ 114 } \ 115 \ 116 static int hpp__color_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ 117 { \ 118 return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \ 119 (hpp_snprint_fn)percent_color_snprintf, true); \ 120 } 121 122 #define __HPP_ENTRY_PERCENT_FN(_type, _field) \ 123 static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ 124 { \ 125 const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ 126 return __hpp__fmt(hpp, he, he_get_##_field, fmt, \ 127 scnprintf, true); \ 128 } 129 130 #define __HPP_ENTRY_RAW_FN(_type, _field) \ 131 static u64 he_get_raw_##_field(struct hist_entry *he) \ 132 { \ 133 return he->stat._field; \ 134 } \ 135 \ 136 static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ 137 { \ 138 const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \ 139 return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf, false); \ 140 } 141 142 #define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \ 143 __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ 144 __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ 145 __HPP_COLOR_PERCENT_FN(_type, _field) \ 146 __HPP_ENTRY_PERCENT_FN(_type, _field) 147 148 #define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \ 149 __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ 150 __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ 151 __HPP_ENTRY_RAW_FN(_type, _field) 152 153 154 HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8) 155 HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8) 156 HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8) 157 HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8) 158 HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8) 159 160 HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) 161 HPP_RAW_FNS(period, "Period", period, 12, 12) 162 163 164 static int hpp__header_baseline(struct perf_hpp *hpp) 165 { 166 return scnprintf(hpp->buf, hpp->size, "Baseline"); 167 } 168 169 static int hpp__width_baseline(struct perf_hpp *hpp __maybe_unused) 170 { 171 return 8; 172 } 173 174 static double baseline_percent(struct hist_entry *he) 175 { 176 struct hist_entry *pair = hist_entry__next_pair(he); 177 struct hists *pair_hists = pair ? pair->hists : NULL; 178 double percent = 0.0; 179 180 if (pair) { 181 u64 total_period = pair_hists->stats.total_period; 182 u64 base_period = pair->stat.period; 183 184 percent = 100.0 * base_period / total_period; 185 } 186 187 return percent; 188 } 189 190 static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he) 191 { 192 double percent = baseline_percent(he); 193 194 if (hist_entry__has_pairs(he) || symbol_conf.field_sep) 195 return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); 196 else 197 return scnprintf(hpp->buf, hpp->size, " "); 198 } 199 200 static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he) 201 { 202 double percent = baseline_percent(he); 203 const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%"; 204 205 if (hist_entry__has_pairs(he) || symbol_conf.field_sep) 206 return scnprintf(hpp->buf, hpp->size, fmt, percent); 207 else 208 return scnprintf(hpp->buf, hpp->size, " "); 209 } 210 211 static int hpp__header_period_baseline(struct perf_hpp *hpp) 212 { 213 const char *fmt = symbol_conf.field_sep ? "%s" : "%12s"; 214 215 return scnprintf(hpp->buf, hpp->size, fmt, "Period Base"); 216 } 217 218 static int hpp__width_period_baseline(struct perf_hpp *hpp __maybe_unused) 219 { 220 return 12; 221 } 222 223 static int hpp__entry_period_baseline(struct perf_hpp *hpp, struct hist_entry *he) 224 { 225 struct hist_entry *pair = hist_entry__next_pair(he); 226 u64 period = pair ? pair->stat.period : 0; 227 const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64; 228 229 return scnprintf(hpp->buf, hpp->size, fmt, period); 230 } 231 232 static int hpp__header_delta(struct perf_hpp *hpp) 233 { 234 const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; 235 236 return scnprintf(hpp->buf, hpp->size, fmt, "Delta"); 237 } 238 239 static int hpp__width_delta(struct perf_hpp *hpp __maybe_unused) 240 { 241 return 7; 242 } 243 244 static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he) 245 { 246 struct hist_entry *pair = hist_entry__next_pair(he); 247 const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s"; 248 char buf[32] = " "; 249 double diff = 0.0; 250 251 if (pair) { 252 if (he->diff.computed) 253 diff = he->diff.period_ratio_delta; 254 else 255 diff = perf_diff__compute_delta(he, pair); 256 } else 257 diff = perf_diff__period_percent(he, he->stat.period); 258 259 if (fabs(diff) >= 0.01) 260 scnprintf(buf, sizeof(buf), "%+4.2F%%", diff); 261 262 return scnprintf(hpp->buf, hpp->size, fmt, buf); 263 } 264 265 static int hpp__header_ratio(struct perf_hpp *hpp) 266 { 267 const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; 268 269 return scnprintf(hpp->buf, hpp->size, fmt, "Ratio"); 270 } 271 272 static int hpp__width_ratio(struct perf_hpp *hpp __maybe_unused) 273 { 274 return 14; 275 } 276 277 static int hpp__entry_ratio(struct perf_hpp *hpp, struct hist_entry *he) 278 { 279 struct hist_entry *pair = hist_entry__next_pair(he); 280 const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; 281 char buf[32] = " "; 282 double ratio = 0.0; 283 284 if (pair) { 285 if (he->diff.computed) 286 ratio = he->diff.period_ratio; 287 else 288 ratio = perf_diff__compute_ratio(he, pair); 289 } 290 291 if (ratio > 0.0) 292 scnprintf(buf, sizeof(buf), "%+14.6F", ratio); 293 294 return scnprintf(hpp->buf, hpp->size, fmt, buf); 295 } 296 297 static int hpp__header_wdiff(struct perf_hpp *hpp) 298 { 299 const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; 300 301 return scnprintf(hpp->buf, hpp->size, fmt, "Weighted diff"); 302 } 303 304 static int hpp__width_wdiff(struct perf_hpp *hpp __maybe_unused) 305 { 306 return 14; 307 } 308 309 static int hpp__entry_wdiff(struct perf_hpp *hpp, struct hist_entry *he) 310 { 311 struct hist_entry *pair = hist_entry__next_pair(he); 312 const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; 313 char buf[32] = " "; 314 s64 wdiff = 0; 315 316 if (pair) { 317 if (he->diff.computed) 318 wdiff = he->diff.wdiff; 319 else 320 wdiff = perf_diff__compute_wdiff(he, pair); 321 } 322 323 if (wdiff != 0) 324 scnprintf(buf, sizeof(buf), "%14ld", wdiff); 325 326 return scnprintf(hpp->buf, hpp->size, fmt, buf); 327 } 328 329 static int hpp__header_formula(struct perf_hpp *hpp) 330 { 331 const char *fmt = symbol_conf.field_sep ? "%s" : "%70s"; 332 333 return scnprintf(hpp->buf, hpp->size, fmt, "Formula"); 334 } 335 336 static int hpp__width_formula(struct perf_hpp *hpp __maybe_unused) 337 { 338 return 70; 339 } 340 341 static int hpp__entry_formula(struct perf_hpp *hpp, struct hist_entry *he) 342 { 343 struct hist_entry *pair = hist_entry__next_pair(he); 344 const char *fmt = symbol_conf.field_sep ? "%s" : "%-70s"; 345 char buf[96] = " "; 346 347 if (pair) 348 perf_diff__formula(he, pair, buf, sizeof(buf)); 349 350 return scnprintf(hpp->buf, hpp->size, fmt, buf); 351 } 352 353 #define HPP__COLOR_PRINT_FNS(_name) \ 354 { \ 355 .header = hpp__header_ ## _name, \ 356 .width = hpp__width_ ## _name, \ 357 .color = hpp__color_ ## _name, \ 358 .entry = hpp__entry_ ## _name \ 359 } 360 361 #define HPP__PRINT_FNS(_name) \ 362 { \ 363 .header = hpp__header_ ## _name, \ 364 .width = hpp__width_ ## _name, \ 365 .entry = hpp__entry_ ## _name \ 366 } 367 368 struct perf_hpp_fmt perf_hpp__format[] = { 369 HPP__COLOR_PRINT_FNS(baseline), 370 HPP__COLOR_PRINT_FNS(overhead), 371 HPP__COLOR_PRINT_FNS(overhead_sys), 372 HPP__COLOR_PRINT_FNS(overhead_us), 373 HPP__COLOR_PRINT_FNS(overhead_guest_sys), 374 HPP__COLOR_PRINT_FNS(overhead_guest_us), 375 HPP__PRINT_FNS(samples), 376 HPP__PRINT_FNS(period), 377 HPP__PRINT_FNS(period_baseline), 378 HPP__PRINT_FNS(delta), 379 HPP__PRINT_FNS(ratio), 380 HPP__PRINT_FNS(wdiff), 381 HPP__PRINT_FNS(formula) 382 }; 383 384 LIST_HEAD(perf_hpp__list); 385 386 387 #undef HPP__COLOR_PRINT_FNS 388 #undef HPP__PRINT_FNS 389 390 #undef HPP_PERCENT_FNS 391 #undef HPP_RAW_FNS 392 393 #undef __HPP_HEADER_FN 394 #undef __HPP_WIDTH_FN 395 #undef __HPP_COLOR_PERCENT_FN 396 #undef __HPP_ENTRY_PERCENT_FN 397 #undef __HPP_ENTRY_RAW_FN 398 399 400 void perf_hpp__init(void) 401 { 402 if (symbol_conf.show_cpu_utilization) { 403 perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS); 404 perf_hpp__column_enable(PERF_HPP__OVERHEAD_US); 405 406 if (perf_guest) { 407 perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_SYS); 408 perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_US); 409 } 410 } 411 412 if (symbol_conf.show_nr_samples) 413 perf_hpp__column_enable(PERF_HPP__SAMPLES); 414 415 if (symbol_conf.show_total_period) 416 perf_hpp__column_enable(PERF_HPP__PERIOD); 417 } 418 419 void perf_hpp__column_register(struct perf_hpp_fmt *format) 420 { 421 list_add_tail(&format->list, &perf_hpp__list); 422 } 423 424 void perf_hpp__column_enable(unsigned col) 425 { 426 BUG_ON(col >= PERF_HPP__MAX_INDEX); 427 perf_hpp__column_register(&perf_hpp__format[col]); 428 } 429 430 static inline void advance_hpp(struct perf_hpp *hpp, int inc) 431 { 432 hpp->buf += inc; 433 hpp->size -= inc; 434 } 435 436 int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, 437 bool color) 438 { 439 const char *sep = symbol_conf.field_sep; 440 struct perf_hpp_fmt *fmt; 441 char *start = hpp->buf; 442 int ret; 443 bool first = true; 444 445 if (symbol_conf.exclude_other && !he->parent) 446 return 0; 447 448 perf_hpp__for_each_format(fmt) { 449 /* 450 * If there's no field_sep, we still need 451 * to display initial ' '. 452 */ 453 if (!sep || !first) { 454 ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " "); 455 advance_hpp(hpp, ret); 456 } else 457 first = false; 458 459 if (color && fmt->color) 460 ret = fmt->color(hpp, he); 461 else 462 ret = fmt->entry(hpp, he); 463 464 advance_hpp(hpp, ret); 465 } 466 467 return hpp->buf - start; 468 } 469 470 int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size, 471 struct hists *hists) 472 { 473 const char *sep = symbol_conf.field_sep; 474 struct sort_entry *se; 475 int ret = 0; 476 477 list_for_each_entry(se, &hist_entry__sort_list, list) { 478 if (se->elide) 479 continue; 480 481 ret += scnprintf(s + ret, size - ret, "%s", sep ?: " "); 482 ret += se->se_snprintf(he, s + ret, size - ret, 483 hists__col_len(hists, se->se_width_idx)); 484 } 485 486 return ret; 487 } 488 489 /* 490 * See hists__fprintf to match the column widths 491 */ 492 unsigned int hists__sort_list_width(struct hists *hists) 493 { 494 struct perf_hpp_fmt *fmt; 495 struct sort_entry *se; 496 int i = 0, ret = 0; 497 struct perf_hpp dummy_hpp = { 498 .ptr = hists_to_evsel(hists), 499 }; 500 501 perf_hpp__for_each_format(fmt) { 502 if (i) 503 ret += 2; 504 505 ret += fmt->width(&dummy_hpp); 506 } 507 508 list_for_each_entry(se, &hist_entry__sort_list, list) 509 if (!se->elide) 510 ret += 2 + hists__col_len(hists, se->se_width_idx); 511 512 if (verbose) /* Addr + origin */ 513 ret += 3 + BITS_PER_LONG / 4; 514 515 return ret; 516 } 517