/* * Copyright (c) 2019-2020 Arm Limited * All rights reserved. * * The license below extends only to copyright in the software and shall * not be construed as granting a license to any other intellectual * property including but not limited to intellectual property relating * to a hardware implementation of the functionality of the software * licensed hereunder. You may use the software subject to the license * terms below provided that you ensure that this notice is replicated * unmodified and in its entirety in all distributions of the software, * modified or unmodified, in source code or in binary form. * * Copyright (c) 2004-2005 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(__APPLE__) #define _GLIBCPP_USE_C99 1 #endif #include "base/stats/text.hh" #include #include #include #include #include #include #include #include "base/cast.hh" #include "base/logging.hh" #include "base/stats/info.hh" #include "base/str.hh" namespace gem5 { namespace { constexpr auto Nan = std::numeric_limits::quiet_NaN(); } // anonymous namespace GEM5_DEPRECATED_NAMESPACE(Stats, statistics); namespace statistics { std::list &statsList(); Text::Text() : mystream(false), stream(NULL), descriptions(false), spaces(false) { } Text::Text(std::ostream &stream) : Text() { open(stream); } Text::Text(const std::string &file) : Text() { open(file); } Text::~Text() { if (mystream) { assert(stream); delete stream; } } void Text::open(std::ostream &_stream) { if (stream) panic("stream already set!"); mystream = false; stream = &_stream; if (!valid()) fatal("Unable to open output stream for writing\n"); } void Text::open(const std::string &file) { if (stream) panic("stream already set!"); mystream = true; stream = new std::ofstream(file.c_str(), std::ios::trunc); if (!valid()) fatal("Unable to open statistics file for writing\n"); } bool Text::valid() const { return stream != NULL && stream->good(); } void Text::begin() { ccprintf(*stream, "\n---------- Begin Simulation Statistics ----------\n"); } void Text::end() { ccprintf(*stream, "\n---------- End Simulation Statistics ----------\n"); stream->flush(); } std::string Text::statName(const std::string &name) const { if (path.empty()) return name; else return csprintf("%s.%s", path.top(), name); } void Text::beginGroup(const char *name) { if (path.empty()) { path.push(name); } else { path.push(csprintf("%s.%s", path.top(), name)); } } void Text::endGroup() { assert(!path.empty()); path.pop(); } bool Text::noOutput(const Info &info) { if (!info.flags.isSet(display)) return true; if (info.prereq && info.prereq->zero()) return true; return false; } std::string ValueToString(Result value, int precision) { std::stringstream val; if (!std::isnan(value)) { if (precision != -1) val.precision(precision); else if (value == rint(value)) val.precision(0); val.unsetf(std::ios::showpoint); val.setf(std::ios::fixed); val << value; } else { val << "nan"; } return val.str(); } struct BasePrint { std::string name; Flags flags; int precision; bool descriptions; std::string desc; bool enableUnits; std::string unitStr; bool spaces; BasePrint(bool _spaces=false) : spaces(_spaces) {} void setup(std::string _name, Flags _flags, int _precision, bool enable_descriptions, std::string _desc, bool enable_units, std::string unit_str, bool enable_spaces) { name = _name; flags = _flags; precision = _precision; descriptions = enable_descriptions; desc = _desc; enableUnits = enable_units; unitStr = unit_str; spaces = enable_spaces; } void printUnits(std::ostream &stream) const { if (enableUnits && !unitStr.empty()) { ccprintf(stream, " (%s)", unitStr); } } }; struct ScalarPrint : public BasePrint { Result value; Result pdf; Result cdf; int nameSpaces; int valueSpaces; int pdfstrSpaces; int cdfstrSpaces; ScalarPrint(bool spaces) : BasePrint(spaces) { if (spaces) { nameSpaces = 40; valueSpaces = 12; pdfstrSpaces = 10; cdfstrSpaces = 10; } else { nameSpaces = 0; valueSpaces = 0; pdfstrSpaces = 0; cdfstrSpaces = 0; } } void update(Result val, Result total); void operator()(std::ostream &stream, bool oneLine = false) const; }; void ScalarPrint::update(Result val, Result total) { value = val; if (total) { pdf = val / total; cdf += pdf; } } void ScalarPrint::operator()(std::ostream &stream, bool oneLine) const { if ((flags.isSet(nozero) && (!oneLine) && value == 0.0) || (flags.isSet(nonan) && std::isnan(value))) return; std::stringstream pdfstr, cdfstr; if (!std::isnan(pdf)) ccprintf(pdfstr, "%.2f%%", pdf * 100.0); if (!std::isnan(cdf)) ccprintf(cdfstr, "%.2f%%", cdf * 100.0); if (oneLine) { ccprintf(stream, " |"); } else { ccprintf(stream, "%-*s ", nameSpaces, name); } ccprintf(stream, "%*s", valueSpaces, ValueToString(value, precision)); if (spaces || pdfstr.rdbuf()->in_avail()) ccprintf(stream, " %*s", pdfstrSpaces, pdfstr.str()); if (spaces || cdfstr.rdbuf()->in_avail()) ccprintf(stream, " %*s", cdfstrSpaces, cdfstr.str()); if (!oneLine) { if (descriptions) { if (!desc.empty()) ccprintf(stream, " # %s", desc); } printUnits(stream); stream << std::endl; } } struct VectorPrint : public BasePrint { std::string separatorString; std::vector subnames; std::vector subdescs; VResult vec; Result total; bool forceSubnames; int nameSpaces; VectorPrint() = delete; VectorPrint(bool spaces) : BasePrint(spaces) { if (spaces) { nameSpaces = 40; } else { nameSpaces = 0; } } void operator()(std::ostream &stream) const; }; void VectorPrint::operator()(std::ostream &stream) const { size_type _size = vec.size(); Result _total = 0.0; if (flags.isSet(pdf | cdf)) { for (off_type i = 0; i < _size; ++i) { _total += vec[i]; } } std::string base = name + separatorString; ScalarPrint print(spaces); print.setup(name, flags, precision, descriptions, desc, enableUnits, unitStr, spaces); print.pdf = _total ? 0.0 : Nan; print.cdf = _total ? 0.0 : Nan; bool havesub = !subnames.empty(); if (_size == 1) { // If forceSubnames is set, get the first subname (or index in // the case where there are no subnames) and append it to the // base name. if (forceSubnames) print.name = base + (havesub ? subnames[0] : std::to_string(0)); print.value = vec[0]; print(stream); return; } if ((!flags.isSet(nozero)) || (total != 0)) { if (flags.isSet(oneline)) { ccprintf(stream, "%-*s", nameSpaces, name); print.flags = print.flags & (~nozero); } for (off_type i = 0; i < _size; ++i) { if (havesub && (i >= subnames.size() || subnames[i].empty())) continue; print.name = base + (havesub ? subnames[i] : std::to_string(i)); print.desc = subdescs.empty() ? desc : subdescs[i]; print.unitStr = unitStr; print.update(vec[i], _total); print(stream, flags.isSet(oneline)); } if (flags.isSet(oneline)) { if (descriptions) { if (!desc.empty()) ccprintf(stream, " # %s", desc); } printUnits(stream); stream << std::endl; } } if (flags.isSet(statistics::total)) { print.pdf = Nan; print.cdf = Nan; print.name = base + "total"; print.desc = desc; print.unitStr = unitStr; print.value = total; print(stream); } } struct DistPrint : public BasePrint { std::string separatorString; int nameSpaces; const DistData &data; DistPrint(const Text *text, const DistInfo &info); DistPrint(const Text *text, const VectorDistInfo &info, int i); void init(const Text *text, const Info &info); void operator()(std::ostream &stream) const; }; DistPrint::DistPrint(const Text *text, const DistInfo &info) : data(info.data) { init(text, info); } DistPrint::DistPrint(const Text *text, const VectorDistInfo &info, int i) : data(info.data[i]) { init(text, info); name = text->statName( info.name + "_" + (info.subnames[i].empty() ? (std::to_string(i)) : info.subnames[i])); if (!info.subdescs[i].empty()) desc = info.subdescs[i]; unitStr = info.unit->getUnitString(); } void DistPrint::init(const Text *text, const Info &info) { setup(text->statName(info.name), info.flags, info.precision, text->descriptions, info.desc, text->enableUnits, info.unit->getUnitString(), text->spaces); separatorString = info.separatorString; if (spaces) { nameSpaces = 40; } else { nameSpaces = 0; } } void DistPrint::operator()(std::ostream &stream) const { if (flags.isSet(nozero) && data.samples == 0) return; std::string base = name + separatorString; ScalarPrint print(spaces); print.precision = precision; print.flags = flags; print.descriptions = descriptions; print.desc = desc; print.unitStr = unitStr; print.pdf = Nan; print.cdf = Nan; if (flags.isSet(oneline)) { print.name = base + "bucket_size"; print.value = data.bucket_size; print(stream); print.name = base + "min_bucket"; print.value = data.min; print(stream); print.name = base + "max_bucket"; print.value = data.max; print(stream); } print.name = base + "samples"; print.value = data.samples; print(stream); print.name = base + "mean"; print.value = data.samples ? data.sum / data.samples : Nan; print(stream); if (data.type == Hist) { print.name = base + "gmean"; print.value = data.samples ? exp(data.logs / data.samples) : Nan; print(stream); } Result stdev = Nan; if (data.samples) stdev = sqrt((data.samples * data.squares - data.sum * data.sum) / (data.samples * (data.samples - 1.0))); print.name = base + "stdev"; print.value = stdev; print(stream); if (data.type == Deviation) return; size_t size = data.cvec.size(); Result total = 0.0; if (data.type == Dist && data.underflow != Nan) total += data.underflow; for (off_type i = 0; i < size; ++i) total += data.cvec[i]; if (data.type == Dist && data.overflow != Nan) total += data.overflow; if (total) { print.pdf = 0.0; print.cdf = 0.0; } if (data.type == Dist && data.underflow != Nan) { print.name = base + "underflows"; print.update(data.underflow, total); print(stream); } if (flags.isSet(oneline)) { ccprintf(stream, "%-*s", nameSpaces, name); } for (off_type i = 0; i < size; ++i) { std::stringstream namestr; namestr << base; Counter low = i * data.bucket_size + data.min; Counter high = std::min(low + data.bucket_size - 1.0, data.max); namestr << low; if (low < high) namestr << "-" << high; print.name = namestr.str(); print.update(data.cvec[i], total); print(stream, flags.isSet(oneline)); } if (flags.isSet(oneline)) { if (descriptions) { if (!desc.empty()) ccprintf(stream, " # %s", desc); } printUnits(stream); stream << std::endl; } if (data.type == Dist && data.overflow != Nan) { print.name = base + "overflows"; print.update(data.overflow, total); print(stream); } print.pdf = Nan; print.cdf = Nan; if (data.type == Dist && data.min_val != Nan) { print.name = base + "min_value"; print.value = data.min_val; print(stream); } if (data.type == Dist && data.max_val != Nan) { print.name = base + "max_value"; print.value = data.max_val; print(stream); } print.name = base + "total"; print.value = total; print(stream); } void Text::visit(const ScalarInfo &info) { if (noOutput(info)) return; ScalarPrint print(spaces); print.setup(statName(info.name), info.flags, info.precision, descriptions, info.desc, enableUnits, info.unit->getUnitString(), spaces); print.value = info.result(); print.pdf = Nan; print.cdf = Nan; print(*stream); } void Text::visit(const VectorInfo &info) { if (noOutput(info)) return; size_type size = info.size(); VectorPrint print(spaces); print.setup(statName(info.name), info.flags, info.precision, descriptions, info.desc, enableUnits, info.unit->getUnitString(), spaces); print.separatorString = info.separatorString; print.vec = info.result(); print.total = info.total(); print.forceSubnames = false; if (!info.subnames.empty()) { for (off_type i = 0; i < size; ++i) { if (!info.subnames[i].empty()) { print.subnames = info.subnames; print.subnames.resize(size); for (off_type i = 0; i < size; ++i) { if (!info.subnames[i].empty() && !info.subdescs[i].empty()) { print.subdescs = info.subdescs; print.subdescs.resize(size); break; } } break; } } } print(*stream); } void Text::visit(const Vector2dInfo &info) { if (noOutput(info)) return; bool havesub = false; VectorPrint print(spaces); if (!info.y_subnames.empty()) { for (off_type i = 0; i < info.y; ++i) { if (!info.y_subnames[i].empty()) { print.subnames = info.y_subnames; break; } } } print.flags = info.flags; print.separatorString = info.separatorString; print.descriptions = descriptions; print.enableUnits = enableUnits; print.precision = info.precision; print.forceSubnames = true; if (!info.subnames.empty()) { for (off_type i = 0; i < info.x; ++i) if (!info.subnames[i].empty()) havesub = true; } VResult tot_vec(info.y); for (off_type i = 0; i < info.x; ++i) { if (havesub && (i >= info.subnames.size() || info.subnames[i].empty())) continue; off_type iy = i * info.y; VResult yvec(info.y); Result total = 0.0; for (off_type j = 0; j < info.y; ++j) { yvec[j] = info.cvec[iy + j]; tot_vec[j] += yvec[j]; total += yvec[j]; } print.name = statName( info.name + "_" + (havesub ? info.subnames[i] : std::to_string(i))); print.desc = info.desc; print.unitStr = info.unit->getUnitString(); print.vec = yvec; print.total = total; print(*stream); } // Create a subname for printing the total std::vector total_subname; total_subname.push_back("total"); if (info.flags.isSet(statistics::total) && (info.x > 1)) { print.name = statName(info.name); print.subnames = total_subname; print.desc = info.desc; print.unitStr = info.unit->getUnitString(); print.vec = VResult(1, info.total()); print.flags = print.flags & ~total; print(*stream); } } void Text::visit(const DistInfo &info) { if (noOutput(info)) return; DistPrint print(this, info); print(*stream); } void Text::visit(const VectorDistInfo &info) { if (noOutput(info)) return; for (off_type i = 0; i < info.size(); ++i) { DistPrint print(this, info, i); print(*stream); } } void Text::visit(const FormulaInfo &info) { visit((const VectorInfo &)info); } /* This struct implements the output methods for the sparse histogram stat */ struct SparseHistPrint : public BasePrint { std::string separatorString; const SparseHistData &data; SparseHistPrint(const Text *text, const SparseHistInfo &info); void init(const Text *text, const Info &info); void operator()(std::ostream &stream) const; }; /* Call initialization function */ SparseHistPrint::SparseHistPrint(const Text *text, const SparseHistInfo &info) : data(info.data) { init(text, info); } /* Initialization function */ void SparseHistPrint::init(const Text *text, const Info &info) { setup(text->statName(info.name), info.flags, info.precision, text->descriptions, info.desc, text->enableUnits, info.unit->getUnitString(), text->spaces); separatorString = info.separatorString; } /* Grab data from map and write to output stream */ void SparseHistPrint::operator()(std::ostream &stream) const { std::string base = name + separatorString; ScalarPrint print(spaces); print.setup(base + "samples", flags, precision, descriptions, desc, enableUnits, unitStr, spaces); print.pdf = Nan; print.cdf = Nan; print.value = data.samples; print(stream); MCounter::const_iterator it; for (it = data.cmap.begin(); it != data.cmap.end(); it++) { std::stringstream namestr; namestr << base; namestr <<(*it).first; print.name = namestr.str(); print.value = (*it).second; print(stream); } } void Text::visit(const SparseHistInfo &info) { if (noOutput(info)) return; SparseHistPrint print(this, info); print(*stream); } Output * initText(const std::string &filename, bool desc, bool spaces) { static Text text; static bool connected = false; if (!connected) { text.open(*simout.findOrCreate(filename)->stream()); text.descriptions = desc; text.enableUnits = desc; // the units are printed if descs are text.spaces = spaces; connected = true; } return &text; } } // namespace statistics } // namespace gem5