diff options
| author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2009-12-16 09:27:09 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-12-16 10:51:50 -0500 |
| commit | 4ecf84d086fbeca5a622e971fff013b291dbde86 (patch) | |
| tree | ea482bdd8006c0318d2236737c5868af859c2179 /tools/perf/builtin-report.c | |
| parent | d599db3fc5dd4f1e8432fdbc6d899584b25f4dff (diff) | |
perf tools: Move hist entries printing routines from perf report
Will be used in other tools such as 'perf diff'.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1260973631-28035-1-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/builtin-report.c')
| -rw-r--r-- | tools/perf/builtin-report.c | 397 |
1 files changed, 0 insertions, 397 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 26f4de6d9a51..24d20e7d125a 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -43,316 +43,6 @@ static char *pretty_printing_style = default_pretty_printing_style; | |||
| 43 | 43 | ||
| 44 | static char callchain_default_opt[] = "fractal,0.5"; | 44 | static char callchain_default_opt[] = "fractal,0.5"; |
| 45 | 45 | ||
| 46 | static size_t | ||
| 47 | callchain__fprintf_left_margin(FILE *fp, int left_margin) | ||
| 48 | { | ||
| 49 | int i; | ||
| 50 | int ret; | ||
| 51 | |||
| 52 | ret = fprintf(fp, " "); | ||
| 53 | |||
| 54 | for (i = 0; i < left_margin; i++) | ||
| 55 | ret += fprintf(fp, " "); | ||
| 56 | |||
| 57 | return ret; | ||
| 58 | } | ||
| 59 | |||
| 60 | static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, | ||
| 61 | int left_margin) | ||
| 62 | { | ||
| 63 | int i; | ||
| 64 | size_t ret = 0; | ||
| 65 | |||
| 66 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 67 | |||
| 68 | for (i = 0; i < depth; i++) | ||
| 69 | if (depth_mask & (1 << i)) | ||
| 70 | ret += fprintf(fp, "| "); | ||
| 71 | else | ||
| 72 | ret += fprintf(fp, " "); | ||
| 73 | |||
| 74 | ret += fprintf(fp, "\n"); | ||
| 75 | |||
| 76 | return ret; | ||
| 77 | } | ||
| 78 | static size_t | ||
| 79 | ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth, | ||
| 80 | int depth_mask, int count, u64 total_samples, | ||
| 81 | int hits, int left_margin) | ||
| 82 | { | ||
| 83 | int i; | ||
| 84 | size_t ret = 0; | ||
| 85 | |||
| 86 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 87 | for (i = 0; i < depth; i++) { | ||
| 88 | if (depth_mask & (1 << i)) | ||
| 89 | ret += fprintf(fp, "|"); | ||
| 90 | else | ||
| 91 | ret += fprintf(fp, " "); | ||
| 92 | if (!count && i == depth - 1) { | ||
| 93 | double percent; | ||
| 94 | |||
| 95 | percent = hits * 100.0 / total_samples; | ||
| 96 | ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); | ||
| 97 | } else | ||
| 98 | ret += fprintf(fp, "%s", " "); | ||
| 99 | } | ||
| 100 | if (chain->sym) | ||
| 101 | ret += fprintf(fp, "%s\n", chain->sym->name); | ||
| 102 | else | ||
| 103 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); | ||
| 104 | |||
| 105 | return ret; | ||
| 106 | } | ||
| 107 | |||
| 108 | static struct symbol *rem_sq_bracket; | ||
| 109 | static struct callchain_list rem_hits; | ||
| 110 | |||
| 111 | static void init_rem_hits(void) | ||
| 112 | { | ||
| 113 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
| 114 | if (!rem_sq_bracket) { | ||
| 115 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
| 116 | return; | ||
| 117 | } | ||
| 118 | |||
| 119 | strcpy(rem_sq_bracket->name, "[...]"); | ||
| 120 | rem_hits.sym = rem_sq_bracket; | ||
| 121 | } | ||
| 122 | |||
| 123 | static size_t | ||
| 124 | __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
| 125 | u64 total_samples, int depth, int depth_mask, | ||
| 126 | int left_margin) | ||
| 127 | { | ||
| 128 | struct rb_node *node, *next; | ||
| 129 | struct callchain_node *child; | ||
| 130 | struct callchain_list *chain; | ||
| 131 | int new_depth_mask = depth_mask; | ||
| 132 | u64 new_total; | ||
| 133 | u64 remaining; | ||
| 134 | size_t ret = 0; | ||
| 135 | int i; | ||
| 136 | |||
| 137 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
| 138 | new_total = self->children_hit; | ||
| 139 | else | ||
| 140 | new_total = total_samples; | ||
| 141 | |||
| 142 | remaining = new_total; | ||
| 143 | |||
| 144 | node = rb_first(&self->rb_root); | ||
| 145 | while (node) { | ||
| 146 | u64 cumul; | ||
| 147 | |||
| 148 | child = rb_entry(node, struct callchain_node, rb_node); | ||
| 149 | cumul = cumul_hits(child); | ||
| 150 | remaining -= cumul; | ||
| 151 | |||
| 152 | /* | ||
| 153 | * The depth mask manages the output of pipes that show | ||
| 154 | * the depth. We don't want to keep the pipes of the current | ||
| 155 | * level for the last child of this depth. | ||
| 156 | * Except if we have remaining filtered hits. They will | ||
| 157 | * supersede the last child | ||
| 158 | */ | ||
| 159 | next = rb_next(node); | ||
| 160 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) | ||
| 161 | new_depth_mask &= ~(1 << (depth - 1)); | ||
| 162 | |||
| 163 | /* | ||
| 164 | * But we keep the older depth mask for the line seperator | ||
| 165 | * to keep the level link until we reach the last child | ||
| 166 | */ | ||
| 167 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, | ||
| 168 | left_margin); | ||
| 169 | i = 0; | ||
| 170 | list_for_each_entry(chain, &child->val, list) { | ||
| 171 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
| 172 | continue; | ||
| 173 | ret += ipchain__fprintf_graph(fp, chain, depth, | ||
| 174 | new_depth_mask, i++, | ||
| 175 | new_total, | ||
| 176 | cumul, | ||
| 177 | left_margin); | ||
| 178 | } | ||
| 179 | ret += __callchain__fprintf_graph(fp, child, new_total, | ||
| 180 | depth + 1, | ||
| 181 | new_depth_mask | (1 << depth), | ||
| 182 | left_margin); | ||
| 183 | node = next; | ||
| 184 | } | ||
| 185 | |||
| 186 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
| 187 | remaining && remaining != new_total) { | ||
| 188 | |||
| 189 | if (!rem_sq_bracket) | ||
| 190 | return ret; | ||
| 191 | |||
| 192 | new_depth_mask &= ~(1 << (depth - 1)); | ||
| 193 | |||
| 194 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
| 195 | new_depth_mask, 0, new_total, | ||
| 196 | remaining, left_margin); | ||
| 197 | } | ||
| 198 | |||
| 199 | return ret; | ||
| 200 | } | ||
| 201 | |||
| 202 | |||
| 203 | static size_t | ||
| 204 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
| 205 | u64 total_samples, int left_margin) | ||
| 206 | { | ||
| 207 | struct callchain_list *chain; | ||
| 208 | bool printed = false; | ||
| 209 | int i = 0; | ||
| 210 | int ret = 0; | ||
| 211 | |||
| 212 | list_for_each_entry(chain, &self->val, list) { | ||
| 213 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
| 214 | continue; | ||
| 215 | |||
| 216 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
| 217 | continue; | ||
| 218 | |||
| 219 | if (!printed) { | ||
| 220 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 221 | ret += fprintf(fp, "|\n"); | ||
| 222 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 223 | ret += fprintf(fp, "---"); | ||
| 224 | |||
| 225 | left_margin += 3; | ||
| 226 | printed = true; | ||
| 227 | } else | ||
| 228 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 229 | |||
| 230 | if (chain->sym) | ||
| 231 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
| 232 | else | ||
| 233 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | ||
| 234 | } | ||
| 235 | |||
| 236 | ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); | ||
| 237 | |||
| 238 | return ret; | ||
| 239 | } | ||
| 240 | |||
| 241 | static size_t | ||
| 242 | callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | ||
| 243 | u64 total_samples) | ||
| 244 | { | ||
| 245 | struct callchain_list *chain; | ||
| 246 | size_t ret = 0; | ||
| 247 | |||
| 248 | if (!self) | ||
| 249 | return 0; | ||
| 250 | |||
| 251 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); | ||
| 252 | |||
| 253 | |||
| 254 | list_for_each_entry(chain, &self->val, list) { | ||
| 255 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
| 256 | continue; | ||
| 257 | if (chain->sym) | ||
| 258 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
| 259 | else | ||
| 260 | ret += fprintf(fp, " %p\n", | ||
| 261 | (void *)(long)chain->ip); | ||
| 262 | } | ||
| 263 | |||
| 264 | return ret; | ||
| 265 | } | ||
| 266 | |||
| 267 | static size_t | ||
| 268 | hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | ||
| 269 | u64 total_samples, int left_margin) | ||
| 270 | { | ||
| 271 | struct rb_node *rb_node; | ||
| 272 | struct callchain_node *chain; | ||
| 273 | size_t ret = 0; | ||
| 274 | |||
| 275 | rb_node = rb_first(&self->sorted_chain); | ||
| 276 | while (rb_node) { | ||
| 277 | double percent; | ||
| 278 | |||
| 279 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
| 280 | percent = chain->hit * 100.0 / total_samples; | ||
| 281 | switch (callchain_param.mode) { | ||
| 282 | case CHAIN_FLAT: | ||
| 283 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | ||
| 284 | percent); | ||
| 285 | ret += callchain__fprintf_flat(fp, chain, total_samples); | ||
| 286 | break; | ||
| 287 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
| 288 | case CHAIN_GRAPH_REL: | ||
| 289 | ret += callchain__fprintf_graph(fp, chain, total_samples, | ||
| 290 | left_margin); | ||
| 291 | case CHAIN_NONE: | ||
| 292 | default: | ||
| 293 | break; | ||
| 294 | } | ||
| 295 | ret += fprintf(fp, "\n"); | ||
| 296 | rb_node = rb_next(rb_node); | ||
| 297 | } | ||
| 298 | |||
| 299 | return ret; | ||
| 300 | } | ||
| 301 | |||
| 302 | static size_t hist_entry__fprintf(FILE *fp, struct hist_entry *self, | ||
| 303 | struct perf_session *session) | ||
| 304 | { | ||
| 305 | struct sort_entry *se; | ||
| 306 | size_t ret; | ||
| 307 | |||
| 308 | if (symbol_conf.exclude_other && !self->parent) | ||
| 309 | return 0; | ||
| 310 | |||
| 311 | if (session->events_stats.total) | ||
| 312 | ret = percent_color_fprintf(fp, | ||
| 313 | symbol_conf.field_sep ? "%.2f" : " %6.2f%%", | ||
| 314 | (self->count * 100.0) / session->events_stats.total); | ||
| 315 | else | ||
| 316 | ret = fprintf(fp, symbol_conf.field_sep ? "%lld" : "%12lld ", self->count); | ||
| 317 | |||
| 318 | if (symbol_conf.show_nr_samples) { | ||
| 319 | if (symbol_conf.field_sep) | ||
| 320 | fprintf(fp, "%c%lld", *symbol_conf.field_sep, self->count); | ||
| 321 | else | ||
| 322 | fprintf(fp, "%11lld", self->count); | ||
| 323 | } | ||
| 324 | |||
| 325 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
| 326 | if (se->elide) | ||
| 327 | continue; | ||
| 328 | |||
| 329 | fprintf(fp, "%s", symbol_conf.field_sep ?: " "); | ||
| 330 | ret += se->print(fp, self, se->width ? *se->width : 0); | ||
| 331 | } | ||
| 332 | |||
| 333 | ret += fprintf(fp, "\n"); | ||
| 334 | |||
| 335 | if (symbol_conf.use_callchain) { | ||
| 336 | int left_margin = 0; | ||
| 337 | |||
| 338 | if (sort__first_dimension == SORT_COMM) { | ||
| 339 | se = list_first_entry(&hist_entry__sort_list, typeof(*se), | ||
| 340 | list); | ||
| 341 | left_margin = se->width ? *se->width : 0; | ||
| 342 | left_margin -= thread__comm_len(self->thread); | ||
| 343 | } | ||
| 344 | |||
| 345 | hist_entry_callchain__fprintf(fp, self, session->events_stats.total, | ||
| 346 | left_margin); | ||
| 347 | } | ||
| 348 | |||
| 349 | return ret; | ||
| 350 | } | ||
| 351 | |||
| 352 | /* | ||
| 353 | * collect histogram counts | ||
| 354 | */ | ||
| 355 | |||
| 356 | static int perf_session__add_hist_entry(struct perf_session *self, | 46 | static int perf_session__add_hist_entry(struct perf_session *self, |
| 357 | struct addr_location *al, | 47 | struct addr_location *al, |
| 358 | struct ip_callchain *chain, u64 count) | 48 | struct ip_callchain *chain, u64 count) |
| @@ -381,93 +71,6 @@ static int perf_session__add_hist_entry(struct perf_session *self, | |||
| 381 | return 0; | 71 | return 0; |
| 382 | } | 72 | } |
| 383 | 73 | ||
| 384 | static size_t perf_session__fprintf_hists(struct perf_session *self, FILE *fp) | ||
| 385 | { | ||
| 386 | struct hist_entry *pos; | ||
| 387 | struct sort_entry *se; | ||
| 388 | struct rb_node *nd; | ||
| 389 | size_t ret = 0; | ||
| 390 | unsigned int width; | ||
| 391 | char *col_width = symbol_conf.col_width_list_str; | ||
| 392 | |||
| 393 | init_rem_hits(); | ||
| 394 | |||
| 395 | fprintf(fp, "# Samples: %ld\n", self->events_stats.total); | ||
| 396 | fprintf(fp, "#\n"); | ||
| 397 | |||
| 398 | fprintf(fp, "# Overhead"); | ||
| 399 | if (symbol_conf.show_nr_samples) { | ||
| 400 | if (symbol_conf.field_sep) | ||
| 401 | fprintf(fp, "%cSamples", *symbol_conf.field_sep); | ||
| 402 | else | ||
| 403 | fputs(" Samples ", fp); | ||
| 404 | } | ||
| 405 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
| 406 | if (se->elide) | ||
| 407 | continue; | ||
| 408 | if (symbol_conf.field_sep) { | ||
| 409 | fprintf(fp, "%c%s", *symbol_conf.field_sep, se->header); | ||
| 410 | continue; | ||
| 411 | } | ||
| 412 | width = strlen(se->header); | ||
| 413 | if (se->width) { | ||
| 414 | if (symbol_conf.col_width_list_str) { | ||
| 415 | if (col_width) { | ||
| 416 | *se->width = atoi(col_width); | ||
| 417 | col_width = strchr(col_width, ','); | ||
| 418 | if (col_width) | ||
| 419 | ++col_width; | ||
| 420 | } | ||
| 421 | } | ||
| 422 | width = *se->width = max(*se->width, width); | ||
| 423 | } | ||
| 424 | fprintf(fp, " %*s", width, se->header); | ||
| 425 | } | ||
| 426 | fprintf(fp, "\n"); | ||
| 427 | |||
| 428 | if (symbol_conf.field_sep) | ||
| 429 | goto print_entries; | ||
| 430 | |||
| 431 | fprintf(fp, "# ........"); | ||
| 432 | if (symbol_conf.show_nr_samples) | ||
| 433 | fprintf(fp, " .........."); | ||
| 434 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
| 435 | unsigned int i; | ||
| 436 | |||
| 437 | if (se->elide) | ||
| 438 | continue; | ||
| 439 | |||
| 440 | fprintf(fp, " "); | ||
| 441 | if (se->width) | ||
| 442 | width = *se->width; | ||
| 443 | else | ||
| 444 | width = strlen(se->header); | ||
| 445 | for (i = 0; i < width; i++) | ||
| 446 | fprintf(fp, "."); | ||
| 447 | } | ||
| 448 | fprintf(fp, "\n"); | ||
| 449 | |||
| 450 | fprintf(fp, "#\n"); | ||
| 451 | |||
| 452 | print_entries: | ||
| 453 | for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { | ||
| 454 | pos = rb_entry(nd, struct hist_entry, rb_node); | ||
| 455 | ret += hist_entry__fprintf(fp, pos, self); | ||
| 456 | } | ||
| 457 | |||
| 458 | if (sort_order == default_sort_order && | ||
| 459 | parent_pattern == default_parent_pattern) { | ||
| 460 | fprintf(fp, "#\n"); | ||
| 461 | fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n"); | ||
| 462 | fprintf(fp, "#\n"); | ||
| 463 | } | ||
| 464 | fprintf(fp, "\n"); | ||
| 465 | |||
| 466 | free(rem_sq_bracket); | ||
| 467 | |||
| 468 | return ret; | ||
| 469 | } | ||
| 470 | |||
| 471 | static int validate_chain(struct ip_callchain *chain, event_t *event) | 74 | static int validate_chain(struct ip_callchain *chain, event_t *event) |
| 472 | { | 75 | { |
| 473 | unsigned int chain_size; | 76 | unsigned int chain_size; |
