blob: 5c77ee0b8e567551a2f49eb7960e202490582798 [file] [log] [blame]
/*
* 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) 2003-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.
*/
#include "base/statistics.hh"
#include <fstream>
#include <iomanip>
#include <list>
#include <map>
#include <string>
#include "base/callback.hh"
#include "base/cprintf.hh"
#include "base/debug.hh"
#include "base/hostinfo.hh"
#include "base/logging.hh"
#include "base/str.hh"
#include "base/time.hh"
#include "base/trace.hh"
#include "sim/root.hh"
using namespace std;
namespace Stats {
std::string Info::separatorString = "::";
// We wrap these in a function to make sure they're built in time.
list<Info *> &
statsList()
{
static list<Info *> the_list;
return the_list;
}
MapType &
statsMap()
{
static MapType the_map;
return the_map;
}
void
InfoAccess::setInfo(Group *parent, Info *info)
{
panic_if(statsMap().find(this) != statsMap().end() ||
_info != nullptr,
"shouldn't register stat twice!");
// New-style stats are reachable through the hierarchy and
// shouldn't be added to the global lists.
if (parent) {
_info = info;
return;
}
statsList().push_back(info);
#ifndef NDEBUG
pair<MapType::iterator, bool> result =
#endif
statsMap().insert(make_pair(this, info));
assert(result.second && "this should never fail");
assert(statsMap().find(this) != statsMap().end());
}
void
InfoAccess::setParams(const StorageParams *params)
{
info()->storageParams = params;
}
void
InfoAccess::setInit()
{
info()->flags.set(init);
}
Info *
InfoAccess::info()
{
if (_info) {
// New-style stats
return _info;
} else {
// Legacy stats
MapType::const_iterator i = statsMap().find(this);
assert(i != statsMap().end());
return (*i).second;
}
}
const Info *
InfoAccess::info() const
{
if (_info) {
// New-style stats
return _info;
} else {
// Legacy stats
MapType::const_iterator i = statsMap().find(this);
assert(i != statsMap().end());
return (*i).second;
}
}
StorageParams::~StorageParams()
{
}
NameMapType &
nameMap()
{
static NameMapType the_map;
return the_map;
}
int Info::id_count = 0;
int debug_break_id = -1;
Info::Info()
: flags(none), precision(-1), prereq(0), storageParams(NULL)
{
id = id_count++;
if (debug_break_id >= 0 and debug_break_id == id)
Debug::breakpoint();
}
Info::~Info()
{
}
bool
validateStatName(const string &name)
{
if (name.empty())
return false;
vector<string> vec;
tokenize(vec, name, '.');
vector<string>::const_iterator item = vec.begin();
while (item != vec.end()) {
if (item->empty())
return false;
string::const_iterator c = item->begin();
// The first character is different
if (!isalpha(*c) && *c != '_')
return false;
// The rest of the characters have different rules.
while (++c != item->end()) {
if (!isalnum(*c) && *c != '_')
return false;
}
++item;
}
return true;
}
void
Info::setName(const string &name)
{
setName(nullptr, name);
}
void
Info::setName(const Group *parent, const string &name)
{
if (!validateStatName(name))
panic("invalid stat name '%s'", name);
// We only register the stat with the nameMap() if we are using
// old-style stats without a parent group. New-style stats should
// be unique since their names should correspond to a member
// variable.
if (!parent) {
auto p = nameMap().insert(make_pair(name, this));
if (!p.second)
panic("same statistic name used twice! name=%s\n",
name);
}
this->name = name;
}
bool
Info::less(Info *stat1, Info *stat2)
{
const string &name1 = stat1->name;
const string &name2 = stat2->name;
vector<string> v1;
vector<string> v2;
tokenize(v1, name1, '.');
tokenize(v2, name2, '.');
size_type last = min(v1.size(), v2.size()) - 1;
for (off_type i = 0; i < last; ++i)
if (v1[i] != v2[i])
return v1[i] < v2[i];
// Special compare for last element.
if (v1[last] == v2[last])
return v1.size() < v2.size();
else
return v1[last] < v2[last];
return false;
}
bool
Info::baseCheck() const
{
if (!(flags & Stats::init)) {
#ifdef DEBUG
cprintf("this is stat number %d\n", id);
#endif
panic("Not all stats have been initialized.\n"
"You may need to add <ParentClass>::regStats() to a"
" new SimObject's regStats() function. Name: %s",
name);
return false;
}
if ((flags & display) && name.empty()) {
panic("all printable stats must be named");
return false;
}
return true;
}
void
Info::enable()
{
}
void
VectorInfo::enable()
{
size_type s = size();
if (subnames.size() < s)
subnames.resize(s);
if (subdescs.size() < s)
subdescs.resize(s);
}
void
VectorDistInfo::enable()
{
size_type s = size();
if (subnames.size() < s)
subnames.resize(s);
if (subdescs.size() < s)
subdescs.resize(s);
}
void
Vector2dInfo::enable()
{
if (subnames.size() < x)
subnames.resize(x);
if (subdescs.size() < x)
subdescs.resize(x);
if (y_subnames.size() < y)
y_subnames.resize(y);
}
void
HistStor::grow_out()
{
int size = cvec.size();
int zero = size / 2; // round down!
int top_half = zero + (size - zero + 1) / 2; // round up!
int bottom_half = (size - zero) / 2; // round down!
// grow down
int low_pair = zero - 1;
for (int i = zero - 1; i >= bottom_half; i--) {
cvec[i] = cvec[low_pair];
if (low_pair - 1 >= 0)
cvec[i] += cvec[low_pair - 1];
low_pair -= 2;
}
assert(low_pair == 0 || low_pair == -1 || low_pair == -2);
for (int i = bottom_half - 1; i >= 0; i--)
cvec[i] = Counter();
// grow up
int high_pair = zero;
for (int i = zero; i < top_half; i++) {
cvec[i] = cvec[high_pair];
if (high_pair + 1 < size)
cvec[i] += cvec[high_pair + 1];
high_pair += 2;
}
assert(high_pair == size || high_pair == size + 1);
for (int i = top_half; i < size; i++)
cvec[i] = Counter();
max_bucket *= 2;
min_bucket *= 2;
bucket_size *= 2;
}
void
HistStor::grow_convert()
{
int size = cvec.size();
int half = (size + 1) / 2; // round up!
//bool even = (size & 1) == 0;
int pair = size - 1;
for (int i = size - 1; i >= half; --i) {
cvec[i] = cvec[pair];
if (pair - 1 >= 0)
cvec[i] += cvec[pair - 1];
pair -= 2;
}
for (int i = half - 1; i >= 0; i--)
cvec[i] = Counter();
min_bucket = -max_bucket;// - (even ? bucket_size : 0);
bucket_size *= 2;
}
void
HistStor::grow_up()
{
int size = cvec.size();
int half = (size + 1) / 2; // round up!
int pair = 0;
for (int i = 0; i < half; i++) {
cvec[i] = cvec[pair];
if (pair + 1 < size)
cvec[i] += cvec[pair + 1];
pair += 2;
}
assert(pair == size || pair == size + 1);
for (int i = half; i < size; i++)
cvec[i] = Counter();
max_bucket *= 2;
bucket_size *= 2;
}
void
HistStor::add(HistStor *hs)
{
int b_size = hs->size();
assert(size() == b_size);
assert(min_bucket == hs->min_bucket);
sum += hs->sum;
logs += hs->logs;
squares += hs->squares;
samples += hs->samples;
while (bucket_size > hs->bucket_size)
hs->grow_up();
while (bucket_size < hs->bucket_size)
grow_up();
for (uint32_t i = 0; i < b_size; i++)
cvec[i] += hs->cvec[i];
}
Formula::Formula(Group *parent, const char *name, const char *desc)
: DataWrapVec<Formula, FormulaInfoProxy>(parent, name, desc)
{
}
Formula::Formula(Group *parent, const char *name, const char *desc,
const Temp &r)
: DataWrapVec<Formula, FormulaInfoProxy>(parent, name, desc)
{
*this = r;
}
const Formula &
Formula::operator=(const Temp &r)
{
assert(!root && "Can't change formulas");
root = r.getNodePtr();
setInit();
assert(size());
return *this;
}
const Formula &
Formula::operator+=(Temp r)
{
if (root)
root = NodePtr(new BinaryNode<std::plus<Result> >(root, r));
else {
root = r.getNodePtr();
setInit();
}
assert(size());
return *this;
}
const Formula &
Formula::operator/=(Temp r)
{
assert (root);
root = NodePtr(new BinaryNode<std::divides<Result> >(root, r));
assert(size());
return *this;
}
void
Formula::result(VResult &vec) const
{
if (root)
vec = root->result();
}
Result
Formula::total() const
{
return root ? root->total() : 0.0;
}
size_type
Formula::size() const
{
if (!root)
return 0;
else
return root->size();
}
void
Formula::reset()
{
}
bool
Formula::zero() const
{
VResult vec;
result(vec);
for (VResult::size_type i = 0; i < vec.size(); ++i)
if (vec[i] != 0.0)
return false;
return true;
}
string
Formula::str() const
{
return root ? root->str() : "";
}
Handler resetHandler = NULL;
Handler dumpHandler = NULL;
void
registerHandlers(Handler reset_handler, Handler dump_handler)
{
resetHandler = reset_handler;
dumpHandler = dump_handler;
}
CallbackQueue dumpQueue;
CallbackQueue resetQueue;
void
processResetQueue()
{
resetQueue.process();
}
void
processDumpQueue()
{
dumpQueue.process();
}
void
registerResetCallback(const std::function<void()> &callback)
{
resetQueue.push_back(callback);
}
bool _enabled = false;
bool
enabled()
{
return _enabled;
}
void
enable()
{
if (_enabled)
fatal("Stats are already enabled");
_enabled = true;
}
void
dump()
{
if (dumpHandler)
dumpHandler();
else
fatal("No registered Stats::dump handler");
}
void
reset()
{
if (resetHandler)
resetHandler();
else
fatal("No registered Stats::reset handler");
}
const Info *
resolve(const std::string &name)
{
const auto &it = nameMap().find(name);
if (it != nameMap().cend()) {
return it->second;
} else {
return Root::root()->resolveStat(name);
}
}
void
registerDumpCallback(const std::function<void()> &callback)
{
dumpQueue.push_back(callback);
}
} // namespace Stats
void
debugDumpStats()
{
Stats::dump();
}