format_output.cpp revision cc2ee177dbb3befca43e36cfc56778b006c3d050
1/** 2 * @file format_output.cpp 3 * outputting format for symbol lists 4 * 5 * @remark Copyright 2002 OProfile authors 6 * @remark Read the file COPYING 7 * 8 * @author Philippe Elie 9 * @author John Levon 10 */ 11 12/* older glibc has C99 INFINITY in _GNU_SOURCE */ 13#ifndef _GNU_SOURCE 14#define _GNU_SOURCE 15#endif 16 17#include <sstream> 18#include <iomanip> 19#include <iostream> 20#include <cmath> 21 22#include "string_manip.h" 23 24#include "format_output.h" 25#include "profile_container.h" 26#include "callgraph_container.h" 27#include "diff_container.h" 28 29using namespace std; 30 31 32namespace { 33 34string const & get_image_name(image_name_id id, bool lf) 35{ 36 return lf ? image_names.name(id) : image_names.basename(id); 37} 38 39string const get_linenr_info(file_location const floc, bool lf) 40{ 41 ostringstream out; 42 43 string const & filename = lf 44 ? debug_names.name(floc.filename) 45 : debug_names.basename(floc.filename); 46 47 if (!filename.empty()) { 48 out << filename << ":" << floc.linenr; 49 } else { 50 out << "(no location information)"; 51 } 52 53 return out.str(); 54} 55 56string get_vma(bfd_vma vma, bool vma_64) 57{ 58 ostringstream out; 59 int width = vma_64 ? 16 : 8; 60 61 out << hex << setw(width) << setfill('0') << vma; 62 63 return out.str(); 64} 65 66string get_percent(size_t dividend, size_t divisor) 67{ 68 double ratio = op_ratio(dividend, divisor); 69 70 return ::format_percent(ratio * 100, percent_int_width, 71 percent_fract_width); 72} 73 74} // anonymous namespace 75 76 77namespace format_output { 78 79 80formatter::formatter() 81 : 82 nr_classes(1), 83 flags(ff_none), 84 vma_64(false), 85 long_filenames(false), 86 need_header(true) 87{ 88 format_map[ff_vma] = field_description(9, "vma", &formatter::format_vma); 89 format_map[ff_nr_samples] = field_description(9, "samples", &formatter::format_nr_samples); 90 format_map[ff_nr_samples_cumulated] = field_description(14, "cum. samples", &formatter::format_nr_cumulated_samples); 91 format_map[ff_percent] = field_description(9, "%", &formatter::format_percent); 92 format_map[ff_percent_cumulated] = field_description(11, "cum. %", &formatter::format_cumulated_percent); 93 format_map[ff_linenr_info] = field_description(28, "linenr info", &formatter::format_linenr_info); 94 format_map[ff_image_name] = field_description(25, "image name", &formatter::format_image_name); 95 format_map[ff_app_name] = field_description(25, "app name", &formatter::format_app_name); 96 format_map[ff_symb_name] = field_description(30, "symbol name", &formatter::format_symb_name); 97 format_map[ff_percent_details] = field_description(9, "%", &formatter::format_percent_details); 98 format_map[ff_percent_cumulated_details] = field_description(10, "cum. %", &formatter::format_cumulated_percent_details); 99 format_map[ff_diff] = field_description(10, "diff %", &formatter::format_diff); 100} 101 102 103formatter::~formatter() 104{ 105} 106 107 108void formatter::set_nr_classes(size_t nr) 109{ 110 nr_classes = nr; 111} 112 113 114void formatter::add_format(format_flags flag) 115{ 116 flags = static_cast<format_flags>(flags | flag); 117} 118 119 120void formatter::show_header(bool on_off) 121{ 122 need_header = on_off; 123} 124 125 126void formatter::vma_format_64bit(bool on_off) 127{ 128 vma_64 = on_off; 129} 130 131 132void formatter::show_long_filenames(bool on_off) 133{ 134 long_filenames = on_off; 135} 136 137 138void formatter::show_global_percent(bool on_off) 139{ 140 global_percent = on_off; 141} 142 143 144void formatter::output_header(ostream & out) 145{ 146 if (!need_header) 147 return; 148 149 size_t padding = 0; 150 151 // first output the vma field 152 if (flags & ff_vma) 153 padding = output_header_field(out, ff_vma, padding); 154 155 // the field repeated for each profile class 156 for (size_t pclass = 0 ; pclass < nr_classes; ++pclass) { 157 if (flags & ff_nr_samples) 158 padding = output_header_field(out, 159 ff_nr_samples, padding); 160 161 if (flags & ff_nr_samples_cumulated) 162 padding = output_header_field(out, 163 ff_nr_samples_cumulated, padding); 164 165 if (flags & ff_percent) 166 padding = output_header_field(out, 167 ff_percent, padding); 168 169 if (flags & ff_percent_cumulated) 170 padding = output_header_field(out, 171 ff_percent_cumulated, padding); 172 173 if (flags & ff_diff) 174 padding = output_header_field(out, 175 ff_diff, padding); 176 177 if (flags & ff_percent_details) 178 padding = output_header_field(out, 179 ff_percent_details, padding); 180 181 if (flags & ff_percent_cumulated_details) 182 padding = output_header_field(out, 183 ff_percent_cumulated_details, padding); 184 } 185 186 // now the remaining field 187 if (flags & ff_linenr_info) 188 padding = output_header_field(out, ff_linenr_info, padding); 189 190 if (flags & ff_image_name) 191 padding = output_header_field(out, ff_image_name, padding); 192 193 if (flags & ff_app_name) 194 padding = output_header_field(out, ff_app_name, padding); 195 196 if (flags & ff_symb_name) 197 padding = output_header_field(out, ff_symb_name, padding); 198 199 out << "\n"; 200} 201 202 203/// describe each possible field of colummned output. 204// FIXME: use % of the screen width here. sum of % equal to 100, then calculate 205// ratio between 100 and the selected % to grow non fixed field use also 206// lib[n?]curses to get the console width (look info source) (so on add a fixed 207// field flags) 208size_t formatter:: 209output_field(ostream & out, field_datum const & datum, 210 format_flags fl, size_t padding, bool hide_immutable) 211{ 212 if (!hide_immutable) { 213 out << string(padding, ' '); 214 215 field_description const & field(format_map[fl]); 216 string str = (this->*field.formatter)(datum); 217 out << str; 218 219 // at least one separator char 220 padding = 1; 221 if (str.length() < field.width) 222 padding = field.width - str.length(); 223 } else { 224 field_description const & field(format_map[fl]); 225 padding += field.width; 226 } 227 228 return padding; 229} 230 231 232size_t formatter:: 233output_header_field(ostream & out, format_flags fl, size_t padding) 234{ 235 out << string(padding, ' '); 236 237 field_description const & field(format_map[fl]); 238 out << field.header_name; 239 240 // at least one separator char 241 padding = 1; 242 if (field.header_name.length() < field.width) 243 padding = field.width - field.header_name.length(); 244 245 return padding; 246} 247 248 249string formatter::format_vma(field_datum const & f) 250{ 251 return get_vma(f.sample.vma, vma_64); 252} 253 254 255string formatter::format_symb_name(field_datum const & f) 256{ 257 return symbol_names.demangle(f.symbol.name); 258} 259 260 261string formatter::format_image_name(field_datum const & f) 262{ 263 return get_image_name(f.symbol.image_name, long_filenames); 264} 265 266 267string formatter::format_app_name(field_datum const & f) 268{ 269 return get_image_name(f.symbol.app_name, long_filenames); 270} 271 272 273string formatter::format_linenr_info(field_datum const & f) 274{ 275 return get_linenr_info(f.sample.file_loc, long_filenames); 276} 277 278 279string formatter::format_nr_samples(field_datum const & f) 280{ 281 ostringstream out; 282 out << f.sample.counts[f.pclass]; 283 return out.str(); 284} 285 286 287string formatter::format_nr_cumulated_samples(field_datum const & f) 288{ 289 if (f.diff == -INFINITY) 290 return "---"; 291 ostringstream out; 292 f.counts.cumulated_samples[f.pclass] += f.sample.counts[f.pclass]; 293 out << f.counts.cumulated_samples[f.pclass]; 294 return out.str(); 295} 296 297 298string formatter::format_percent(field_datum const & f) 299{ 300 if (f.diff == -INFINITY) 301 return "---"; 302 return get_percent(f.sample.counts[f.pclass], f.counts.total[f.pclass]); 303} 304 305 306string formatter::format_cumulated_percent(field_datum const & f) 307{ 308 if (f.diff == -INFINITY) 309 return "---"; 310 f.counts.cumulated_percent[f.pclass] += f.sample.counts[f.pclass]; 311 312 return get_percent(f.counts.cumulated_percent[f.pclass], 313 f.counts.total[f.pclass]); 314} 315 316 317string formatter::format_percent_details(field_datum const & f) 318{ 319 return get_percent(f.sample.counts[f.pclass], 320 f.counts.total[f.pclass]); 321} 322 323 324string formatter::format_cumulated_percent_details(field_datum const & f) 325{ 326 f.counts.cumulated_percent_details[f.pclass] += f.sample.counts[f.pclass]; 327 328 return get_percent(f.counts.cumulated_percent_details[f.pclass], 329 f.counts.total[f.pclass]); 330} 331 332 333string formatter::format_diff(field_datum const & f) 334{ 335 if (f.diff == INFINITY) { 336 ostringstream out; 337 out << "+++"; 338 return out.str(); 339 } else if (f.diff == -INFINITY) { 340 ostringstream out; 341 out << "---"; 342 return out.str(); 343 } 344 345 return ::format_percent(f.diff, percent_int_width, 346 percent_fract_width, true); 347} 348 349 350void formatter:: 351do_output(ostream & out, symbol_entry const & symb, sample_entry const & sample, 352 counts_t & c, diff_array_t const & diffs, bool hide_immutable) 353{ 354 size_t padding = 0; 355 356 // first output the vma field 357 field_datum datum(symb, sample, 0, c); 358 if (flags & ff_vma) 359 padding = output_field(out, datum, ff_vma, padding, false); 360 361 // repeated fields for each profile class 362 for (size_t pclass = 0 ; pclass < nr_classes; ++pclass) { 363 field_datum datum(symb, sample, pclass, c, diffs[pclass]); 364 365 if (flags & ff_nr_samples) 366 padding = output_field(out, datum, 367 ff_nr_samples, padding, false); 368 369 if (flags & ff_nr_samples_cumulated) 370 padding = output_field(out, datum, 371 ff_nr_samples_cumulated, padding, false); 372 373 if (flags & ff_percent) 374 padding = output_field(out, datum, 375 ff_percent, padding, false); 376 377 if (flags & ff_percent_cumulated) 378 padding = output_field(out, datum, 379 ff_percent_cumulated, padding, false); 380 381 if (flags & ff_diff) 382 padding = output_field(out, datum, 383 ff_diff, padding, false); 384 385 if (flags & ff_percent_details) 386 padding = output_field(out, datum, 387 ff_percent_details, padding, false); 388 389 if (flags & ff_percent_cumulated_details) 390 padding = output_field(out, datum, 391 ff_percent_cumulated_details, padding, false); 392 } 393 394 // now the remaining field 395 if (flags & ff_linenr_info) 396 padding = output_field(out, datum, ff_linenr_info, 397 padding, false); 398 399 if (flags & ff_image_name) 400 padding = output_field(out, datum, ff_image_name, 401 padding, hide_immutable); 402 403 if (flags & ff_app_name) 404 padding = output_field(out, datum, ff_app_name, 405 padding, hide_immutable); 406 407 if (flags & ff_symb_name) 408 padding = output_field(out, datum, ff_symb_name, 409 padding, hide_immutable); 410 411 out << "\n"; 412} 413 414 415opreport_formatter::opreport_formatter(profile_container const & p) 416 : 417 profile(p), 418 need_details(false) 419{ 420 counts.total = profile.samples_count(); 421} 422 423 424void opreport_formatter::show_details(bool on_off) 425{ 426 need_details = on_off; 427} 428 429 430void opreport_formatter::output(ostream & out, symbol_entry const * symb) 431{ 432 do_output(out, *symb, symb->sample, counts); 433 434 if (need_details) 435 output_details(out, symb); 436} 437 438 439void opreport_formatter:: 440output(ostream & out, symbol_collection const & syms) 441{ 442 output_header(out); 443 444 symbol_collection::const_iterator it = syms.begin(); 445 symbol_collection::const_iterator end = syms.end(); 446 for (; it != end; ++it) 447 output(out, *it); 448} 449 450 451void opreport_formatter:: 452output_details(ostream & out, symbol_entry const * symb) 453{ 454 counts_t c = counts; 455 456 if (!global_percent) 457 c.total = symb->sample.counts; 458 459 // cumulated percent are relative to current symbol. 460 c.cumulated_samples = count_array_t(); 461 c.cumulated_percent = count_array_t(); 462 463 sample_container::samples_iterator it = profile.begin(symb); 464 sample_container::samples_iterator end = profile.end(symb); 465 for (; it != end; ++it) { 466 out << " "; 467 do_output(out, *symb, it->second, c, diff_array_t(), true); 468 } 469} 470 471 472cg_formatter::cg_formatter(callgraph_container const & profile) 473{ 474 counts.total = profile.samples_count(); 475} 476 477 478void cg_formatter::output(ostream & out, cg_collection const & syms) 479{ 480 // amount of spacing prefixing child and parent lines 481 string const child_parent_prefix(" "); 482 483 output_header(out); 484 485 out << string(79, '-') << endl; 486 487 cg_collection::const_iterator it; 488 cg_collection::const_iterator end = syms.end(); 489 490 for (it = syms.begin(); it < end; ++it) { 491 cg_symbol const & sym = *it; 492 493 cg_symbol::children::const_iterator cit; 494 cg_symbol::children::const_iterator cend = sym.callers.end(); 495 496 counts_t c; 497 if (global_percent) 498 c.total = counts.total; 499 else 500 c.total = sym.total_caller_count; 501 502 for (cit = sym.callers.begin(); cit != cend; ++cit) { 503 out << child_parent_prefix; 504 do_output(out, *cit, cit->sample, c); 505 } 506 507 do_output(out, sym, sym.sample, counts); 508 509 c = counts_t(); 510 if (global_percent) 511 c.total = counts.total; 512 else 513 c.total = sym.total_callee_count; 514 515 cend = sym.callees.end(); 516 517 for (cit = sym.callees.begin(); cit != cend; ++cit) { 518 out << child_parent_prefix; 519 do_output(out, *cit, cit->sample, c); 520 } 521 522 out << string(79, '-') << endl; 523 } 524} 525 526 527diff_formatter::diff_formatter(diff_container const & profile) 528{ 529 counts.total = profile.samples_count(); 530} 531 532 533void diff_formatter::output(ostream & out, diff_collection const & syms) 534{ 535 output_header(out); 536 537 diff_collection::const_iterator it = syms.begin(); 538 diff_collection::const_iterator end = syms.end(); 539 for (; it != end; ++it) 540 do_output(out, *it, it->sample, counts, it->diffs); 541} 542 543 544} // namespace format_output 545