stats: add --stats-root option to dump only under some SimObjects
[gem5.git] / src / python / m5 / stats / __init__.py
index 4b118ea13e7c9726f484eab763ae6d37d056f355..6c4a42cb8b4eb1dbc98ac08fea8bfa878dc79398 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2017-2019 ARM Limited
+# Copyright (c) 2017-2020 ARM Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -36,9 +36,6 @@
 # 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: Nathan Binkert
-#          Andreas Sandberg
 
 from __future__ import print_function
 from __future__ import absolute_import
@@ -47,6 +44,7 @@ import m5
 
 import _m5.stats
 from m5.objects import Root
+from m5.params import isNullPointer
 from m5.util import attrdict, fatal
 
 # Stat exports
@@ -133,7 +131,7 @@ def _url_factory(schemes, enable=True):
     return decorator
 
 @_url_factory([ None, "", "text", "file", ])
-def _textFactory(fn, desc=True):
+def _textFactory(fn, desc=True, spaces=True):
     """Output stats in text format.
 
     Text stat files contain one stat per line with an optional
@@ -142,13 +140,14 @@ def _textFactory(fn, desc=True):
 
     Parameters:
       * desc (bool): Output stat descriptions (default: True)
+      * spaces (bool): Output alignment spaces (default: True)
 
     Example:
-      text://stats.txt?desc=False
+      text://stats.txt?desc=False;spaces=False
 
     """
 
-    return _m5.stats.initText(fn, desc)
+    return _m5.stats.initText(fn, desc, spaces)
 
 @_url_factory([ "h5", ], enable=hasattr(_m5.stats, "initHDF5"))
 def _hdf5Factory(fn, chunking=10, desc=True, formulas=True):
@@ -253,9 +252,14 @@ def _visit_stats(visitor, root=None):
 
 def _bindStatHierarchy(root):
     def _bind_obj(name, obj):
+        if isNullPointer(obj):
+            return
         if m5.SimObject.isSimObjectVector(obj):
-            for idx, obj in enumerate(obj):
-                _bind_obj("{}{}".format(name, idx), obj)
+            if len(obj) == 1:
+                _bind_obj(name, obj[0])
+            else:
+                for idx, obj in enumerate(obj):
+                    _bind_obj("{}{}".format(name, idx), obj)
         else:
             # We need this check because not all obj.getCCObject() is an
             # instance of Stat::Group. For example, sc_core::sc_module, the C++
@@ -322,35 +326,45 @@ def prepare():
     # New stats
     _visit_stats(lambda g, s: s.prepare())
 
-def _dump_to_visitor(visitor, root=None):
-    # Legacy stats
-    if root is None:
-        for stat in stats_list:
-            stat.visit(visitor)
-
+def _dump_to_visitor(visitor, roots=None):
     # 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()
 
-    if root is not None:
-        for p in root.path_list():
-            visitor.beginGroup(p)
-    dump_group(root if root is not None else Root.getInstance())
-    if root is not None:
-        for p in reversed(root.path_list()):
-            visitor.endGroup()
+    if roots:
+        # New stats from selected subroots.
+        for root in roots:
+            for p in root.path_list():
+                visitor.beginGroup(p)
+            dump_group(root)
+            for p in reversed(root.path_list()):
+                visitor.endGroup()
+    else:
+        # Legacy stats
+        for stat in stats_list:
+            stat.visit(visitor)
+
+        # New stats starting from root.
+        dump_group(Root.getInstance())
 
 lastDump = 0
+# List[SimObject].
+global_dump_roots = []
 
-def dump(root=None):
+def dump(roots=None):
     '''Dump all statistics data to the registered outputs'''
 
+    all_roots = []
+    if roots is not None:
+        all_roots.extend(roots)
+    global global_dump_roots
+    all_roots.extend(global_dump_roots)
+
     now = m5.curTick()
     global lastDump
     assert lastDump <= now
@@ -359,18 +373,22 @@ def dump(root=None):
 
     # Don't allow multiple global stat dumps in the same tick. It's
     # still possible to dump a multiple sub-trees.
-    if not new_dump and root is None:
+    if not new_dump and not all_roots:
         return
 
     # Only prepare stats the first time we dump them in the same tick.
     if new_dump:
         _m5.stats.processDumpQueue()
+        # Notify new-style stats group that we are about to dump stats.
+        sim_root = Root.getInstance()
+        if sim_root:
+            sim_root.preDumpStats();
         prepare()
 
     for output in outputList:
         if output.valid():
             output.begin()
-            _dump_to_visitor(output, root=root)
+            _dump_to_visitor(output, roots=all_roots)
             output.end()
 
 def reset():