stats: Add support for hierarchical stats
authorAndreas Sandberg <andreas.sandberg@arm.com>
Wed, 26 Jun 2019 17:58:24 +0000 (18:58 +0100)
committerAndreas Sandberg <andreas.sandberg@arm.com>
Thu, 29 Aug 2019 09:01:38 +0000 (09:01 +0000)
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 <andreas.sandberg@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/19368
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br>
14 files changed:
src/base/SConscript
src/base/statistics.cc
src/base/statistics.hh
src/base/stats/group.cc [new file with mode: 0644]
src/base/stats/group.hh [new file with mode: 0644]
src/base/stats/output.hh
src/base/stats/text.cc
src/base/stats/text.hh
src/python/m5/SimObject.py
src/python/m5/simulate.py
src/python/m5/stats/__init__.py
src/python/pybind11/stats.cc
src/sim/sim_object.cc
src/sim/sim_object.hh

index 9449a3d4e23f3df4063802f9c0112f59ea4354b6..96f7b5b5001160a536a25f6aea8b587620d38d74 100644 (file)
@@ -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',
index 123351b8017fc195c152f09042ad29d3ce7bde71..a186f976312278a35526defec52f0dbec8ad43b2 100644 (file)
@@ -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 <ParentClass>::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<Formula, FormulaInfoProxy>(parent, name, desc)
+
 {
 }
 
-Formula::Formula(Temp r)
+
+
+Formula::Formula(Group *parent, const char *name, const char *desc,
+                 const Temp &r)
+    : DataWrapVec<Formula, FormulaInfoProxy>(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
 {
index 6ddf7ff17beb95f65f3f8ee3e04433bb8a8f89dd..f4fa123e970d71780883f1c2495adae18dd8deee 100644 (file)
@@ -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 <string>
 #include <vector>
 
+#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<const Info *>(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<Derived, InfoProxyType>
   public:
     typedef InfoProxyType<Derived> Info;
 
-    DataWrapVec()
-    {}
-
-    DataWrapVec(const DataWrapVec &ref)
-    {}
-
-    void operator=(const DataWrapVec &)
+    DataWrapVec(Group *parent = nullptr, const char *name = nullptr,
+                const char *desc = nullptr)
+        : DataWrap<Derived, InfoProxyType>(parent, name, desc)
     {}
 
     // The following functions are specific to vectors.  If you use them
@@ -420,6 +441,11 @@ class DataWrapVec2d : public DataWrapVec<Derived, InfoProxyType>
   public:
     typedef InfoProxyType<Derived> Info;
 
+    DataWrapVec2d(Group *parent, const char *name, const char *desc)
+        : DataWrapVec<Derived, InfoProxyType>(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<Derived, ScalarInfoProxy>
     Counter value() const { return data()->value(); }
 
   public:
-    ScalarBase()
+    ScalarBase(Group *parent = nullptr, const char *name = nullptr,
+               const char *desc = nullptr)
+        : DataWrap<Derived, ScalarInfoProxy>(parent, name, desc)
     {
         this->doInit();
     }
@@ -807,7 +835,12 @@ class ValueBase : public DataWrap<Derived, ScalarInfoProxy>
     ProxyInfo *proxy;
 
   public:
-    ValueBase() : proxy(NULL) { }
+    ValueBase(Group *parent, const char *name, const char *desc)
+        : DataWrap<Derived, ScalarInfoProxy>(parent, name, desc),
+          proxy(NULL)
+    {
+    }
+
     ~ValueBase() { if (proxy) delete proxy; }
 
     template <class T>
@@ -1095,8 +1128,9 @@ class VectorBase : public DataWrapVec<Derived, VectorInfoProxy>
     }
 
   public:
-    VectorBase()
-        : storage(nullptr), _size(0)
+    VectorBase(Group *parent, const char *name, const char *desc)
+        : DataWrapVec<Derived, VectorInfoProxy>(parent, name, desc),
+          storage(nullptr), _size(0)
     {}
 
     ~VectorBase()
@@ -1235,8 +1269,9 @@ class Vector2dBase : public DataWrapVec2d<Derived, Vector2dInfoProxy>
     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<Derived, Vector2dInfoProxy>(parent, name, desc),
+          x(0), y(0), _size(0), storage(nullptr)
     {}
 
     ~Vector2dBase()
@@ -1851,7 +1886,10 @@ class DistBase : public DataWrap<Derived, DistInfoProxy>
     }
 
   public:
-    DistBase() { }
+    DistBase(Group *parent, const char *name, const char *desc)
+        : DataWrap<Derived, DistInfoProxy>(parent, name, desc)
+    {
+    }
 
     /**
      * Add a value to the distribtion n times. Calls sample on the storage
@@ -1945,8 +1983,9 @@ class VectorDistBase : public DataWrapVec<Derived, VectorDistInfoProxy>
     }
 
   public:
-    VectorDistBase()
-        : storage(NULL)
+    VectorDistBase(Group *parent, const char *name, const char *desc)
+        : DataWrapVec<Derived, VectorDistInfoProxy>(parent, name, desc),
+          storage(NULL)
     {}
 
     ~VectorDistBase()
@@ -2472,6 +2511,12 @@ class Scalar : public ScalarBase<Scalar, StatStor>
 {
   public:
     using ScalarBase<Scalar, StatStor>::operator=;
+
+    Scalar(Group *parent = nullptr, const char *name = nullptr,
+           const char *desc = nullptr)
+        : ScalarBase<Scalar, StatStor>(parent, name, desc)
+    {
+    }
 };
 
 /**
@@ -2482,10 +2527,22 @@ class Average : public ScalarBase<Average, AvgStor>
 {
   public:
     using ScalarBase<Average, AvgStor>::operator=;
+
+    Average(Group *parent = nullptr, const char *name = nullptr,
+            const char *desc = nullptr)
+        : ScalarBase<Average, AvgStor>(parent, name, desc)
+    {
+    }
 };
 
 class Value : public ValueBase<Value>
 {
+  public:
+    Value(Group *parent = nullptr, const char *name = nullptr,
+          const char *desc = nullptr)
+        : ValueBase<Value>(parent, name, desc)
+    {
+    }
 };
 
 /**
@@ -2494,6 +2551,12 @@ class Value : public ValueBase<Value>
  */
 class Vector : public VectorBase<Vector, StatStor>
 {
+  public:
+    Vector(Group *parent = nullptr, const char *name = nullptr,
+           const char *desc = nullptr)
+        : VectorBase<Vector, StatStor>(parent, name, desc)
+    {
+    }
 };
 
 /**
@@ -2502,6 +2565,12 @@ class Vector : public VectorBase<Vector, StatStor>
  */
 class AverageVector : public VectorBase<AverageVector, AvgStor>
 {
+  public:
+    AverageVector(Group *parent = nullptr, const char *name = nullptr,
+                  const char *desc = nullptr)
+        : VectorBase<AverageVector, AvgStor>(parent, name, desc)
+    {
+    }
 };
 
 /**
@@ -2510,6 +2579,12 @@ class AverageVector : public VectorBase<AverageVector, AvgStor>
  */
 class Vector2d : public Vector2dBase<Vector2d, StatStor>
 {
+  public:
+    Vector2d(Group *parent = nullptr, const char *name = nullptr,
+             const char *desc = nullptr)
+        : Vector2dBase<Vector2d, StatStor>(parent, name, desc)
+    {
+    }
 };
 
 /**
@@ -2519,6 +2594,12 @@ class Vector2d : public Vector2dBase<Vector2d, StatStor>
 class Distribution : public DistBase<Distribution, DistStor>
 {
   public:
+    Distribution(Group *parent = nullptr, const char *name = nullptr,
+                 const char *desc = nullptr)
+        : DistBase<Distribution, DistStor>(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<Distribution, DistStor>
 class Histogram : public DistBase<Histogram, HistStor>
 {
   public:
+    Histogram(Group *parent = nullptr, const char *name = nullptr,
+              const char *desc = nullptr)
+        : DistBase<Histogram, HistStor>(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<StandardDeviation, SampleStor>
     /**
      * Construct and initialize this distribution.
      */
-    StandardDeviation()
+    StandardDeviation(Group *parent = nullptr, const char *name = nullptr,
+                      const char *desc = nullptr)
+        : DistBase<StandardDeviation, SampleStor>(parent, name, desc)
     {
         SampleStor::Params *params = new SampleStor::Params;
         this->doInit();
@@ -2594,7 +2683,9 @@ class AverageDeviation : public DistBase<AverageDeviation, AvgSampleStor>
     /**
      * Construct and initialize this distribution.
      */
-    AverageDeviation()
+    AverageDeviation(Group *parent = nullptr, const char *name = nullptr,
+                     const char *desc = nullptr)
+        : DistBase<AverageDeviation, AvgSampleStor>(parent, name, desc)
     {
         AvgSampleStor::Params *params = new AvgSampleStor::Params;
         this->doInit();
@@ -2609,6 +2700,12 @@ class AverageDeviation : public DistBase<AverageDeviation, AvgSampleStor>
 class VectorDistribution : public VectorDistBase<VectorDistribution, DistStor>
 {
   public:
+    VectorDistribution(Group *parent = nullptr, const char *name = nullptr,
+                       const char *desc = nullptr)
+        : VectorDistBase<VectorDistribution, DistStor>(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<VectorStandardDeviation, SampleStor>
 {
   public:
+    VectorStandardDeviation(Group *parent = nullptr, const char *name = nullptr,
+                            const char *desc = nullptr)
+        : VectorDistBase<VectorStandardDeviation, SampleStor>(parent, name,
+                                                              desc)
+    {
+    }
+
     /**
      * Initialize storage for this distribution.
      * @param size The size of the vector.
@@ -2662,6 +2766,13 @@ class VectorAverageDeviation
     : public VectorDistBase<VectorAverageDeviation, AvgSampleStor>
 {
   public:
+    VectorAverageDeviation(Group *parent = nullptr, const char *name = nullptr,
+                           const char *desc = nullptr)
+        : VectorDistBase<VectorAverageDeviation, AvgSampleStor>(parent, name,
+                                                                desc)
+    {
+    }
+
     /**
      * Initialize storage for this distribution.
      * @param size The size of the vector.
@@ -2753,7 +2864,10 @@ class SparseHistBase : public DataWrap<Derived, SparseHistInfoProxy>
     }
 
   public:
-    SparseHistBase() { }
+    SparseHistBase(Group *parent, const char *name, const char *desc)
+        : DataWrap<Derived, SparseHistInfoProxy>(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<SparseHistogram, SparseHistStor>
 {
   public:
+    SparseHistogram(Group *parent = nullptr, const char *name = nullptr,
+                    const char *desc = nullptr)
+        : SparseHistBase<SparseHistogram, SparseHistStor>(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<Formula, FormulaInfoProxy>
     /**
      * 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<typename T>
+    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 (file)
index 0000000..2bfc89d
--- /dev/null
@@ -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 <cassert>
+
+#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<const SimObject *>(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<std::string, Group *> &
+Group::getStatGroups() const
+{
+    return statGroups;
+}
+
+const std::vector<Info *> &
+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 (file)
index 0000000..f65e464
--- /dev/null
@@ -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 <map>
+#include <vector>
+#include <string>
+
+/**
+ * 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<std::string, Group *> &getStatGroups() const;
+
+    /**
+     * Get all stats associated with this object.
+     */
+    const std::vector<Info *> &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<std::string, Group *> statGroups;
+    std::vector<Group *> mergedStatGroups;
+    std::vector<Info *> stats;
+};
+
+} // namespace Stats
+
+#endif // __BASE_STATS_GROUP_HH__
index 9cd33a5f963bd5fb7a3826a6fead4ea8da2bc781..fe3860dfbaa930a6cdd48e0a3b80fe179fd2b90d 100644 (file)
@@ -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;
index 10e94a7702f7a09bda251097f765e0844ea05472..da68188bd0f7466f1ade2804682e02cead35c4c9 100644 (file)
@@ -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;
index 8bb290a9916eab48546605479a62f6d99b4dafcc..c8fba5a19092bdc8a60addb703ca6d493ae561b7 100644 (file)
@@ -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 <iosfwd>
+#include <stack>
 #include <string>
 
 #include "base/stats/output.hh"
@@ -46,6 +59,9 @@ class Text : public Output
     bool mystream;
     std::ostream *stream;
 
+    // Object/group path
+    std::stack<std::string> 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);
index 317ae6c33bf5856b28c7177c927cbf639822eebf..3d2f123b970095f50b2ee094e1d4350c35a66e8f 100644 (file)
@@ -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"),
index f8da1fb32b37f32d4a60b908aa0c4417f6d5c4af..1369fb5edc7ee405713f7c8f2470ea4a154f68ef 100644 (file)
@@ -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()
index 019c7ebb8246301e4f335843c0cc08174a4c2b02..d9174d387a9453329533f042f2f40cef6749461b 100644 (file)
@@ -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()
 
index 39d9ce873145cae78cd4616685acc7d54a421647..1302c7cc5de6d5f51dcd4b869b7ccd8e906691f9 100644 (file)
@@ -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_<Stats::Info>(m, "Info")
+    py::class_<Stats::Info, std::unique_ptr<Stats::Info, py::nodelete>>(
+        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_<Stats::Group, std::unique_ptr<Stats::Group, py::nodelete>>(
+        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)
+        ;
 }
index 7b794a0b746f45a14437988d739bc848bebf809a..eb6e15ae1064222672b743f7738e10eee26a1fa0 100644 (file)
@@ -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.
  */
index 5c9bf0019d7312fd11fe593155b62b0e29465ad9..c938ba5e561f64b4c7df7d842cd78fd94d84570b 100644 (file)
@@ -52,6 +52,7 @@
 #include <string>
 #include <vector>
 
+#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 <i>before</i> 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<SimObject *> 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.
      */