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