From: Andreas Sandberg Date: Wed, 26 Jun 2019 17:58:24 +0000 (+0100) Subject: stats: Add support for hierarchical stats X-Git-Tag: v19.0.0.0~614 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=6f38428abbe106d63318dd86bfa82a03b6b368ee;p=gem5.git stats: Add support for hierarchical stats This change makes the stat system aware of the hierarchical nature of stats. The aim is to achieve the following goals: * Make the SimObject hierarchy explicit in the stat system (i.e., get rid of name() + ".foo"). This makes stat naming less fragile and makes it possible to implement hierarchical formats like XML/HDF5/JSON in a clean way. * Make it more convenient to split stats into a separate struct/class that can be bound to a SimObject. This makes the namespace cleaner and makes stat accesses a bit more obvious. * Make it possible to build groups of stats in C++ that can be used in subcomponents in a SimObject (similar to what we do for checkpoint sections). This makes it easier to structure large components. * Enable partial stat dumps. Some of our internal users have been asking for this since a full stat dump can be large. * Enable better stat access from Python. This changeset implements solves the first three points by introducing a class (Stats::Group) that owns statistics belonging to the same object. SimObjects inherit from Stats::Group since they typically have statistics. New-style statistics need to be associated with a parent group at instantiation time. Instantiation typically sets the name and the description, other parameters need to be set by overriding Group::regStats() just like with legacy stats. Simple objects with scalar stats can typically avoid implementing regStats() altogether since the stat name and description are both specified in the constructor. For convenience reasons, statistics groups can be merged into other groups. This means that a SimObject can create a stat struct that inherits from Stats::Group and merge it into the parent group (SimObject). This can make the code cleaner since statistics tracking gets grouped into a single object. Stat visitors have a new API to expose the group structure. The Output::beginGroup(name) method is called at the beginning of a group and the Output::endGroup() method is called when all stats, and sub-groups, have been visited. Flat formats (e.g., the text format) typically need to maintain a stack to track the full path to a stat. Legacy, flat, statistics are still supported after applying this change. These stats don't belong to any group and stat visitors will not see a Output::beginGroup(name) call before their corresponding Output::visit() methods are called. Change-Id: I9025d61dfadeabcc8ecf30813ab2060def455648 Signed-off-by: Andreas Sandberg Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/19368 Tested-by: kokoro Reviewed-by: Jason Lowe-Power Reviewed-by: Daniel Carvalho --- diff --git a/src/base/SConscript b/src/base/SConscript index 9449a3d4e..96f7b5b50 100644 --- a/src/base/SConscript +++ b/src/base/SConscript @@ -81,6 +81,7 @@ Source('loader/object_file.cc') Source('loader/raw_object.cc') Source('loader/symtab.cc') +Source('stats/group.cc') Source('stats/text.cc') GTest('addr_range.test', 'addr_range.test.cc') @@ -101,6 +102,7 @@ DebugFlag('GDBRecv', "Messages received from the remote application") DebugFlag('GDBSend', "Messages sent to the remote application") DebugFlag('GDBWrite', "Writes to the remote address space") DebugFlag('SQL', "SQL queries sent to the server") +DebugFlag('Stats', "Statistics management") DebugFlag('StatEvents', "Statistics event tracking") CompoundFlag('GDBAll', diff --git a/src/base/statistics.cc b/src/base/statistics.cc index 123351b80..a186f9763 100644 --- a/src/base/statistics.cc +++ b/src/base/statistics.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2019 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. * @@ -67,10 +79,18 @@ statsMap() } void -InfoAccess::setInfo(Info *info) +InfoAccess::setInfo(Group *parent, Info *info) { - if (statsMap().find(this) != statsMap().end()) - panic("shouldn't register stat twice!"); + 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); @@ -97,17 +117,29 @@ InfoAccess::setInit() Info * InfoAccess::info() { - MapType::const_iterator i = statsMap().find(this); - assert(i != statsMap().end()); - return (*i).second; + 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 { - MapType::const_iterator i = statsMap().find(this); - assert(i != statsMap().end()); - return (*i).second; + 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() @@ -224,7 +256,8 @@ Info::baseCheck() const #endif panic("Not all stats have been initialized.\n" "You may need to add ::regStats() to a" - " new SimObject's regStats() function."); + " new SimObject's regStats() function. Name: %s", + name); return false; } @@ -376,19 +409,23 @@ HistStor::add(HistStor *hs) cvec[i] += hs->cvec[i]; } -Formula::Formula() +Formula::Formula(Group *parent, const char *name, const char *desc) + : DataWrapVec(parent, name, desc) + { } -Formula::Formula(Temp r) + + +Formula::Formula(Group *parent, const char *name, const char *desc, + const Temp &r) + : DataWrapVec(parent, name, desc) { - root = r.getNodePtr(); - setInit(); - assert(size()); + *this = r; } const Formula & -Formula::operator=(Temp r) +Formula::operator=(const Temp &r) { assert(!root && "Can't change formulas"); root = r.getNodePtr(); @@ -421,6 +458,7 @@ Formula::operator/=(Temp r) return *this; } + void Formula::result(VResult &vec) const { diff --git a/src/base/statistics.hh b/src/base/statistics.hh index 6ddf7ff17..f4fa123e9 100644 --- a/src/base/statistics.hh +++ b/src/base/statistics.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2019 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 * Copyright (c) 2017, Centre National de la Recherche Scientifique * All rights reserved. @@ -63,6 +75,7 @@ #include #include +#include "base/stats/group.hh" #include "base/stats/info.hh" #include "base/stats/output.hh" #include "base/stats/types.hh" @@ -172,9 +185,12 @@ struct StorageParams class InfoAccess { + private: + Info *_info; + protected: /** Set up an info class for this statistic */ - void setInfo(Info *info); + void setInfo(Group *parent, Info *info); /** Save Storage class parameters if any */ void setParams(const StorageParams *params); /** Save Storage class parameters if any */ @@ -186,6 +202,9 @@ class InfoAccess const Info *info() const; public: + InfoAccess() + : _info(nullptr) {}; + /** * Reset the stat to the default state. */ @@ -228,21 +247,27 @@ class DataWrap : public InfoAccess return safe_cast(InfoAccess::info()); } - protected: - /** - * Copy constructor, copies are not allowed. - */ - DataWrap(const DataWrap &stat) = delete; + public: + DataWrap() = delete; + DataWrap(const DataWrap &) = delete; + DataWrap &operator=(const DataWrap &) = delete; - /** - * Can't copy stats. - */ - void operator=(const DataWrap &) {} - public: - DataWrap() + DataWrap(Group *parent, const char *name, const char *desc) { - this->setInfo(new Info(self())); + auto info = new Info(self()); + this->setInfo(parent, info); + + if (parent) + parent->addStat(info); + + if (name) { + info->setName(name); + info->flags.set(display); + } + + if (desc) + info->desc = desc; } /** @@ -335,13 +360,9 @@ class DataWrapVec : public DataWrap public: typedef InfoProxyType Info; - DataWrapVec() - {} - - DataWrapVec(const DataWrapVec &ref) - {} - - void operator=(const DataWrapVec &) + DataWrapVec(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : DataWrap(parent, name, desc) {} // The following functions are specific to vectors. If you use them @@ -420,6 +441,11 @@ class DataWrapVec2d : public DataWrapVec public: typedef InfoProxyType Info; + DataWrapVec2d(Group *parent, const char *name, const char *desc) + : DataWrapVec(parent, name, desc) + { + } + /** * @warning This makes the assumption that if you're gonna subnames a 2d * vector, you're subnaming across all y @@ -677,7 +703,9 @@ class ScalarBase : public DataWrap Counter value() const { return data()->value(); } public: - ScalarBase() + ScalarBase(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : DataWrap(parent, name, desc) { this->doInit(); } @@ -807,7 +835,12 @@ class ValueBase : public DataWrap ProxyInfo *proxy; public: - ValueBase() : proxy(NULL) { } + ValueBase(Group *parent, const char *name, const char *desc) + : DataWrap(parent, name, desc), + proxy(NULL) + { + } + ~ValueBase() { if (proxy) delete proxy; } template @@ -1095,8 +1128,9 @@ class VectorBase : public DataWrapVec } public: - VectorBase() - : storage(nullptr), _size(0) + VectorBase(Group *parent, const char *name, const char *desc) + : DataWrapVec(parent, name, desc), + storage(nullptr), _size(0) {} ~VectorBase() @@ -1235,8 +1269,9 @@ class Vector2dBase : public DataWrapVec2d const Storage *data(off_type index) const { return &storage[index]; } public: - Vector2dBase() - : x(0), y(0), _size(0), storage(nullptr) + Vector2dBase(Group *parent, const char *name, const char *desc) + : DataWrapVec2d(parent, name, desc), + x(0), y(0), _size(0), storage(nullptr) {} ~Vector2dBase() @@ -1851,7 +1886,10 @@ class DistBase : public DataWrap } public: - DistBase() { } + DistBase(Group *parent, const char *name, const char *desc) + : DataWrap(parent, name, desc) + { + } /** * Add a value to the distribtion n times. Calls sample on the storage @@ -1945,8 +1983,9 @@ class VectorDistBase : public DataWrapVec } public: - VectorDistBase() - : storage(NULL) + VectorDistBase(Group *parent, const char *name, const char *desc) + : DataWrapVec(parent, name, desc), + storage(NULL) {} ~VectorDistBase() @@ -2472,6 +2511,12 @@ class Scalar : public ScalarBase { public: using ScalarBase::operator=; + + Scalar(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : ScalarBase(parent, name, desc) + { + } }; /** @@ -2482,10 +2527,22 @@ class Average : public ScalarBase { public: using ScalarBase::operator=; + + Average(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : ScalarBase(parent, name, desc) + { + } }; class Value : public ValueBase { + public: + Value(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : ValueBase(parent, name, desc) + { + } }; /** @@ -2494,6 +2551,12 @@ class Value : public ValueBase */ class Vector : public VectorBase { + public: + Vector(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : VectorBase(parent, name, desc) + { + } }; /** @@ -2502,6 +2565,12 @@ class Vector : public VectorBase */ class AverageVector : public VectorBase { + public: + AverageVector(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : VectorBase(parent, name, desc) + { + } }; /** @@ -2510,6 +2579,12 @@ class AverageVector : public VectorBase */ class Vector2d : public Vector2dBase { + public: + Vector2d(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : Vector2dBase(parent, name, desc) + { + } }; /** @@ -2519,6 +2594,12 @@ class Vector2d : public Vector2dBase class Distribution : public DistBase { public: + Distribution(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : DistBase(parent, name, desc) + { + } + /** * Set the parameters of this distribution. @sa DistStor::Params * @param min The minimum value of the distribution. @@ -2550,6 +2631,12 @@ class Distribution : public DistBase class Histogram : public DistBase { public: + Histogram(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : DistBase(parent, name, desc) + { + } + /** * Set the parameters of this histogram. @sa HistStor::Params * @param size The number of buckets in the histogram @@ -2576,7 +2663,9 @@ class StandardDeviation : public DistBase /** * Construct and initialize this distribution. */ - StandardDeviation() + StandardDeviation(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : DistBase(parent, name, desc) { SampleStor::Params *params = new SampleStor::Params; this->doInit(); @@ -2594,7 +2683,9 @@ class AverageDeviation : public DistBase /** * Construct and initialize this distribution. */ - AverageDeviation() + AverageDeviation(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : DistBase(parent, name, desc) { AvgSampleStor::Params *params = new AvgSampleStor::Params; this->doInit(); @@ -2609,6 +2700,12 @@ class AverageDeviation : public DistBase class VectorDistribution : public VectorDistBase { public: + VectorDistribution(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : VectorDistBase(parent, name, desc) + { + } + /** * Initialize storage and parameters for this distribution. * @param size The size of the vector (the number of distributions). @@ -2639,6 +2736,13 @@ class VectorStandardDeviation : public VectorDistBase { public: + VectorStandardDeviation(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : VectorDistBase(parent, name, + desc) + { + } + /** * Initialize storage for this distribution. * @param size The size of the vector. @@ -2662,6 +2766,13 @@ class VectorAverageDeviation : public VectorDistBase { public: + VectorAverageDeviation(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : VectorDistBase(parent, name, + desc) + { + } + /** * Initialize storage for this distribution. * @param size The size of the vector. @@ -2753,7 +2864,10 @@ class SparseHistBase : public DataWrap } public: - SparseHistBase() { } + SparseHistBase(Group *parent, const char *name, const char *desc) + : DataWrap(parent, name, desc) + { + } /** * Add a value to the distribtion n times. Calls sample on the storage @@ -2870,6 +2984,12 @@ class SparseHistStor class SparseHistogram : public SparseHistBase { public: + SparseHistogram(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr) + : SparseHistBase(parent, name, desc) + { + } + /** * Set the parameters of this histogram. @sa HistStor::Params * @param size The number of buckets in the histogram @@ -2902,21 +3022,25 @@ class Formula : public DataWrapVec /** * Create and initialize thie formula, and register it with the database. */ - Formula(); + Formula(Group *parent = nullptr, const char *name = nullptr, + const char *desc = nullptr); - /** - * Create a formula with the given root node, register it with the - * database. - * @param r The root of the expression tree. - */ - Formula(Temp r); + Formula(Group *parent, const char *name, const char *desc, + const Temp &r); /** * Set an unitialized Formula to the given root. * @param r The root of the expression tree. * @return a reference to this formula. */ - const Formula &operator=(Temp r); + const Formula &operator=(const Temp &r); + + template + const Formula &operator=(const T &v) + { + *this = Temp(v); + return *this; + } /** * Add the given tree to the existing one. diff --git a/src/base/stats/group.cc b/src/base/stats/group.cc new file mode 100644 index 000000000..2bfc89d00 --- /dev/null +++ b/src/base/stats/group.cc @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2019 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. + * + * 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. + * + * Authors: Andreas Sandberg + */ + +#include "base/stats/group.hh" + +#include + +#include "base/stats/info.hh" +#include "base/trace.hh" +#include "debug/Stats.hh" +#include "sim/sim_object.hh" + +namespace Stats { + +Group::Group(Group *parent, const char *name) + : mergedParent(name ? nullptr : parent) +{ + if (parent && name) { + parent->addStatGroup(name, this); + } else if (parent && !name) { + parent->mergeStatGroup(this); + } +} + +Group::~Group() +{ +} + +void +Group::regStats() +{ + for (auto &g : mergedStatGroups) + g->regStats(); + + for (auto &g : statGroups) { + if (DTRACE(Stats)) { + const SimObject *so = dynamic_cast(this); + DPRINTF(Stats, "%s: regStats in group %s\n", + so ? so->name() : "?", + g.first); + } + g.second->regStats(); + } +} + +void +Group::resetStats() +{ + for (auto &s : stats) + s->reset(); + + for (auto &g : mergedStatGroups) + g->resetStats(); + + for (auto &g : statGroups) + g.second->resetStats(); +} + +void +Group::addStat(Stats::Info *info) +{ + stats.push_back(info); + if (mergedParent) + mergedParent->addStat(info); +} + +void +Group::addStatGroup(const char *name, Group *block) +{ + assert(statGroups.find(name) == statGroups.end()); + + statGroups[name] = block; +} + +void +Group::mergeStatGroup(Group *block) +{ + mergedStatGroups.push_back(block); +} + +const std::map & +Group::getStatGroups() const +{ + return statGroups; +} + +const std::vector & +Group::getStats() const +{ + return stats; +} + +} // namespace Stats diff --git a/src/base/stats/group.hh b/src/base/stats/group.hh new file mode 100644 index 000000000..f65e46448 --- /dev/null +++ b/src/base/stats/group.hh @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2019 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. + * + * 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. + * + * Authors: Andreas Sandberg + */ + +#ifndef __BASE_STATS_GROUP_HH__ +#define __BASE_STATS_GROUP_HH__ + +#include +#include +#include + +/** + * Convenience macro to add a stat to a statistics group. + * + * This macro is used to add a stat to a Stats::Group in the + * initilization list in the Group's constructor. The macro + * automatically assigns the stat to the current group and gives it + * the same name as in the class. For example: + * + * \code + * struct MyStats : public Stats::Group + * { + * Stats::Scalar scalar0; + * Stats::Scalar scalar1; + * + * Group() + * : ADD_STAT(scalar0, "Description of scalar0"), + * scalar1(this, "scalar1", "Description of scalar1") + * { + * } + * }; + * \endcode + */ +#define ADD_STAT(n, ...) n(this, # n, __VA_ARGS__) + +namespace Stats { + +class Info; + +/** + * Statistics container. + * + * A stat group is a hierarchical structure that contain statistics + * and other groups. Groups are used by the stat system to reflect + * gem5's SimObject hierarchy and to expose internal hierarchy within + * an object. They can also be used to conveniently group stats into + * their own class/struct and then be merged into the parent group + * (typically a SimObject). + */ +class Group +{ + public: + Group() = delete; + Group(const Group &) = delete; + Group &operator=(const Group &) = delete; + + /** + * Construct a new statistics group. + * + * The constructor takes two parameters, a parent and a name. The + * parent group should typically be specified. However, there are + * special cases where the parent group may be null. One such + * special case is SimObjects where the Python code performs late + * binding of the group parent. + * + * If the name parameter is NULL, the group gets merged into the + * parent group instead of creating a sub-group. Stats belonging + * to a merged group behave as if they have been added directly to + * the parent group. + * + * @param parent Parent group to associate this object to. + * @param name Name of this group, can be NULL to merge this group + * with the parent group. + */ + Group(Group *parent, const char *name = nullptr); + + virtual ~Group(); + + /** + * Callback to set stat parameters. + * + * This callback is typically used for complex stats (e.g., + * distributions) that need parameters in addition to a name and a + * description. Stat names and descriptions should typically be + * set from the constructor usingo from the constructor using the + * ADD_STAT macro. + */ + virtual void regStats(); + + /** + * Callback to reset stats. + */ + virtual void resetStats(); + + /** + * Register a stat with this group. This method is normally called + * automatically when a stat is instantiated. + */ + void addStat(Stats::Info *info); + + /** + * Get all child groups associated with this object. + */ + const std::map &getStatGroups() const; + + /** + * Get all stats associated with this object. + */ + const std::vector &getStats() const; + + /** + * Add a stat block as a child of this block + * + * This method may only be called from a Group constructor or from + * regStats. It's typically only called explicitly from Python + * when setting up the SimObject hierarchy. + */ + void addStatGroup(const char *name, Group *block); + + private: + /** + * Merge the contents (stats & children) of a block to this block. + * + * This is called on a parent group by the child when it is being + * merged into the parent. + */ + void mergeStatGroup(Group *block); + + private: + /** Parent pointer if merged into parent */ + Group *const mergedParent; + + std::map statGroups; + std::vector mergedStatGroups; + std::vector stats; +}; + +} // namespace Stats + +#endif // __BASE_STATS_GROUP_HH__ diff --git a/src/base/stats/output.hh b/src/base/stats/output.hh index 9cd33a5f9..fe3860dfb 100644 --- a/src/base/stats/output.hh +++ b/src/base/stats/output.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2019 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. * @@ -48,10 +60,14 @@ class SparseHistInfo; // Sparse histogram struct Output { virtual ~Output() {} + virtual void begin() = 0; virtual void end() = 0; virtual bool valid() const = 0; + virtual void beginGroup(const char *name) = 0; + virtual void endGroup() = 0; + virtual void visit(const ScalarInfo &info) = 0; virtual void visit(const VectorInfo &info) = 0; virtual void visit(const DistInfo &info) = 0; diff --git a/src/base/stats/text.cc b/src/base/stats/text.cc index 10e94a770..da68188bd 100644 --- a/src/base/stats/text.cc +++ b/src/base/stats/text.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2019 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. * @@ -153,6 +165,32 @@ Text::end() 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) { @@ -368,7 +406,7 @@ DistPrint::DistPrint(const Text *text, const VectorDistInfo &info, int i) void DistPrint::init(const Text *text, const Info &info) { - name = info.name; + name = text->statName(info.name); separatorString = info.separatorString; desc = info.desc; flags = info.flags; @@ -511,7 +549,7 @@ Text::visit(const ScalarInfo &info) ScalarPrint print; print.value = info.result(); - print.name = info.name; + print.name = statName(info.name); print.desc = info.desc; print.flags = info.flags; print.descriptions = descriptions; @@ -531,7 +569,7 @@ Text::visit(const VectorInfo &info) size_type size = info.size(); VectorPrint print; - print.name = info.name; + print.name = statName(info.name); print.separatorString = info.separatorString; print.desc = info.desc; print.flags = info.flags; @@ -606,8 +644,9 @@ Text::visit(const Vector2dInfo &info) total += yvec[j]; } - print.name = info.name + "_" + - (havesub ? info.subnames[i] : std::to_string(i)); + print.name = statName( + info.name + "_" + + (havesub ? info.subnames[i] : std::to_string(i))); print.desc = info.desc; print.vec = yvec; print.total = total; @@ -619,7 +658,7 @@ Text::visit(const Vector2dInfo &info) total_subname.push_back("total"); if (info.flags.isSet(::Stats::total) && (info.x > 1)) { - print.name = info.name; + print.name = statName(info.name); print.subnames = total_subname; print.desc = info.desc; print.vec = VResult(1, info.total()); @@ -687,7 +726,7 @@ SparseHistPrint::SparseHistPrint(const Text *text, const SparseHistInfo &info) void SparseHistPrint::init(const Text *text, const Info &info) { - name = info.name; + name = text->statName(info.name); separatorString = info.separatorString; desc = info.desc; flags = info.flags; diff --git a/src/base/stats/text.hh b/src/base/stats/text.hh index 8bb290a99..c8fba5a19 100644 --- a/src/base/stats/text.hh +++ b/src/base/stats/text.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2019 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. * @@ -32,6 +44,7 @@ #define __BASE_STATS_TEXT_HH__ #include +#include #include #include "base/stats/output.hh" @@ -46,6 +59,9 @@ class Text : public Output bool mystream; std::ostream *stream; + // Object/group path + std::stack path; + protected: bool noOutput(const Info &info); @@ -60,20 +76,25 @@ class Text : public Output void open(std::ostream &stream); void open(const std::string &file); + std::string statName(const std::string &name) const; // Implement Visit - virtual void visit(const ScalarInfo &info); - virtual void visit(const VectorInfo &info); - virtual void visit(const DistInfo &info); - virtual void visit(const VectorDistInfo &info); - virtual void visit(const Vector2dInfo &info); - virtual void visit(const FormulaInfo &info); - virtual void visit(const SparseHistInfo &info); + void visit(const ScalarInfo &info) override; + void visit(const VectorInfo &info) override; + void visit(const DistInfo &info) override; + void visit(const VectorDistInfo &info) override; + void visit(const Vector2dInfo &info) override; + void visit(const FormulaInfo &info) override; + void visit(const SparseHistInfo &info) override; + + // Group handling + void beginGroup(const char *name) override; + void endGroup() override; // Implement Output - virtual bool valid() const; - virtual void begin(); - virtual void end(); + bool valid() const override; + void begin() override; + void end() override; }; std::string ValueToString(Result value, int precision); diff --git a/src/python/m5/SimObject.py b/src/python/m5/SimObject.py index 317ae6c33..3d2f123b9 100644 --- a/src/python/m5/SimObject.py +++ b/src/python/m5/SimObject.py @@ -1086,7 +1086,7 @@ class SimObject(object): abstract = True cxx_header = "sim/sim_object.hh" - cxx_extra_bases = [ "Drainable", "Serializable" ] + cxx_extra_bases = [ "Drainable", "Serializable", "Stats::Group" ] eventq_index = Param.UInt32(Parent.eventq_index, "Event Queue Index") cxx_exports = [ @@ -1094,8 +1094,6 @@ class SimObject(object): PyBindMethod("initState"), PyBindMethod("memInvalidate"), PyBindMethod("memWriteback"), - PyBindMethod("regStats"), - PyBindMethod("resetStats"), PyBindMethod("regProbePoints"), PyBindMethod("regProbeListeners"), PyBindMethod("startup"), diff --git a/src/python/m5/simulate.py b/src/python/m5/simulate.py index f8da1fb32..1369fb5ed 100644 --- a/src/python/m5/simulate.py +++ b/src/python/m5/simulate.py @@ -124,7 +124,8 @@ def instantiate(ckpt_dir=None): for obj in root.descendants(): obj.init() # Do a third pass to initialize statistics - for obj in root.descendants(): obj.regStats() + stats._bindStatHierarchy(root) + root.regStats() # Do a fourth pass to initialize probe points for obj in root.descendants(): obj.regProbePoints() diff --git a/src/python/m5/stats/__init__.py b/src/python/m5/stats/__init__.py index 019c7ebb8..d9174d387 100644 --- a/src/python/m5/stats/__init__.py +++ b/src/python/m5/stats/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2017 ARM Limited +# Copyright (c) 2017, 2019 Arm Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -158,6 +158,29 @@ def initSimStats(): _m5.stats.initSimStats() _m5.stats.registerPythonStatsHandlers() +def _visit_groups(root, visitor): + for group in root.getStatGroups().values(): + visitor(group) + _visit_groups(group, visitor) + +def _visit_stats(root, visitor): + def for_each_stat(g): + for stat in g.getStats(): + visitor(g, stat) + _visit_groups(root, for_each_stat) + +def _bindStatHierarchy(root): + def _bind_obj(name, obj): + if m5.SimObject.isSimObjectVector(obj): + for idx, obj in enumerate(obj): + _bind_obj("{}{}".format(name, idx), obj) + else: + root.addStatGroup(name, obj.getCCObject()) + _bindStatHierarchy(obj) + + for name, obj in root._children.items(): + _bind_obj(name, obj) + names = [] stats_dict = {} stats_list = [] @@ -166,10 +189,7 @@ def enable(): enabled, all statistics must be created and initialized and once the package is enabled, no more statistics can be created.''' - global stats_list - stats_list = list(_m5.stats.statsList()) - - for stat in stats_list: + def check_stat(group, stat): if not stat.check() or not stat.baseCheck(): fatal("statistic '%s' (%d) was not properly initialized " \ "by a regStats() function\n", stat.name, stat.id) @@ -177,21 +197,57 @@ def enable(): if not (stat.flags & flags.display): stat.name = "__Stat%06d" % stat.id + + # Legacy stat + global stats_list + stats_list = list(_m5.stats.statsList()) + + for stat in stats_list: + check_stat(None, stat) + stats_list.sort(key=lambda s: s.name.split('.')) for stat in stats_list: stats_dict[stat.name] = stat stat.enable() + + # New stats + _visit_stats(Root.getInstance(), check_stat) + _visit_stats(Root.getInstance(), lambda g, s: s.enable()) + _m5.stats.enable(); def prepare(): '''Prepare all stats for data access. This must be done before dumping and serialization.''' + # Legacy stats for stat in stats_list: stat.prepare() + # New stats + _visit_stats(Root.getInstance(), lambda g, s: s.prepare()) + lastDump = 0 + +def _dump_to_visitor(visitor): + # Legacy stats + for stat in stats_list: + stat.visit(visitor) + + # New stats + def dump_group(group): + for stat in group.getStats(): + stat.visit(visitor) + + for n, g in group.getStatGroups().items(): + visitor.beginGroup(n) + dump_group(g) + visitor.endGroup() + + dump_group(Root.getInstance()) + + def dump(): '''Dump all statistics data to the registered outputs''' @@ -210,8 +266,7 @@ def dump(): for output in outputList: if output.valid(): output.begin() - for stat in stats_list: - stat.visit(output) + _dump_to_visitor(output) output.end() def reset(): @@ -220,9 +275,9 @@ def reset(): # call reset stats on all SimObjects root = Root.getInstance() if root: - for obj in root.descendants(): obj.resetStats() + root.resetStats() - # call any other registered stats reset callbacks + # call any other registered legacy stats reset callbacks for stat in stats_list: stat.reset() diff --git a/src/python/pybind11/stats.cc b/src/python/pybind11/stats.cc index 39d9ce873..1302c7cc5 100644 --- a/src/python/pybind11/stats.cc +++ b/src/python/pybind11/stats.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 ARM Limited + * Copyright (c) 2017, 2019 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -93,9 +93,12 @@ pybind_init_stats(py::module &m_native) .def("begin", &Stats::Output::begin) .def("end", &Stats::Output::end) .def("valid", &Stats::Output::valid) + .def("beginGroup", &Stats::Output::beginGroup) + .def("endGroup", &Stats::Output::endGroup) ; - py::class_(m, "Info") + py::class_>( + m, "Info") .def_readwrite("name", &Stats::Info::name) .def_readonly("desc", &Stats::Info::desc) .def_readonly("id", &Stats::Info::id) @@ -110,4 +113,13 @@ pybind_init_stats(py::module &m_native) .def("zero", &Stats::Info::zero) .def("visit", &Stats::Info::visit) ; + + py::class_>( + m, "Group") + .def("regStats", &Stats::Group::regStats) + .def("resetStats", &Stats::Group::resetStats) + .def("getStats", &Stats::Group::getStats) + .def("getStatGroups", &Stats::Group::getStatGroups) + .def("addStatGroup", &Stats::Group::addStatGroup) + ; } diff --git a/src/sim/sim_object.cc b/src/sim/sim_object.cc index 7b794a0b7..eb6e15ae1 100644 --- a/src/sim/sim_object.cc +++ b/src/sim/sim_object.cc @@ -56,7 +56,9 @@ SimObject::SimObjectList SimObject::simObjectList; // SimObject constructor: used to maintain static simObjectList // SimObject::SimObject(const Params *p) - : EventManager(getEventQueue(p->eventq_index)), _params(p) + : EventManager(getEventQueue(p->eventq_index)), + Stats::Group(nullptr), + _params(p) { #ifdef DEBUG doDebugBreak = false; @@ -98,19 +100,6 @@ SimObject::startup() { } -// -// no default statistics, so nothing to do in base implementation -// -void -SimObject::regStats() -{ -} - -void -SimObject::resetStats() -{ -} - /** * No probe points by default, so do nothing in base. */ diff --git a/src/sim/sim_object.hh b/src/sim/sim_object.hh index 5c9bf0019..c938ba5e5 100644 --- a/src/sim/sim_object.hh +++ b/src/sim/sim_object.hh @@ -52,6 +52,7 @@ #include #include +#include "base/stats/group.hh" #include "params/SimObject.hh" #include "sim/drain.hh" #include "sim/eventq.hh" @@ -92,7 +93,8 @@ class ProbeManager; * SimObject.py). This has the effect of calling the method on the * parent node before its children. */ -class SimObject : public EventManager, public Serializable, public Drainable +class SimObject : public EventManager, public Serializable, public Drainable, + public Stats::Group { private: typedef std::vector SimObjectList; @@ -145,16 +147,6 @@ class SimObject : public EventManager, public Serializable, public Drainable */ virtual void initState(); - /** - * Register statistics for this object. - */ - virtual void regStats(); - - /** - * Reset statistics associated with this object. - */ - virtual void resetStats(); - /** * Register probe points for this object. */