1/* 2 * Copyright (c) 2019 Arm Limited 3 * All rights reserved. 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Copyright (c) 2004-2005 The Regents of The University of Michigan 15 * All rights reserved. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions are 19 * met: redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer; 21 * redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution; 24 * neither the name of the copyright holders nor the names of its 25 * contributors may be used to endorse or promote products derived from 26 * this software without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 * 40 * Authors: Nathan Binkert 41 */ 42 43#if defined(__APPLE__) 44#define _GLIBCPP_USE_C99 1 45#endif 46 47#if defined(__sun) 48#include <cmath> 49 50#endif 51 52#include <cassert> 53 54#ifdef __SUNPRO_CC 55#include <cmath> 56 57#endif 58#include "base/stats/text.hh" 59 60#include <cmath> 61#include <fstream> 62#include <iostream> 63#include <sstream> 64#include <string> 65 66#include "base/cast.hh" 67#include "base/logging.hh" 68#include "base/stats/info.hh" 69#include "base/str.hh" 70 71using namespace std; 72 73#ifndef NAN 74float __nan(); 75/** Define Not a number. */ 76#define NAN (__nan()) 77/** Need to define __nan() */ 78#define __M5_NAN 79#endif 80 81#ifdef __M5_NAN 82float 83__nan() 84{ 85 union { 86 uint32_t ui; 87 float f; 88 } nan; 89 90 nan.ui = 0x7fc00000; 91 return nan.f; 92} 93#endif 94 95namespace Stats { 96 97std::list<Info *> &statsList(); 98 99Text::Text() 100 : mystream(false), stream(NULL), descriptions(false) 101{ 102} 103 104Text::Text(std::ostream &stream) 105 : mystream(false), stream(NULL), descriptions(false) 106{ 107 open(stream); 108} 109 110Text::Text(const std::string &file) 111 : mystream(false), stream(NULL), descriptions(false) 112{ 113 open(file); 114} 115 116 117Text::~Text() 118{ 119 if (mystream) { 120 assert(stream); 121 delete stream; 122 } 123} 124 125void 126Text::open(std::ostream &_stream) 127{ 128 if (stream) 129 panic("stream already set!"); 130 131 mystream = false; 132 stream = &_stream; 133 if (!valid()) 134 fatal("Unable to open output stream for writing\n"); 135} 136 137void 138Text::open(const std::string &file) 139{ 140 if (stream) 141 panic("stream already set!"); 142 143 mystream = true; 144 stream = new ofstream(file.c_str(), ios::trunc); 145 if (!valid()) 146 fatal("Unable to open statistics file for writing\n"); 147} 148 149bool 150Text::valid() const 151{ 152 return stream != NULL && stream->good(); 153} 154 155void 156Text::begin() 157{ 158 ccprintf(*stream, "\n---------- Begin Simulation Statistics ----------\n"); 159} 160 161void 162Text::end() 163{ 164 ccprintf(*stream, "\n---------- End Simulation Statistics ----------\n"); 165 stream->flush(); 166} 167 168std::string 169Text::statName(const std::string &name) const 170{ 171 if (path.empty()) 172 return name; 173 else 174 return csprintf("%s.%s", path.top(), name); 175} 176 177void 178Text::beginGroup(const char *name) 179{ 180 if (path.empty()) { 181 path.push(name); 182 } else { 183 path.push(csprintf("%s.%s", path.top(), name)); 184 } 185} 186 187void 188Text::endGroup() 189{ 190 assert(!path.empty()); 191 path.pop(); 192} 193 194bool 195Text::noOutput(const Info &info) 196{ 197 if (!info.flags.isSet(display)) 198 return true; 199 200 if (info.prereq && info.prereq->zero()) 201 return true; 202 203 return false; 204} 205 206string 207ValueToString(Result value, int precision) 208{ 209 stringstream val; 210 211 if (!std::isnan(value)) { 212 if (precision != -1) 213 val.precision(precision); 214 else if (value == rint(value)) 215 val.precision(0); 216 217 val.unsetf(ios::showpoint); 218 val.setf(ios::fixed); 219 val << value; 220 } else { 221 val << "nan"; 222 } 223 224 return val.str(); 225} 226 227struct ScalarPrint 228{ 229 Result value; 230 string name; 231 string desc; 232 Flags flags; 233 bool descriptions; 234 int precision; 235 Result pdf; 236 Result cdf; 237 238 void update(Result val, Result total); 239 void operator()(ostream &stream, bool oneLine = false) const; 240}; 241 242void 243ScalarPrint::update(Result val, Result total) 244{ 245 value = val; 246 if (total) { 247 pdf = val / total; 248 cdf += pdf; 249 } 250} 251 252void 253ScalarPrint::operator()(ostream &stream, bool oneLine) const 254{ 255 if ((flags.isSet(nozero) && (!oneLine) && value == 0.0) || 256 (flags.isSet(nonan) && std::isnan(value))) 257 return; 258 259 stringstream pdfstr, cdfstr; 260 261 if (!std::isnan(pdf)) 262 ccprintf(pdfstr, "%.2f%%", pdf * 100.0); 263 264 if (!std::isnan(cdf)) 265 ccprintf(cdfstr, "%.2f%%", cdf * 100.0); 266 267 if (oneLine) { 268 ccprintf(stream, " |%12s %10s %10s", 269 ValueToString(value, precision), pdfstr.str(), cdfstr.str()); 270 } else { 271 ccprintf(stream, "%-40s %12s %10s %10s", name, 272 ValueToString(value, precision), pdfstr.str(), cdfstr.str()); 273 274 if (descriptions) { 275 if (!desc.empty()) 276 ccprintf(stream, " # %s", desc); 277 } 278 stream << endl; 279 } 280} 281 282struct VectorPrint 283{ 284 string name; 285 string separatorString; 286 string desc; 287 vector<string> subnames; 288 vector<string> subdescs; 289 Flags flags; 290 bool descriptions; 291 int precision; 292 VResult vec; 293 Result total; 294 bool forceSubnames; 295 296 void operator()(ostream &stream) const; 297}; 298 299void 300VectorPrint::operator()(std::ostream &stream) const 301{ 302 size_type _size = vec.size(); 303 Result _total = 0.0; 304 305 if (flags.isSet(pdf | cdf)) { 306 for (off_type i = 0; i < _size; ++i) { 307 _total += vec[i]; 308 } 309 } 310 311 string base = name + separatorString; 312 313 ScalarPrint print; 314 print.name = name; 315 print.desc = desc; 316 print.precision = precision; 317 print.descriptions = descriptions; 318 print.flags = flags; 319 print.pdf = _total ? 0.0 : NAN; 320 print.cdf = _total ? 0.0 : NAN; 321 322 bool havesub = !subnames.empty(); 323 324 if (_size == 1) { 325 // If forceSubnames is set, get the first subname (or index in 326 // the case where there are no subnames) and append it to the 327 // base name. 328 if (forceSubnames) 329 print.name = base + (havesub ? subnames[0] : std::to_string(0)); 330 print.value = vec[0]; 331 print(stream); 332 return; 333 } 334 335 if ((!flags.isSet(nozero)) || (total != 0)) { 336 if (flags.isSet(oneline)) { 337 ccprintf(stream, "%-40s", name); 338 print.flags = print.flags & (~nozero); 339 } 340 341 for (off_type i = 0; i < _size; ++i) { 342 if (havesub && (i >= subnames.size() || subnames[i].empty())) 343 continue; 344 345 print.name = base + (havesub ? subnames[i] : std::to_string(i)); 346 print.desc = subdescs.empty() ? desc : subdescs[i]; 347 348 print.update(vec[i], _total); 349 print(stream, flags.isSet(oneline)); 350 } 351 352 if (flags.isSet(oneline)) { 353 if (descriptions) { 354 if (!desc.empty()) 355 ccprintf(stream, " # %s", desc); 356 } 357 stream << endl; 358 } 359 } 360 361 if (flags.isSet(::Stats::total)) { 362 print.pdf = NAN; 363 print.cdf = NAN; 364 print.name = base + "total"; 365 print.desc = desc; 366 print.value = total; 367 print(stream); 368 } 369} 370 371struct DistPrint 372{ 373 string name; 374 string separatorString; 375 string desc; 376 Flags flags; 377 bool descriptions; 378 int precision; 379 380 const DistData &data; 381 382 DistPrint(const Text *text, const DistInfo &info); 383 DistPrint(const Text *text, const VectorDistInfo &info, int i); 384 void init(const Text *text, const Info &info); 385 void operator()(ostream &stream) const; 386}; 387 388DistPrint::DistPrint(const Text *text, const DistInfo &info) 389 : data(info.data) 390{ 391 init(text, info); 392} 393 394DistPrint::DistPrint(const Text *text, const VectorDistInfo &info, int i) 395 : data(info.data[i]) 396{ 397 init(text, info); 398 399 name = info.name + "_" + 400 (info.subnames[i].empty() ? (std::to_string(i)) : info.subnames[i]); 401 402 if (!info.subdescs[i].empty()) 403 desc = info.subdescs[i]; 404} 405 406void 407DistPrint::init(const Text *text, const Info &info) 408{ 409 name = text->statName(info.name); 410 separatorString = info.separatorString; 411 desc = info.desc; 412 flags = info.flags; 413 precision = info.precision; 414 descriptions = text->descriptions; 415} 416 417void 418DistPrint::operator()(ostream &stream) const 419{ 420 if (flags.isSet(nozero) && data.samples == 0) return; 421 string base = name + separatorString; 422 423 ScalarPrint print; 424 print.precision = precision; 425 print.flags = flags; 426 print.descriptions = descriptions; 427 print.desc = desc; 428 print.pdf = NAN; 429 print.cdf = NAN; 430 431 if (flags.isSet(oneline)) { 432 print.name = base + "bucket_size"; 433 print.value = data.bucket_size; 434 print(stream); 435 436 print.name = base + "min_bucket"; 437 print.value = data.min; 438 print(stream); 439 440 print.name = base + "max_bucket"; 441 print.value = data.max; 442 print(stream); 443 } 444 445 print.name = base + "samples"; 446 print.value = data.samples; 447 print(stream); 448 449 print.name = base + "mean"; 450 print.value = data.samples ? data.sum / data.samples : NAN; 451 print(stream); 452 453 if (data.type == Hist) { 454 print.name = base + "gmean"; 455 print.value = data.samples ? exp(data.logs / data.samples) : NAN; 456 print(stream); 457 } 458 459 Result stdev = NAN; 460 if (data.samples) 461 stdev = sqrt((data.samples * data.squares - data.sum * data.sum) / 462 (data.samples * (data.samples - 1.0))); 463 print.name = base + "stdev"; 464 print.value = stdev; 465 print(stream); 466 467 if (data.type == Deviation) 468 return; 469 470 size_t size = data.cvec.size(); 471 472 Result total = 0.0; 473 if (data.type == Dist && data.underflow != NAN) 474 total += data.underflow; 475 for (off_type i = 0; i < size; ++i) 476 total += data.cvec[i]; 477 if (data.type == Dist && data.overflow != NAN) 478 total += data.overflow; 479 480 if (total) { 481 print.pdf = 0.0; 482 print.cdf = 0.0; 483 } 484 485 if (data.type == Dist && data.underflow != NAN) { 486 print.name = base + "underflows"; 487 print.update(data.underflow, total); 488 print(stream); 489 } 490 491 if (flags.isSet(oneline)) { 492 ccprintf(stream, "%-40s", name); 493 } 494 495 for (off_type i = 0; i < size; ++i) { 496 stringstream namestr; 497 namestr << base; 498 499 Counter low = i * data.bucket_size + data.min; 500 Counter high = ::min(low + data.bucket_size - 1.0, data.max); 501 namestr << low; 502 if (low < high) 503 namestr << "-" << high; 504 505 print.name = namestr.str(); 506 print.update(data.cvec[i], total); 507 print(stream, flags.isSet(oneline)); 508 } 509 510 if (flags.isSet(oneline)) { 511 if (descriptions) { 512 if (!desc.empty()) 513 ccprintf(stream, " # %s", desc); 514 } 515 stream << endl; 516 } 517 518 if (data.type == Dist && data.overflow != NAN) { 519 print.name = base + "overflows"; 520 print.update(data.overflow, total); 521 print(stream); 522 } 523 524 print.pdf = NAN; 525 print.cdf = NAN; 526 527 if (data.type == Dist && data.min_val != NAN) { 528 print.name = base + "min_value"; 529 print.value = data.min_val; 530 print(stream); 531 } 532 533 if (data.type == Dist && data.max_val != NAN) { 534 print.name = base + "max_value"; 535 print.value = data.max_val; 536 print(stream); 537 } 538 539 print.name = base + "total"; 540 print.value = total; 541 print(stream); 542} 543 544void 545Text::visit(const ScalarInfo &info) 546{ 547 if (noOutput(info)) 548 return; 549 550 ScalarPrint print; 551 print.value = info.result(); 552 print.name = statName(info.name); 553 print.desc = info.desc; 554 print.flags = info.flags; 555 print.descriptions = descriptions; 556 print.precision = info.precision; 557 print.pdf = NAN; 558 print.cdf = NAN; 559 560 print(*stream); 561} 562 563void 564Text::visit(const VectorInfo &info) 565{ 566 if (noOutput(info)) 567 return; 568 569 size_type size = info.size(); 570 VectorPrint print; 571 572 print.name = statName(info.name); 573 print.separatorString = info.separatorString; 574 print.desc = info.desc; 575 print.flags = info.flags; 576 print.descriptions = descriptions; 577 print.precision = info.precision; 578 print.vec = info.result(); 579 print.total = info.total(); 580 print.forceSubnames = false; 581 582 if (!info.subnames.empty()) { 583 for (off_type i = 0; i < size; ++i) { 584 if (!info.subnames[i].empty()) { 585 print.subnames = info.subnames; 586 print.subnames.resize(size); 587 for (off_type i = 0; i < size; ++i) { 588 if (!info.subnames[i].empty() && 589 !info.subdescs[i].empty()) { 590 print.subdescs = info.subdescs; 591 print.subdescs.resize(size); 592 break; 593 } 594 } 595 break; 596 } 597 } 598 } 599 600 print(*stream); 601} 602 603void 604Text::visit(const Vector2dInfo &info) 605{ 606 if (noOutput(info)) 607 return; 608 609 bool havesub = false; 610 VectorPrint print; 611 612 if (!info.y_subnames.empty()) { 613 for (off_type i = 0; i < info.y; ++i) { 614 if (!info.y_subnames[i].empty()) { 615 print.subnames = info.y_subnames; 616 break; 617 } 618 } 619 } 620 print.flags = info.flags; 621 print.separatorString = info.separatorString; 622 print.descriptions = descriptions; 623 print.precision = info.precision; 624 print.forceSubnames = true; 625 626 if (!info.subnames.empty()) { 627 for (off_type i = 0; i < info.x; ++i) 628 if (!info.subnames[i].empty()) 629 havesub = true; 630 } 631 632 VResult tot_vec(info.y); 633 for (off_type i = 0; i < info.x; ++i) { 634 if (havesub && (i >= info.subnames.size() || info.subnames[i].empty())) 635 continue; 636 637 off_type iy = i * info.y; 638 VResult yvec(info.y); 639 640 Result total = 0.0; 641 for (off_type j = 0; j < info.y; ++j) { 642 yvec[j] = info.cvec[iy + j]; 643 tot_vec[j] += yvec[j]; 644 total += yvec[j]; 645 } 646 647 print.name = statName( 648 info.name + "_" + 649 (havesub ? info.subnames[i] : std::to_string(i))); 650 print.desc = info.desc; 651 print.vec = yvec; 652 print.total = total; 653 print(*stream); 654 } 655 656 // Create a subname for printing the total 657 vector<string> total_subname; 658 total_subname.push_back("total"); 659 660 if (info.flags.isSet(::Stats::total) && (info.x > 1)) { 661 print.name = statName(info.name); 662 print.subnames = total_subname; 663 print.desc = info.desc; 664 print.vec = VResult(1, info.total()); 665 print.flags = print.flags & ~total; 666 print(*stream); 667 } 668} 669 670void 671Text::visit(const DistInfo &info) 672{ 673 if (noOutput(info)) 674 return; 675 676 DistPrint print(this, info); 677 print(*stream); 678} 679 680void 681Text::visit(const VectorDistInfo &info) 682{ 683 if (noOutput(info)) 684 return; 685 686 for (off_type i = 0; i < info.size(); ++i) { 687 DistPrint print(this, info, i); 688 print(*stream); 689 } 690} 691 692void 693Text::visit(const FormulaInfo &info) 694{ 695 visit((const VectorInfo &)info); 696} 697 698/* 699 This struct implements the output methods for the sparse 700 histogram stat 701*/ 702struct SparseHistPrint 703{ 704 string name; 705 string separatorString; 706 string desc; 707 Flags flags; 708 bool descriptions; 709 int precision; 710 711 const SparseHistData &data; 712 713 SparseHistPrint(const Text *text, const SparseHistInfo &info); 714 void init(const Text *text, const Info &info); 715 void operator()(ostream &stream) const; 716}; 717 718/* Call initialization function */ 719SparseHistPrint::SparseHistPrint(const Text *text, const SparseHistInfo &info) 720 : data(info.data) 721{ 722 init(text, info); 723} 724 725/* Initialization function */ 726void 727SparseHistPrint::init(const Text *text, const Info &info) 728{ 729 name = text->statName(info.name); 730 separatorString = info.separatorString; 731 desc = info.desc; 732 flags = info.flags; 733 precision = info.precision; 734 descriptions = text->descriptions; 735} 736 737/* Grab data from map and write to output stream */ 738void 739SparseHistPrint::operator()(ostream &stream) const 740{ 741 string base = name + separatorString; 742 743 ScalarPrint print; 744 print.precision = precision; 745 print.flags = flags; 746 print.descriptions = descriptions; 747 print.desc = desc; 748 print.pdf = NAN; 749 print.cdf = NAN; 750 751 print.name = base + "samples"; 752 print.value = data.samples; 753 print(stream); 754 755 MCounter::const_iterator it; 756 for (it = data.cmap.begin(); it != data.cmap.end(); it++) { 757 stringstream namestr; 758 namestr << base; 759 760 namestr <<(*it).first; 761 print.name = namestr.str(); 762 print.value = (*it).second; 763 print(stream); 764 } 765} 766 767void 768Text::visit(const SparseHistInfo &info) 769{ 770 if (noOutput(info)) 771 return; 772 773 SparseHistPrint print(this, info); 774 print(*stream); 775} 776 777Output * 778initText(const string &filename, bool desc) 779{ 780 static Text text; 781 static bool connected = false; 782 783 if (!connected) { 784 text.open(*simout.findOrCreate(filename)->stream()); 785 text.descriptions = desc; 786 connected = true; 787 } 788 789 return &text; 790} 791 792} // namespace Stats 793