From bcb4d034747b62bb38d9587b8927f5a2959e317a Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Mon, 12 Dec 2022 13:48:26 -0800 Subject: [PATCH 01/20] Param: added PcCountPair Change-Id: Ib2e3f376bbbc48fc9a37036fd63e5dd39d72aa67 --- src/base/types.hh | 33 +++++++++++++++++++++++++++++++++ src/python/m5/params.py | 15 +++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/base/types.hh b/src/base/types.hh index 913455fca0..b38e1a4af7 100644 --- a/src/base/types.hh +++ b/src/base/types.hh @@ -138,6 +138,39 @@ class Cycles friend std::ostream& operator<<(std::ostream &out, const Cycles & cycles); }; +class PcCountPair +{ + + private: + + /** Member holding the actual value. */ + uint64_t pc; + int count; + + public: + + /** Explicit constructor assigning a value. */ + explicit constexpr PcCountPair(uint64_t _pc, int _count) : + pc(_pc), count(_count) { } + + /** Default constructor for parameter classes. */ + PcCountPair() : pc(0), count(0) { } + + /** Prefix increment operator. */ + PcCountPair& operator++() { ++count; return *this; } + + uint64_t getPC () const {return pc;} + int getCount() const {return count;} + + /** Greater than comparison used for > PcCountPair(0). */ + constexpr bool + operator>(const PcCountPair& cc) const + { + return count > cc.count; + } + +}; + /** * Address type * This will probably be moved somewhere else in the near future. diff --git a/src/python/m5/params.py b/src/python/m5/params.py index e76380bc40..96b2ddf5cc 100644 --- a/src/python/m5/params.py +++ b/src/python/m5/params.py @@ -852,6 +852,20 @@ def pretty_print(self, value): except TypeError: val = int(value) return "0x%x" % int(val) + +class PcCountPair(ParamValue): + cxx_type = "PcCountPair" + cmd_line_settable = True + + def __init__(self, _pc, _count): + self.pc = _pc + self.count = _count + + def getPC(self): + return Addr(self.pc) + + def getCount(self): + return int(self.count) class AddrRange(ParamValue): @@ -2426,4 +2440,5 @@ def clear(): "VectorMasterPort", "VectorSlavePort", "DeprecatedParam", + "PcCountPair", ] From 547db659f49313e839b21eb45dae950be1adcc92 Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Mon, 12 Dec 2022 17:43:28 -0800 Subject: [PATCH 02/20] PcCountTracker and PcCountTrackerManager basic finished Change-Id: Id463388a150628c0ee331fb07d5813d99e70f016 --- src/base/types.hh | 29 ++++++-- src/cpu/probes/PcCountTracker.py | 49 ++++++++++++++ src/cpu/probes/SConscript | 35 ++++++++++ src/cpu/probes/pc_count_tracker.cc | 62 +++++++++++++++++ src/cpu/probes/pc_count_tracker.hh | 58 ++++++++++++++++ src/cpu/probes/pc_count_tracker_manager.cc | 79 ++++++++++++++++++++++ src/cpu/probes/pc_count_tracker_manager.hh | 60 ++++++++++++++++ src/python/m5/params.py | 4 ++ src/python/pybind11/core.cc | 16 +++++ 9 files changed, 387 insertions(+), 5 deletions(-) create mode 100644 src/cpu/probes/PcCountTracker.py create mode 100644 src/cpu/probes/SConscript create mode 100644 src/cpu/probes/pc_count_tracker.cc create mode 100644 src/cpu/probes/pc_count_tracker.hh create mode 100644 src/cpu/probes/pc_count_tracker_manager.cc create mode 100644 src/cpu/probes/pc_count_tracker_manager.hh diff --git a/src/base/types.hh b/src/base/types.hh index b38e1a4af7..ba9e98fad5 100644 --- a/src/base/types.hh +++ b/src/base/types.hh @@ -151,24 +151,43 @@ class PcCountPair /** Explicit constructor assigning a value. */ explicit constexpr PcCountPair(uint64_t _pc, int _count) : - pc(_pc), count(_count) { } + pc(_pc), count(_count) {} /** Default constructor for parameter classes. */ - PcCountPair() : pc(0), count(0) { } + PcCountPair() : pc(0), count(0) {} /** Prefix increment operator. */ PcCountPair& operator++() { ++count; return *this; } - uint64_t getPC () const {return pc;} - int getCount() const {return count;} + constexpr uint64_t getPC () const {return pc;} + constexpr int getCount() const {return count;} /** Greater than comparison used for > PcCountPair(0). */ constexpr bool operator>(const PcCountPair& cc) const { - return count > cc.count; + return count > cc.getCount(); } + constexpr bool + operator==(const PcCountPair& cc) const + { + if(pc == cc.getPC() && count == cc.getCount()) { + return true; + } + return false; + } + + struct HashFunction + { + size_t operator()(const PcCountPair& item) const + { + size_t xHash = std::hash()(item.pc); + size_t yHash = std::hash()(item.count) << 1; + return xHash ^ yHash; + } + }; + }; /** diff --git a/src/cpu/probes/PcCountTracker.py b/src/cpu/probes/PcCountTracker.py new file mode 100644 index 0000000000..026f27f2e4 --- /dev/null +++ b/src/cpu/probes/PcCountTracker.py @@ -0,0 +1,49 @@ +# Copyright (c) 2022 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from m5.params import * +from m5.objects.Probe import ProbeListenerObject + +from m5.objects import SimObject + +class PcCountTrackerManager(SimObject): + + type = "PcCountTrackerManager" + cxx_header = "cpu/probes/pc_count_tracker_manager.hh" + cxx_class = "gem5::PcCountTrackerManager" + + targets = VectorParam.PcCountPair("the target PC Count pairs") + +class PcCountTracker(ProbeListenerObject): + + type = "PcCountTracker" + cxx_header = "cpu/probes/pc_count_tracker.hh" + cxx_class = "gem5::PcCountTracker" + + targets = VectorParam.PcCountPair("the target PC Count pairs") + core = Param.BaseCPU("the connected cpu") + ptmanager = Param.PcCountTrackerManager("the PcCountTracker manager") + \ No newline at end of file diff --git a/src/cpu/probes/SConscript b/src/cpu/probes/SConscript new file mode 100644 index 0000000000..48c83aaa49 --- /dev/null +++ b/src/cpu/probes/SConscript @@ -0,0 +1,35 @@ +# Copyright (c) 2022 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Import('*') + +if not env['CONF']['USE_NULL_ISA']: + SimObject('PcCountTracker.py', + sim_objects=['PcCountTracker','PcCountTrackerManager']) + Source('pc_count_tracker.cc') + Source('pc_count_tracker_manager.cc') + + DebugFlag('PcCountTracker') diff --git a/src/cpu/probes/pc_count_tracker.cc b/src/cpu/probes/pc_count_tracker.cc new file mode 100644 index 0000000000..fe4ec4d0c5 --- /dev/null +++ b/src/cpu/probes/pc_count_tracker.cc @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cpu/probes/pc_count_tracker.hh" + + +namespace gem5 +{ + PcCountTracker::PcCountTracker(const PcCountTrackerParams &p) + : ProbeListenerObject(p), + cpuptr(p.core), + manager(p.ptmanager) + { + if (!cpuptr) { + fatal("%s is NULL", !cpuptr ? "CPU":"PcCountTrackerManager"); + } + for (int i = 0; i < p.targets.size(); i++) { + targetPC.insert(p.targets[i].getPC()); + } + } + + void + PcCountTracker::regProbeListeners() { + + typedef ProbeListenerArg PcCountTrackerListener; + listeners.push_back(new PcCountTrackerListener(this, "RetiredInstsPC", + &PcCountTracker::check_pc)); + } + + void + PcCountTracker::check_pc(const Addr& pc) { + if(targetPC.find(pc) != targetPC.end()) { + manager->check_count(pc); + } + } + +} \ No newline at end of file diff --git a/src/cpu/probes/pc_count_tracker.hh b/src/cpu/probes/pc_count_tracker.hh new file mode 100644 index 0000000000..b6eb248e82 --- /dev/null +++ b/src/cpu/probes/pc_count_tracker.hh @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __CPU_PROBES_PC_COUNT_TRACKER_HH__ +#define __CPU_PROBES_PC_COUNT_TRACKER_HH__ + +#include + +#include "cpu/base.hh" +#include "cpu/probes/pc_count_tracker_manager.hh" +#include "params/PcCountTracker.hh" +#include "sim/probe/probe.hh" + +namespace gem5 +{ + class PcCountTracker : public ProbeListenerObject { + public: + PcCountTracker(const PcCountTrackerParams ¶ms); + + virtual void regProbeListeners(); + + void check_pc(const Addr& pc); + + private: + std::unordered_set targetPC; + + BaseCPU *cpuptr; + + PcCountTrackerManager *manager; + }; +} + +#endif // __CPU_PROBES_PC_COUNT_TRACKER_HH__ \ No newline at end of file diff --git a/src/cpu/probes/pc_count_tracker_manager.cc b/src/cpu/probes/pc_count_tracker_manager.cc new file mode 100644 index 0000000000..76bcaa5db5 --- /dev/null +++ b/src/cpu/probes/pc_count_tracker_manager.cc @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cpu/probes/pc_count_tracker_manager.hh" + +namespace gem5 +{ + PcCountTrackerManager::PcCountTrackerManager( + const PcCountTrackerManagerParams &p) + : SimObject(p) + { + ifListNotEmpty = true; + + lastTick = 0; + + for (int i = 0 ; i < p.targets.size() ; i++) { + counter.insert(std::make_pair(p.targets[i].getPC(),0)); + targetPair.insert(p.targets[i]); + } + } + + void + PcCountTrackerManager::check_count(Addr pc) { + + int count = ++counter.find(pc)->second; + + if(ifListNotEmpty) { + + if(targetPair.empty()) { + if(curTick() > lastTick) { + DPRINTF(PcCountTracker, + "all targets are encountered. Last Tick:%i\n", lastTick); + exitSimLoop("reached the end of the looppoint list"); + ifListNotEmpty = false; + } + } + + PcCountPair temp = PcCountPair(pc,count); + if(targetPair.find(temp) != targetPair.end()) { + + DPRINTF(PcCountTracker, + "pc:%lu count:%i encountered\n", + temp.getPC(), temp.getCount()); + + lastTick = curTick(); + exitSimLoopNow("simpoint starting point found"); + + targetPair.erase(PcCountPair(pc,count)); + } + } + } + + +} \ No newline at end of file diff --git a/src/cpu/probes/pc_count_tracker_manager.hh b/src/cpu/probes/pc_count_tracker_manager.hh new file mode 100644 index 0000000000..15a3613cdc --- /dev/null +++ b/src/cpu/probes/pc_count_tracker_manager.hh @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __CPU_PROBES_PC_COUNT_TRACKER_MANAGER_HH__ +#define __CPU_PROBES_PC_COUNT_TRACKER_MANAGER_HH__ + +#include +#include + +#include "cpu/base.hh" +#include "params/PcCountTrackerManager.hh" +#include "sim/sim_exit.hh" +#include "debug/PcCountTracker.hh" + +namespace gem5 +{ + + + class PcCountTrackerManager : public SimObject { + public: + PcCountTrackerManager(const PcCountTrackerManagerParams ¶ms); + + void check_count(Addr pc); + + private: + std::unordered_map counter; + std::unordered_set targetPair; + + Tick lastTick; + bool ifListNotEmpty; + }; + +} + +#endif // __CPU_PROBES_PC_COUNT_TRACKER_MANAGER_HH__ \ No newline at end of file diff --git a/src/python/m5/params.py b/src/python/m5/params.py index 96b2ddf5cc..ea4cf839b5 100644 --- a/src/python/m5/params.py +++ b/src/python/m5/params.py @@ -866,6 +866,10 @@ def getPC(self): def getCount(self): return int(self.count) + + def getValue(self): + from _m5.pc import PcCountPair + return PcCountPair(self.pc, self.count) class AddrRange(ParamValue): diff --git a/src/python/pybind11/core.cc b/src/python/pybind11/core.cc index 89466750d0..68cd921a40 100644 --- a/src/python/pybind11/core.cc +++ b/src/python/pybind11/core.cc @@ -163,6 +163,21 @@ init_range(py::module_ &m_native) m.def("RangeSize", &RangeSize); } +static void +init_pc(py::module_ &m_native) +{ + py::module_ m = m_native.def_submodule("pc"); + + py::class_(m, "PcCountPair") + .def(py::init<>()) + .def(py::init()) + + .def("getPC", &PcCountPair::getPC) + .def("getCount", &PcCountPair::getCount) + ; +} + + static void init_net(py::module_ &m_native) { @@ -307,6 +322,7 @@ pybind_init_core(py::module_ &m_native) init_range(m_native); init_net(m_native); init_loader(m_native); + init_pc(m_native); } } // namespace gem5 From 1aea6e6a478ed6d51ca3fef43b85afd46f8d9745 Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Mon, 12 Dec 2022 18:37:49 -0800 Subject: [PATCH 03/20] added get_pc_count and get_current_pc_count_pair function Change-Id: I3f43350c7c68ea57cd673a62a6e1c4c0aaa420dd --- src/cpu/probes/PcCountTracker.py | 7 ++++++- src/cpu/probes/pc_count_tracker_manager.cc | 15 ++++++++------- src/cpu/probes/pc_count_tracker_manager.hh | 13 ++++++++++++- src/python/gem5/simulate/exit_event.py | 3 +++ 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/cpu/probes/PcCountTracker.py b/src/cpu/probes/PcCountTracker.py index 026f27f2e4..e27ac9506e 100644 --- a/src/cpu/probes/PcCountTracker.py +++ b/src/cpu/probes/PcCountTracker.py @@ -25,8 +25,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.params import * +from m5.util.pybind import * from m5.objects.Probe import ProbeListenerObject - from m5.objects import SimObject class PcCountTrackerManager(SimObject): @@ -34,6 +34,11 @@ class PcCountTrackerManager(SimObject): type = "PcCountTrackerManager" cxx_header = "cpu/probes/pc_count_tracker_manager.hh" cxx_class = "gem5::PcCountTrackerManager" + + cxx_exports = [ + PyBindMethod("get_pc_count"), + PyBindMethod("get_current_pc_count_pair") + ] targets = VectorParam.PcCountPair("the target PC Count pairs") diff --git a/src/cpu/probes/pc_count_tracker_manager.cc b/src/cpu/probes/pc_count_tracker_manager.cc index 76bcaa5db5..24435c9f90 100644 --- a/src/cpu/probes/pc_count_tracker_manager.cc +++ b/src/cpu/probes/pc_count_tracker_manager.cc @@ -34,8 +34,8 @@ namespace gem5 const PcCountTrackerManagerParams &p) : SimObject(p) { + currentPair = PcCountPair(0,0); ifListNotEmpty = true; - lastTick = 0; for (int i = 0 ; i < p.targets.size() ; i++) { @@ -55,25 +55,26 @@ namespace gem5 if(curTick() > lastTick) { DPRINTF(PcCountTracker, "all targets are encountered. Last Tick:%i\n", lastTick); - exitSimLoop("reached the end of the looppoint list"); + exitSimLoop("reached the end of the PcCountPair list"); ifListNotEmpty = false; } } - PcCountPair temp = PcCountPair(pc,count); - if(targetPair.find(temp) != targetPair.end()) { + currentPair = PcCountPair(pc,count); + if(targetPair.find(currentPair) != targetPair.end()) { DPRINTF(PcCountTracker, "pc:%lu count:%i encountered\n", - temp.getPC(), temp.getCount()); + currentPair.getPC(), currentPair.getCount()); lastTick = curTick(); exitSimLoopNow("simpoint starting point found"); - targetPair.erase(PcCountPair(pc,count)); + targetPair.erase(currentPair); + DPRINTF(PcCountTracker, + "There are %i targets remained", targetPair.size()); } } } - } \ No newline at end of file diff --git a/src/cpu/probes/pc_count_tracker_manager.hh b/src/cpu/probes/pc_count_tracker_manager.hh index 15a3613cdc..568c6f308b 100644 --- a/src/cpu/probes/pc_count_tracker_manager.hh +++ b/src/cpu/probes/pc_count_tracker_manager.hh @@ -44,15 +44,26 @@ namespace gem5 class PcCountTrackerManager : public SimObject { public: PcCountTrackerManager(const PcCountTrackerManagerParams ¶ms); - void check_count(Addr pc); private: std::unordered_map counter; std::unordered_set targetPair; + PcCountPair currentPair; Tick lastTick; bool ifListNotEmpty; + + public: + int get_pc_count(Addr pc) const { + if(counter.find(pc) != counter.end()) { + return counter.find(pc)->second; + } + return -1; + } + PcCountPair get_current_pc_count_pair() const { + return currentPair; + } }; } diff --git a/src/python/gem5/simulate/exit_event.py b/src/python/gem5/simulate/exit_event.py index 1e14fdd11a..7285427cf8 100644 --- a/src/python/gem5/simulate/exit_event.py +++ b/src/python/gem5/simulate/exit_event.py @@ -49,6 +49,7 @@ class ExitEvent(Enum): ) SIMPOINT_BEGIN = "simpoint begins" MAX_INSTS = "number of instructions reached" + PCCOUNTTRACK_END = "pc count pair tracking ends" @classmethod def translate_exit_status(cls, exit_string: str) -> "ExitEvent": @@ -96,6 +97,8 @@ def translate_exit_status(cls, exit_string: str) -> "ExitEvent": elif exit_string.endswith("is finished updating the memory.\n"): # This is for the gups generator exit event return ExitEvent.EXIT + elif exit_string == "reached the end of the PcCountPair list": + return ExitEvent.PCCOUNTTRACK_END raise NotImplementedError( "Exit event '{}' not implemented".format(exit_string) ) From 5ed5db1f9efe644cfe27903d4b80df4dbe7b1bc1 Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Mon, 12 Dec 2022 21:29:03 -0800 Subject: [PATCH 04/20] added stdlib looppoint class and output json file Change-Id: I06dc3a04795eafeba586a5ba9da21a7f254eafe0 --- src/python/SConscript | 1 + .../components/processors/abstract_core.py | 12 +- .../components/processors/base_cpu_core.py | 22 ++- src/python/gem5/utils/looppoint.py | 150 ++++++++++++++++++ 4 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 src/python/gem5/utils/looppoint.py diff --git a/src/python/SConscript b/src/python/SConscript index aeeb8925a3..68b5e1d926 100644 --- a/src/python/SConscript +++ b/src/python/SConscript @@ -240,6 +240,7 @@ PySource('gem5.components.processors', PySource('gem5.components.processors', 'gem5/components/processors/switchable_processor.py') PySource('gem5.utils', 'gem5/utils/simpoint.py') +PySource('gem5.utils', 'gem5/utils/looppoint.py') PySource('gem5.components.processors', 'gem5/components/processors/traffic_generator_core.py') PySource('gem5.components.processors', diff --git a/src/python/gem5/components/processors/abstract_core.py b/src/python/gem5/components/processors/abstract_core.py index 58296bca3b..8c9ffdc33a 100644 --- a/src/python/gem5/components/processors/abstract_core.py +++ b/src/python/gem5/components/processors/abstract_core.py @@ -29,7 +29,8 @@ from ...isas import ISA -from m5.objects import BaseMMU, Port, SubSystem +from m5.objects import BaseMMU, Port, SubSystem, PcCountTrackerManager +from m5.params import PcCountPair class AbstractCore(SubSystem): @@ -155,3 +156,12 @@ def _set_inst_stop_any_thread( instruction stop is setup differently dependent on this. """ raise NotImplementedError("This core type does not support MAX_INSTS") + + @abstractmethod + def add_pc_tracker_probe( + self, + targetPair: List[PcCountPair], + manager: PcCountTrackerManager + ) -> None: + raise NotImplementedError + \ No newline at end of file diff --git a/src/python/gem5/components/processors/base_cpu_core.py b/src/python/gem5/components/processors/base_cpu_core.py index 631fd0ad0e..b0f4eec4a9 100644 --- a/src/python/gem5/components/processors/base_cpu_core.py +++ b/src/python/gem5/components/processors/base_cpu_core.py @@ -33,7 +33,15 @@ from ...utils.override import overrides from ...utils.requires import requires -from m5.objects import BaseMMU, Port, BaseCPU, Process +from m5.objects import ( + BaseMMU, + Port, + BaseCPU, + Process, + PcCountTracker, + PcCountTrackerManager, +) +from m5.params import PcCountPair class BaseCPUCore(AbstractCore): @@ -169,3 +177,15 @@ def _set_inst_stop_any_thread( self.core.scheduleInstStopAnyThread(inst) else: self.core.max_insts_any_thread = inst + + @overrides(AbstractCore) + def add_pc_tracker_probe( + self, + target_pair: List[PcCountPair], + manager: PcCountTrackerManager + ) -> None: + pair_tracker = PcCountTracker() + pair_tracker.targets = target_pair + pair_tracker.core = self.core + pair_tracker.ptmanager = manager + self.core.probeListener = pair_tracker \ No newline at end of file diff --git a/src/python/gem5/utils/looppoint.py b/src/python/gem5/utils/looppoint.py new file mode 100644 index 0000000000..9e549956bd --- /dev/null +++ b/src/python/gem5/utils/looppoint.py @@ -0,0 +1,150 @@ +# Copyright (c) 2022 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +from m5.util import fatal +from m5.params import PcCountPair +from pathlib import Path +from typing import List, Dict +from gem5.components.processors.abstract_processor import AbstractProcessor +from m5.objects import PcCountTrackerManager +import csv +import re +import json + +class LoopPoint: + + def __init__( + self, + targets: List[PcCountPair], + regions: Dict[PcCountPair,int], + json_file: Dict[int, Dict] + ) -> None: + + self._manager = PcCountTrackerManager() + self._manager.targets = targets + self._targets = targets + self._regions = regions + self._json_file = json_file + + def setup_processor( + self, + processor: AbstractProcessor, + ) -> None: + for core in processor.get_cores(): + core.add_pc_tracker_probe(self._targets, self._manager) + + def update_relatives(self) -> None: + current_pair = self._manager.get_current_pc_count_pair() + if(current_pair in self._regions): + rid = self._regions[current_pair] + region = self._json_file[rid] + if("warmup" in region): + region = region["simulation"] + start = region["start"]["global"] + temp = start - self._manager.get_pc_count(start.getPC()) + region["start"]["relative"] = int(temp) + end = region["end"]["global"] + temp = end - self._manager.get_pc_count(end.getPC()) + region["end"]["relative"] = int(temp) + + + def output_json_file( + self, + input_indent: int = 4 + ) -> Dict[int, Dict]: + with open("LoopPoint.json", "w") as file: + json.dump(self._json_file, file, indent=input_indent) + + +class LoopPointCheckpoint(LoopPoint): + def __init__( + self, + LoopPointFilePath: Path, + if_csv: bool = True + ) -> None: + + _json_file = {} + _targets = [] + _region_id = {} + + if(if_csv): + self.profile_csv(LoopPointFilePath, _targets, _json_file, _region_id) + + super().__init__( + _targets, + _region_id, + _json_file, + ) + + def profile_csv( + self, + looppoint_file_path: Path, + targets: List[PcCountPair], + json_file: Dict[int, Dict], + region_id: Dict[PcCountPair, int], + ) -> None: + + with open(looppoint_file_path, newline="") as csvfile: + spamreader = csv.reader(csvfile, delimiter=" ", quotechar="|") + for row in spamreader: + if len(row) > 1: + if row[0] == "cluster": + line = row[4].split(",") + start = PcCountPair(int(line[3],16),int(line[6])) + end = PcCountPair(int(line[7],16),int(line[10])) + if(int(line[2]) in json_file): + json_file[int(line[2])]["simulation"]={"start": {"pc": int(line[3],16)}} + else: + json_file[int(line[2])] = {"simulation": {"start": {"pc": int(line[3],16)}}} + json_file[int(line[2])]["simulation"]["start"]["global"] = int(line[6]) + json_file[int(line[2])]["simulation"]["end"] = {"pc" : int(line[7],16)} + json_file[int(line[2])]["simulation"]["end"]["global"] = int(line[10]) + targets.append(start) + targets.append(end) + elif row[0] == "Warmup": + line = row[3].split(",") + start = PcCountPair(int(line[3],16),int(line[6])) + end = PcCountPair(int(line[7],16),int(line[10])) + if(int(line[0]) in json_file): + json_file[int(line[0])]["warmup"] = {"start": {"pc": int(line[3],16)}} + else: + json_file[int(line[0])] = {"warmup": {"start": {"pc": int(line[3],16)}}} + json_file[int(line[0])]["warmup"]["start"]["count"] = int(line[6]) + json_file[int(line[0])]["warmup"]["end"] = {"pc": int(line[7],16)} + json_file[int(line[0])]["warmup"]["end"]["count"] = int(line[10]) + targets.append(start) + targets.append(end) + + for rid, region in json_file.items(): + if("warmup" in region): + start = PcCountPair(region["warmup"]["start"]["pc"], + region["warmup"]["start"]["count"]) + else: + start = PcCountPair(region["simulation"]["start"]["pc"], + region["simulation"]["start"]["global"]) + region_id[start] = rid + \ No newline at end of file From 7d0999418f8720fde0d96855dff80c05e550d00d Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Mon, 12 Dec 2022 23:58:53 -0800 Subject: [PATCH 05/20] fixed hash problem for python PcCountPair Change-Id: I273d531cc9aee337c0092bcc49cbfea4a129da65 --- src/python/gem5/utils/looppoint.py | 36 +++++++++++++++++++++--------- src/python/m5/params.py | 7 ++++++ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/python/gem5/utils/looppoint.py b/src/python/gem5/utils/looppoint.py index 9e549956bd..f0ecb0a07d 100644 --- a/src/python/gem5/utils/looppoint.py +++ b/src/python/gem5/utils/looppoint.py @@ -59,17 +59,17 @@ def setup_processor( def update_relatives(self) -> None: current_pair = self._manager.get_current_pc_count_pair() - if(current_pair in self._regions): - rid = self._regions[current_pair] - region = self._json_file[rid] + temp_pair = PcCountPair(current_pair.getPC(), current_pair.getCount()) + if(temp_pair in self._regions): + rid = self._regions[temp_pair] if("warmup" in region): - region = region["simulation"] - start = region["start"]["global"] - temp = start - self._manager.get_pc_count(start.getPC()) - region["start"]["relative"] = int(temp) - end = region["end"]["global"] - temp = end - self._manager.get_pc_count(end.getPC()) - region["end"]["relative"] = int(temp) + region = self._json_file[rid]["simulation"] + start = region["start"]["pc"] + temp = region["start"]["global"] - self._manager.get_pc_count(start) + self._json_file[rid]["simulation"]["start"]["relative"] = int(temp) + end = region["end"]["pc"] + temp = region["end"]["global"] - self._manager.get_pc_count(end) + self._json_file[rid]["simulation"]["end"]["relative"] = int(temp) def output_json_file( @@ -78,6 +78,22 @@ def output_json_file( ) -> Dict[int, Dict]: with open("LoopPoint.json", "w") as file: json.dump(self._json_file, file, indent=input_indent) + + def get_current_region(self) -> int: + current_pair = self._manager.get_current_pc_count_pair() + temp_pair = PcCountPair(current_pair.getPC(), current_pair.getCount()) + if(temp_pair in self._regions): + return self._regions[temp_pair] + return -1 + + def get_current_pair(self) -> PcCountPair: + return self._manager.get_current_pc_count_pair() + + def get_regions(self) -> Dict[PcCountPair,int]: + return self._regions + + def get_targets(self) -> List[PcCountPair]: + return self._targets class LoopPointCheckpoint(LoopPoint): diff --git a/src/python/m5/params.py b/src/python/m5/params.py index ea4cf839b5..b4c72ddfec 100644 --- a/src/python/m5/params.py +++ b/src/python/m5/params.py @@ -870,6 +870,13 @@ def getCount(self): def getValue(self): from _m5.pc import PcCountPair return PcCountPair(self.pc, self.count) + + def __eq__(self, other): + return (self.pc == other.pc and self.count == other.count) + + def __hash__(self): + return hash((int(self.pc),int(self.count))) + class AddrRange(ParamValue): From eabc1a57a5e06e389536b5d5fdb48d0025381e5f Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Tue, 13 Dec 2022 00:10:47 -0800 Subject: [PATCH 06/20] fixed update_relatives() function Change-Id: Ic0b22083d8ceb7e70e16a087ac23f1df8be5c4f0 --- src/python/gem5/utils/looppoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/gem5/utils/looppoint.py b/src/python/gem5/utils/looppoint.py index f0ecb0a07d..5fdd467264 100644 --- a/src/python/gem5/utils/looppoint.py +++ b/src/python/gem5/utils/looppoint.py @@ -62,7 +62,7 @@ def update_relatives(self) -> None: temp_pair = PcCountPair(current_pair.getPC(), current_pair.getCount()) if(temp_pair in self._regions): rid = self._regions[temp_pair] - if("warmup" in region): + if("warmup" in self._json_file[rid]): region = self._json_file[rid]["simulation"] start = region["start"]["pc"] temp = region["start"]["global"] - self._manager.get_pc_count(start) From 0b735e1801af55c90a5d40b8542cf0abe484e6fe Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Tue, 13 Dec 2022 08:20:55 -0800 Subject: [PATCH 07/20] take relative count for end always Change-Id: I08e7d14d88c6d4ff8a84158acc15dbb7d5d43e92 --- src/python/gem5/utils/looppoint.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/python/gem5/utils/looppoint.py b/src/python/gem5/utils/looppoint.py index 5fdd467264..f90da4ffdb 100644 --- a/src/python/gem5/utils/looppoint.py +++ b/src/python/gem5/utils/looppoint.py @@ -62,14 +62,14 @@ def update_relatives(self) -> None: temp_pair = PcCountPair(current_pair.getPC(), current_pair.getCount()) if(temp_pair in self._regions): rid = self._regions[temp_pair] + region = self._json_file[rid]["simulation"] if("warmup" in self._json_file[rid]): - region = self._json_file[rid]["simulation"] start = region["start"]["pc"] temp = region["start"]["global"] - self._manager.get_pc_count(start) self._json_file[rid]["simulation"]["start"]["relative"] = int(temp) - end = region["end"]["pc"] - temp = region["end"]["global"] - self._manager.get_pc_count(end) - self._json_file[rid]["simulation"]["end"]["relative"] = int(temp) + end = region["end"]["pc"] + temp = region["end"]["global"] - self._manager.get_pc_count(end) + self._json_file[rid]["simulation"]["end"]["relative"] = int(temp) def output_json_file( From 52691abcd8c5662fce73cc248760ff0972233d74 Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Tue, 13 Dec 2022 09:14:39 -0800 Subject: [PATCH 08/20] added LoopPointRestore and json profile option for LoopPointCheckpiont Change-Id: I46a0f867dbab82d7c242b9752da637de29cdc14b --- src/python/gem5/utils/looppoint.py | 90 +++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/src/python/gem5/utils/looppoint.py b/src/python/gem5/utils/looppoint.py index f90da4ffdb..129dcbc917 100644 --- a/src/python/gem5/utils/looppoint.py +++ b/src/python/gem5/utils/looppoint.py @@ -71,7 +71,6 @@ def update_relatives(self) -> None: temp = region["end"]["global"] - self._manager.get_pc_count(end) self._json_file[rid]["simulation"]["end"]["relative"] = int(temp) - def output_json_file( self, input_indent: int = 4 @@ -108,7 +107,19 @@ def __init__( _region_id = {} if(if_csv): - self.profile_csv(LoopPointFilePath, _targets, _json_file, _region_id) + self.profile_csv( + LoopPointFilePath, + _targets, + _json_file, + _region_id + ) + else: + self.profile_json( + LoopPointFilePath, + _targets, + _json_file, + _region_id + ) super().__init__( _targets, @@ -163,4 +174,79 @@ def profile_csv( start = PcCountPair(region["simulation"]["start"]["pc"], region["simulation"]["start"]["global"]) region_id[start] = rid + + def profile_json( + self, + looppoint_file_path: Path, + targets: List[PcCountPair], + json_file: Dict[int, Dict], + region_id: Dict[PcCountPair, int], + ) -> None: + with open(looppoint_file_path) as file: + json_file = json.load(file) + for rid, region in json_file.items(): + sim_start = PcCountPair(region["simulation"]["start"]["pc"], + region["simulation"]["start"]["global"]) + targets.append(sim_start) + end = PcCountPair(region["simulation"]["end"]["pc"], + region["simulation"]["end"]["global"]) + targets.append(end) + if("warmup" in region): + start = PcCountPair(region["warmup"]["start"]["pc"], + region["warmup"]["start"]["count"]) + targets.append(start) + end = PcCountPair(region["warmup"]["end"]["pc"], + region["warmup"]["end"]["count"]) + targets.append(end) + else: + start = sim_start + + region_id[start] = rid + + + +class LoopPointRestore(LoopPoint): + def __init__( + self, LoopPointOutputFilePath: Path, CheckPointDir: Path + ) -> None: + + _json_file = {} + _targets = [] + _region_id = {} + + self.profile_restore(LoopPointFilePath, _targets, _json_file, _region_id) + + super().__init__( + _targets, + _region_id, + _json_file, + ) + + def profile_restore( + self, + looppoint_file_path: Path, + targets: List[PcCountPair], + json_file: Dict[int, Dict], + region_id: Dict[PcCountPair, int], + ) -> None: + regex = re.compile(r"cpt.([0-9]+)") + rid = regex.findall(checkpoint_dir.as_posix())[0] + with open(looppoint_file_path) as file: + json_file = json.load(file) + if(rid not in json_file): + fatal(f"{rid} is not a valid region\n") + region = json_file[rid] + if("warmup" in region): + if("relative" not in region["simulation"]["start"]): + fatal(f"region {rid} doesn't have relative count info\n") + start = PcCountPair(region["simulation"]["start"]["pc"], + region["simulation"]["start"]["relative"]) + region_id[start]=rid + targets.append(start) + if("relative" not in region["simulation"]["end"]): + fatal(f"region {rid} doesn't have relative count info\n") + end = PcCountPair(region["simulation"]["start"]["pc"], + region["simulation"]["end"]["relative"]) + region_id[end]=rid + targets.append(end) \ No newline at end of file From 0eb47208d1997a6991fb2fc50c4139325a1365f4 Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Tue, 13 Dec 2022 14:59:27 -0800 Subject: [PATCH 09/20] fixed small things Change-Id: I47730b77ef0f9165efb5ff24dab725929e73f7a1 --- src/base/types.hh | 13 ++++-- src/cpu/probes/pc_count_tracker_manager.cc | 6 +-- .../components/boards/se_binary_workload.py | 45 +++++++++++++++++++ .../gem5/simulate/exit_event_generators.py | 15 +++++++ src/python/gem5/utils/looppoint.py | 16 ++++--- src/python/m5/params.py | 9 ++-- src/python/pybind11/core.cc | 6 +++ 7 files changed, 94 insertions(+), 16 deletions(-) diff --git a/src/base/types.hh b/src/base/types.hh index ba9e98fad5..2d23319010 100644 --- a/src/base/types.hh +++ b/src/base/types.hh @@ -172,10 +172,15 @@ class PcCountPair constexpr bool operator==(const PcCountPair& cc) const { - if(pc == cc.getPC() && count == cc.getCount()) { - return true; - } - return false; + return (pc == cc.getPC() && count == cc.getCount()); + } + + std::string + to_string() const + { + std::string s = "(" + std::to_string(pc) + + ", " + std::to_string(count) + ")"; + return s; } struct HashFunction diff --git a/src/cpu/probes/pc_count_tracker_manager.cc b/src/cpu/probes/pc_count_tracker_manager.cc index 24435c9f90..46a6407f2c 100644 --- a/src/cpu/probes/pc_count_tracker_manager.cc +++ b/src/cpu/probes/pc_count_tracker_manager.cc @@ -64,15 +64,15 @@ namespace gem5 if(targetPair.find(currentPair) != targetPair.end()) { DPRINTF(PcCountTracker, - "pc:%lu count:%i encountered\n", - currentPair.getPC(), currentPair.getCount()); + "pc:%s encountered\n", + currentPair.to_string()); lastTick = curTick(); exitSimLoopNow("simpoint starting point found"); targetPair.erase(currentPair); DPRINTF(PcCountTracker, - "There are %i targets remained", targetPair.size()); + "There are %i targets remained\n", targetPair.size()); } } } diff --git a/src/python/gem5/components/boards/se_binary_workload.py b/src/python/gem5/components/boards/se_binary_workload.py index 8ec112ee13..356e7e7625 100644 --- a/src/python/gem5/components/boards/se_binary_workload.py +++ b/src/python/gem5/components/boards/se_binary_workload.py @@ -27,6 +27,7 @@ from .abstract_board import AbstractBoard from ...resources.resource import AbstractResource from gem5.utils.simpoint import SimPoint +from gem5.utils.looppoint import LoopPoint from m5.objects import SEWorkload, Process @@ -169,3 +170,47 @@ def get_simpoint(self) -> SimPoint: if getattr(self, "_simpoint_object", None): return self._simpoint_object raise Exception("This board does not have a simpoint set.") + + def set_se_looppoint_workload( + self, + binary: AbstractResource, + arguments: List[str] = [], + looppoint: Union[AbstractResource, LoopPoint] = None, + checkpoint: Optional[Union[Path, AbstractResource]] = None, + ) -> None: + """Set up the system to run a LoopPoint workload. + + **Limitations** + * Dynamically linked executables are partially supported when the host + ISA and the simulated ISA are the same. + + :param binary: The resource encapsulating the binary to be run. + :param arguments: The input arguments for the binary + :param looppoint: The LoopPoint object that contain all the information + gather from the LoopPoint files and a LoopPointManager that will raise + exit events for LoopPoints + """ + + if isinstance(looppoint, AbstractResource): + self._looppoint_object = LoopPoint(looppoint) + else: + assert isinstance(looppoint, LoopPoint) + self._looppoint_object = looppoint + + self._looppoint_object.setup_processor(self.get_processor()) + + # Call set_se_binary_workload after LoopPoint setup is complete + self.set_se_binary_workload( + binary=binary, + arguments=arguments, + checkpoint=checkpoint, + ) + + def get_looppoint(self) -> LoopPoint: + """ + Returns the LoopPoint object set. If no LoopPoint object has been set + an exception is thrown. + """ + if getattr(self, "_looppoint_object", None): + return self._looppoint_object + raise Exception("This board does not have a looppoint set.") \ No newline at end of file diff --git a/src/python/gem5/simulate/exit_event_generators.py b/src/python/gem5/simulate/exit_event_generators.py index d6732bb49d..9885cb3cee 100644 --- a/src/python/gem5/simulate/exit_event_generators.py +++ b/src/python/gem5/simulate/exit_event_generators.py @@ -29,6 +29,7 @@ from ..components.processors.abstract_processor import AbstractProcessor from ..components.processors.switchable_processor import SwitchableProcessor from ..utils.simpoint import SimPoint +from gem5.utils.looppoint import LoopPoint from m5.util import warn from pathlib import Path @@ -167,3 +168,17 @@ def simpoints_save_checkpoint_generator( yield False else: yield True + +def looppoint_save_checkpoint_generator( + checkpoint_dir: Path, looppoint: LoopPoint, update_relatives: bool = True +): + while True: + region = looppoint.get_current_region() + if(region != -1): + if(update_relatives): + looppoint.update_relatives() + m5.checkpoint((checkpoint_dir / f"cpt.Region{region}").as_posix()) + yield False + + + diff --git a/src/python/gem5/utils/looppoint.py b/src/python/gem5/utils/looppoint.py index 129dcbc917..bfb103230c 100644 --- a/src/python/gem5/utils/looppoint.py +++ b/src/python/gem5/utils/looppoint.py @@ -150,6 +150,7 @@ def profile_csv( json_file[int(line[2])]["simulation"]["start"]["global"] = int(line[6]) json_file[int(line[2])]["simulation"]["end"] = {"pc" : int(line[7],16)} json_file[int(line[2])]["simulation"]["end"]["global"] = int(line[10]) + json_file[int(line[2])]["multiplier"] = float(line[14]) targets.append(start) targets.append(end) elif row[0] == "Warmup": @@ -202,19 +203,23 @@ def profile_json( start = sim_start region_id[start] = rid - - class LoopPointRestore(LoopPoint): def __init__( - self, LoopPointOutputFilePath: Path, CheckPointDir: Path + self, LoopPointFilePath: Path, CheckPointDir: Path ) -> None: _json_file = {} _targets = [] _region_id = {} - self.profile_restore(LoopPointFilePath, _targets, _json_file, _region_id) + self.profile_restore( + LoopPointFilePath, + CheckPointDir, + _targets, + _json_file, + _region_id + ) super().__init__( _targets, @@ -225,11 +230,12 @@ def __init__( def profile_restore( self, looppoint_file_path: Path, + checkpoint_dir: Path, targets: List[PcCountPair], json_file: Dict[int, Dict], region_id: Dict[PcCountPair, int], ) -> None: - regex = re.compile(r"cpt.([0-9]+)") + regex = re.compile(r"cpt.Region([0-9]+)") rid = regex.findall(checkpoint_dir.as_posix())[0] with open(looppoint_file_path) as file: json_file = json.load(file) diff --git a/src/python/m5/params.py b/src/python/m5/params.py index b4c72ddfec..93909031c8 100644 --- a/src/python/m5/params.py +++ b/src/python/m5/params.py @@ -862,22 +862,23 @@ def __init__(self, _pc, _count): self.count = _count def getPC(self): - return Addr(self.pc) + return self.pc def getCount(self): - return int(self.count) + return self.count def getValue(self): from _m5.pc import PcCountPair return PcCountPair(self.pc, self.count) + def __str__(self): + return "(%i,%i)" % (self.pc, self.count) + def __eq__(self, other): return (self.pc == other.pc and self.count == other.count) def __hash__(self): return hash((int(self.pc),int(self.count))) - - class AddrRange(ParamValue): cxx_type = "AddrRange" diff --git a/src/python/pybind11/core.cc b/src/python/pybind11/core.cc index 68cd921a40..c8686d3be7 100644 --- a/src/python/pybind11/core.cc +++ b/src/python/pybind11/core.cc @@ -171,6 +171,12 @@ init_pc(py::module_ &m_native) py::class_(m, "PcCountPair") .def(py::init<>()) .def(py::init()) + .def("__eq__", &PcCountPair::operator==) + .def("__str__", [](const PcCountPair &p) { + std::stringstream s; + s << "(" << p.getPC() << ", " << p.getCount() <<")"; + return s.str(); + }) .def("getPC", &PcCountPair::getPC) .def("getCount", &PcCountPair::getCount) From f7a15a420dbd79872a777fadabd3e9fd0896f783 Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Wed, 14 Dec 2022 10:48:07 -0800 Subject: [PATCH 10/20] use c++ to_string function for __str__ Change-Id: I1f35afc2b672ee5b2db40e392a724be8bb04e364 --- src/python/pybind11/core.cc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/python/pybind11/core.cc b/src/python/pybind11/core.cc index c8686d3be7..c3598bc3ae 100644 --- a/src/python/pybind11/core.cc +++ b/src/python/pybind11/core.cc @@ -167,16 +167,11 @@ static void init_pc(py::module_ &m_native) { py::module_ m = m_native.def_submodule("pc"); - py::class_(m, "PcCountPair") .def(py::init<>()) .def(py::init()) .def("__eq__", &PcCountPair::operator==) - .def("__str__", [](const PcCountPair &p) { - std::stringstream s; - s << "(" << p.getPC() << ", " << p.getCount() <<")"; - return s.str(); - }) + .def("__str__", &PcCountPair::to_string) .def("getPC", &PcCountPair::getPC) .def("getCount", &PcCountPair::getCount) From 0a9cf2de35bb7852740bc27d500e364e31bf4278 Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Wed, 14 Dec 2022 10:50:04 -0800 Subject: [PATCH 11/20] fixed the empty space Change-Id: I1c3c50149b502eec35c8e3e75f16bbd070860faa --- src/python/pybind11/core.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/python/pybind11/core.cc b/src/python/pybind11/core.cc index c3598bc3ae..dcbea99761 100644 --- a/src/python/pybind11/core.cc +++ b/src/python/pybind11/core.cc @@ -178,7 +178,6 @@ init_pc(py::module_ &m_native) ; } - static void init_net(py::module_ &m_native) { From 0f5b292d13f7b85443fda2bb1fda2fb9c5aebd2e Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Wed, 14 Dec 2022 15:01:30 -0800 Subject: [PATCH 12/20] added comments Change-Id: Ib9a40029c9e8fce39a0cd496c026b7012994a8cd --- src/base/types.hh | 17 ++- src/cpu/probes/pc_count_tracker.cc | 9 +- src/cpu/probes/pc_count_tracker.hh | 7 + src/cpu/probes/pc_count_tracker_manager.cc | 22 ++- src/cpu/probes/pc_count_tracker_manager.hh | 21 ++- .../gem5/simulate/exit_event_generators.py | 19 ++- src/python/gem5/utils/looppoint.py | 130 +++++++++++++++++- src/python/m5/params.py | 2 + src/python/pybind11/core.cc | 5 +- 9 files changed, 211 insertions(+), 21 deletions(-) diff --git a/src/base/types.hh b/src/base/types.hh index 2d23319010..fc6ab937ae 100644 --- a/src/base/types.hh +++ b/src/base/types.hh @@ -143,38 +143,40 @@ class PcCountPair private: - /** Member holding the actual value. */ + /** The Program Counter address*/ uint64_t pc; + /** The count of the Program Counter address*/ int count; public: - /** Explicit constructor assigning a value. */ + /** Explicit constructor assigning the pc and count values*/ explicit constexpr PcCountPair(uint64_t _pc, int _count) : pc(_pc), count(_count) {} - /** Default constructor for parameter classes. */ + /** Default constructor for parameter classes*/ PcCountPair() : pc(0), count(0) {} - /** Prefix increment operator. */ - PcCountPair& operator++() { ++count; return *this; } - + /** Returns the Program Counter address*/ constexpr uint64_t getPC () const {return pc;} + /** Returns the count of the Program*/ constexpr int getCount() const {return count;} - /** Greater than comparison used for > PcCountPair(0). */ + /** Greater than comparison*/ constexpr bool operator>(const PcCountPair& cc) const { return count > cc.getCount(); } + /** Equal comparison*/ constexpr bool operator==(const PcCountPair& cc) const { return (pc == cc.getPC() && count == cc.getCount()); } + /** String format*/ std::string to_string() const { @@ -183,6 +185,7 @@ class PcCountPair return s; } + /** Enable hashing for this parameter*/ struct HashFunction { size_t operator()(const PcCountPair& item) const diff --git a/src/cpu/probes/pc_count_tracker.cc b/src/cpu/probes/pc_count_tracker.cc index fe4ec4d0c5..3429d8fe9e 100644 --- a/src/cpu/probes/pc_count_tracker.cc +++ b/src/cpu/probes/pc_count_tracker.cc @@ -36,10 +36,11 @@ namespace gem5 cpuptr(p.core), manager(p.ptmanager) { - if (!cpuptr) { + if (!cpuptr || !manager) { fatal("%s is NULL", !cpuptr ? "CPU":"PcCountTrackerManager"); } for (int i = 0; i < p.targets.size(); i++) { + // initialize the set of targeting Program Counter addresses targetPC.insert(p.targets[i].getPC()); } } @@ -50,11 +51,17 @@ namespace gem5 typedef ProbeListenerArg PcCountTrackerListener; listeners.push_back(new PcCountTrackerListener(this, "RetiredInstsPC", &PcCountTracker::check_pc)); + // connect the probe listener with the probe "RetriedInstsPC" in the + // corresponding core. + // when "RetiredInstsPC" notifies the probe listener, then the function + // 'check_pc' is automatically called } void PcCountTracker::check_pc(const Addr& pc) { if(targetPC.find(pc) != targetPC.end()) { + // if the PC is one of the target PCs, then notify the + // PcCounterTrackerManager by calling its `check_count` function manager->check_count(pc); } } diff --git a/src/cpu/probes/pc_count_tracker.hh b/src/cpu/probes/pc_count_tracker.hh index b6eb248e82..84d680b541 100644 --- a/src/cpu/probes/pc_count_tracker.hh +++ b/src/cpu/probes/pc_count_tracker.hh @@ -43,15 +43,22 @@ namespace gem5 PcCountTracker(const PcCountTrackerParams ¶ms); virtual void regProbeListeners(); + // setup the probelistener void check_pc(const Addr& pc); + // this function is called when the probelistener receives signal + // from the probe private: std::unordered_set targetPC; + // a set of Program Counter addresses that should notify the + // PcCounterTrackerManager for BaseCPU *cpuptr; + // the core this PcCountTracker is tracking at PcCountTrackerManager *manager; + // the PcCounterTrackerManager }; } diff --git a/src/cpu/probes/pc_count_tracker_manager.cc b/src/cpu/probes/pc_count_tracker_manager.cc index 46a6407f2c..58535421cb 100644 --- a/src/cpu/probes/pc_count_tracker_manager.cc +++ b/src/cpu/probes/pc_count_tracker_manager.cc @@ -39,38 +39,54 @@ namespace gem5 lastTick = 0; for (int i = 0 ; i < p.targets.size() ; i++) { + // initialize the counter for the inputted PC Count pair + // unordered_map does not allow duplicate, so counter won't + // have duplicates counter.insert(std::make_pair(p.targets[i].getPC(),0)); + // store all the PC Count pair into the targetPair set targetPair.insert(p.targets[i]); } + DPRINTF(PcCountTracker, + "total %i PCs in counter\n", counter.size()); } void PcCountTrackerManager::check_count(Addr pc) { - int count = ++counter.find(pc)->second; - if(ifListNotEmpty) { + int count = ++counter.find(pc)->second; + // increment the counter of the encountered PC address by 1 if(targetPair.empty()) { + // if all target PC Count pairs are encountered if(curTick() > lastTick) { + // if the current Tick is not equal to the Tick the last + // exit event was raised + // this is used to avoid overlapping exit events DPRINTF(PcCountTracker, "all targets are encountered. Last Tick:%i\n", lastTick); + exitSimLoop("reached the end of the PcCountPair list"); + // raise the PCCOUNTTRACK_END exit event ifListNotEmpty = false; } } currentPair = PcCountPair(pc,count); + // update the current PC Count pair if(targetPair.find(currentPair) != targetPair.end()) { - + // if the current PC Count pair is one of the target pairs DPRINTF(PcCountTracker, "pc:%s encountered\n", currentPair.to_string()); lastTick = curTick(); + // record the Tick when the SIMPOINT_BEGIN exit event is raised exitSimLoopNow("simpoint starting point found"); + // raise the SIMPOINT_BEGIN exit event targetPair.erase(currentPair); + // erase the encountered PC Count pair from the target pairs DPRINTF(PcCountTracker, "There are %i targets remained\n", targetPair.size()); } diff --git a/src/cpu/probes/pc_count_tracker_manager.hh b/src/cpu/probes/pc_count_tracker_manager.hh index 568c6f308b..cb2982a639 100644 --- a/src/cpu/probes/pc_count_tracker_manager.hh +++ b/src/cpu/probes/pc_count_tracker_manager.hh @@ -44,15 +44,30 @@ namespace gem5 class PcCountTrackerManager : public SimObject { public: PcCountTrackerManager(const PcCountTrackerManagerParams ¶ms); + void check_count(Addr pc); + // this function is called when PcCountTrackerProbeListener finds + // a target PC private: std::unordered_map counter; - std::unordered_set targetPair; + // a counter that stores all the target PC addresses and the number + // of times the target PC has been executed + std::unordered_set targetPair; + // a set that stores all the PC Count pairs that should raise an + // exit event at PcCountPair currentPair; + // the current PC Count pair. Tick lastTick; + // the Tick when an exit event was last raised. It it used to + // avoid rasing two exit event at the same Tick bool ifListNotEmpty; + // when all the PC Count pairs in the `targetPair` are encountered, + // and the PCCOUNTTRACK_END exit event is raised, this boolean + // variable becomes false and is used to stop the `check_count` + // from functioning. This is default as true. public: int get_pc_count(Addr pc) const { @@ -61,9 +76,13 @@ namespace gem5 } return -1; } + // this function returns the corresponding value of count for the + // inputted Program Counter address. If the PC address does not + // exist in the counter, then it returns a -1. PcCountPair get_current_pc_count_pair() const { return currentPair; } + // this function returns the current PC Count pair }; } diff --git a/src/python/gem5/simulate/exit_event_generators.py b/src/python/gem5/simulate/exit_event_generators.py index 9885cb3cee..3fa96edfb1 100644 --- a/src/python/gem5/simulate/exit_event_generators.py +++ b/src/python/gem5/simulate/exit_event_generators.py @@ -172,13 +172,26 @@ def simpoints_save_checkpoint_generator( def looppoint_save_checkpoint_generator( checkpoint_dir: Path, looppoint: LoopPoint, update_relatives: bool = True ): + """ + A generator for taking a checkpoint for LoopPoint. It will save the + checkpoints in the checkpoint_dir path with the Region id. + (i.e. "cpt.Region10) It only takes a checkpoint if the current PC Count + pair is a significant PC Count Pair. This is determined in the LoopPoint + module. The simulation loop continues after exiting this generator. + :param checkpoint_dir: where to save the checkpoints + :param loopoint: the looppoint object used in the configuration script + :param update_relative: if the generator should update the relative count + information in the output json file, then it should be True. It is default + as True. + """ while True: region = looppoint.get_current_region() + # if it is a significant PC Count pair, then the get_current_region() + # will return an integer greater than 0. By significant PC Count pair, + # it means the PC Count pair that indicates where to take the + # checkpoint at. This is determined in the LoopPoint module. if(region != -1): if(update_relatives): looppoint.update_relatives() m5.checkpoint((checkpoint_dir / f"cpt.Region{region}").as_posix()) yield False - - - diff --git a/src/python/gem5/utils/looppoint.py b/src/python/gem5/utils/looppoint.py index bfb103230c..52eaf7d62a 100644 --- a/src/python/gem5/utils/looppoint.py +++ b/src/python/gem5/utils/looppoint.py @@ -36,13 +36,29 @@ import json class LoopPoint: + """ + This LoopPoint class is used to manage the information needed for LoopPoint + in workload + """ def __init__( self, targets: List[PcCountPair], regions: Dict[PcCountPair,int], json_file: Dict[int, Dict] ) -> None: + """ + :param targets: a list of PcCountPair that are used to generate exit + event at when the PcCountTrackerManager encounter this PcCountPair in + execution + :param regions: a dictionary used to find the corresponding region id + for the significant PcCountPair. This is mainly used to ensure + checkpoints are taken in the correct PcCountPair or relative counts are + updated at the correct count + :param json_file: all the LoopPoint data including relative counts and + multiplier are stored in this parameter. It can be outputted as a json + file. + """ self._manager = PcCountTrackerManager() self._manager.targets = targets @@ -54,10 +70,21 @@ def setup_processor( self, processor: AbstractProcessor, ) -> None: + """ + This function is used to setup a PC tracker in all the cores and + connect all the tracker to the PC tracker manager to perform + multithread PC tracking + :param processor: the processor used in the simulation configuration + """ for core in processor.get_cores(): core.add_pc_tracker_probe(self._targets, self._manager) def update_relatives(self) -> None: + """ + This function is used to update the relative count for restore used. + The new relative count will be stored in the _json_file and can be + outputted into a json file by calling the output_json_file function. + """ current_pair = self._manager.get_current_pc_count_pair() temp_pair = PcCountPair(current_pair.getPC(), current_pair.getCount()) if(temp_pair in self._regions): @@ -73,12 +100,23 @@ def update_relatives(self) -> None: def output_json_file( self, - input_indent: int = 4 + input_indent: int = 4, + filename: str = "LoopPoint.json" ) -> Dict[int, Dict]: - with open("LoopPoint.json", "w") as file: + """ + This function is used to output the _json_file into a json file + :param input_indent: the indent value of the json file + :param filename: the name of the output file + """ + with open(filename, "w") as file: json.dump(self._json_file, file, indent=input_indent) def get_current_region(self) -> int: + """ + This function returns the region id if the current PC Count pair is + significant(e.x. beginning of the checkpoint), otherwise, it returns + a '-1' to indicate the current PC Count pair is not significant + """ current_pair = self._manager.get_current_pc_count_pair() temp_pair = PcCountPair(current_pair.getPC(), current_pair.getCount()) if(temp_pair in self._regions): @@ -86,21 +124,37 @@ def get_current_region(self) -> int: return -1 def get_current_pair(self) -> PcCountPair: + """ + This function returns the current PC Count pair + """ return self._manager.get_current_pc_count_pair() def get_regions(self) -> Dict[PcCountPair,int]: + """ + This function returns the complete dictionary of _regions + """ return self._regions def get_targets(self) -> List[PcCountPair]: + """ + This function returns the complete list of _targets + """ return self._targets - class LoopPointCheckpoint(LoopPoint): def __init__( self, LoopPointFilePath: Path, - if_csv: bool = True + if_csv: bool ) -> None: + """ + This class is specifically designed to take in the LoopPoint data file + and generate the information needed to take checkpoints for LoopPoint + regions(warmup region+simulation region) + :param LoopPointFilePath: the director of the LoopPoint data file + :param if_csv: if the file is a csv file, then it is True. If the file + is a json file, then it is False + """ _json_file = {} _targets = [] @@ -134,16 +188,29 @@ def profile_csv( json_file: Dict[int, Dict], region_id: Dict[PcCountPair, int], ) -> None: + """ + This function profiles the csv LoopPoint data file into three variables + to take correct checkpoints for LoopPoint + :param looppoint_file_path: the director of the LoopPoint data file + :param targets: a list of PcCountPair + :param json_file: a dictionary for all the LoopPoint data + :param region_id: a dictionary for all the significant PcCountPair and + its corresponding region id + """ + # This section is hard-coded to parse the data in the csv file. + # The csv file is assumed to have a constant format. with open(looppoint_file_path, newline="") as csvfile: spamreader = csv.reader(csvfile, delimiter=" ", quotechar="|") for row in spamreader: if len(row) > 1: if row[0] == "cluster": + # if it is a simulation region line = row[4].split(",") start = PcCountPair(int(line[3],16),int(line[6])) end = PcCountPair(int(line[7],16),int(line[10])) if(int(line[2]) in json_file): + # if this region was created in the json_file json_file[int(line[2])]["simulation"]={"start": {"pc": int(line[3],16)}} else: json_file[int(line[2])] = {"simulation": {"start": {"pc": int(line[3],16)}}} @@ -153,6 +220,8 @@ def profile_csv( json_file[int(line[2])]["multiplier"] = float(line[14]) targets.append(start) targets.append(end) + # store all the PC Count pairs from the file to the + # targets list elif row[0] == "Warmup": line = row[3].split(",") start = PcCountPair(int(line[3],16),int(line[6])) @@ -166,12 +235,21 @@ def profile_csv( json_file[int(line[0])]["warmup"]["end"]["count"] = int(line[10]) targets.append(start) targets.append(end) + # store all the PC Count pairs from the file to the + # targets list for rid, region in json_file.items(): + # this loop iterates all the regions and find the significant PC + # Count pair for the region if("warmup" in region): + # if the region has a warmup interval, then the checkpoint + # should be taken at the start of the warmup interval start = PcCountPair(region["warmup"]["start"]["pc"], region["warmup"]["start"]["count"]) else: + # if the region does not have a warmup interval, then the + # checkpoint should be taken at the start of the simulation + # region start = PcCountPair(region["simulation"]["start"]["pc"], region["simulation"]["start"]["global"]) region_id[start] = rid @@ -183,16 +261,31 @@ def profile_json( json_file: Dict[int, Dict], region_id: Dict[PcCountPair, int], ) -> None: + """ + This function profiles the json LoopPoint data file into three + variables to take correct checkpoints for LoopPoint + :param looppoint_file_path: the director of the LoopPoint data file + :param targets: a list of PcCountPair + :param json_file: a dictionary for all the LoopPoint data + :param region_id: a dictionary for all the significant PcCountPair and + its corresponding region id + """ + with open(looppoint_file_path) as file: json_file = json.load(file) + # load all json information into the json_file variable for rid, region in json_file.items(): + # iterates all regions sim_start = PcCountPair(region["simulation"]["start"]["pc"], region["simulation"]["start"]["global"]) targets.append(sim_start) + # store all PC Count pairs in the file into targets list end = PcCountPair(region["simulation"]["end"]["pc"], region["simulation"]["end"]["global"]) targets.append(end) if("warmup" in region): + # if there is a warmup in the region, then the checkpoint + # should be taken at the start of the warmup interval start = PcCountPair(region["warmup"]["start"]["pc"], region["warmup"]["start"]["count"]) targets.append(start) @@ -200,14 +293,26 @@ def profile_json( region["warmup"]["end"]["count"]) targets.append(end) else: + # if there is not a warmup interval in the region, then the + # checkpoint should be taken at the start of the simulation + # region start = sim_start - region_id[start] = rid class LoopPointRestore(LoopPoint): def __init__( self, LoopPointFilePath: Path, CheckPointDir: Path ) -> None: + """ + This class is specifically designed to take in the LoopPoint data file and + generator information needed to restore a checkpoint taken by the + LoopPointCheckPoint. + :param LoopPointFilePath: a json file generated by gem5 that has all the + LoopPoint data information + :param CheckPointDir: the director of the checkpoint taken by the gem5 + standard library looppoint_save_checkpoint_generator + + """ _json_file = {} _targets = [] @@ -235,15 +340,30 @@ def profile_restore( json_file: Dict[int, Dict], region_id: Dict[PcCountPair, int], ) -> None: + """ + This function is used to profile data from the LoopPoint data file to + information needed to restore the LoopPoint checkpoint + :param looppoint_file_path: the director of the LoopPoint data file + :param targets: a list of PcCountPair + :param json_file: a dictionary for all the LoopPoint data + :param region_id: a dictionary for all the significant PcCountPair and + its corresponding region id + """ regex = re.compile(r"cpt.Region([0-9]+)") rid = regex.findall(checkpoint_dir.as_posix())[0] + # finds out the region id from the directory name with open(looppoint_file_path) as file: json_file = json.load(file) if(rid not in json_file): + # if the region id does not exist in the LoopPoint data file + # raise a fatal message fatal(f"{rid} is not a valid region\n") region = json_file[rid] if("warmup" in region): if("relative" not in region["simulation"]["start"]): + # if there are not relative counts for the PC Count pair + # then it means there is not enough information to restore + # this checkpoint fatal(f"region {rid} doesn't have relative count info\n") start = PcCountPair(region["simulation"]["start"]["pc"], region["simulation"]["start"]["relative"]) diff --git a/src/python/m5/params.py b/src/python/m5/params.py index 93909031c8..aee8226744 100644 --- a/src/python/m5/params.py +++ b/src/python/m5/params.py @@ -854,6 +854,8 @@ def pretty_print(self, value): return "0x%x" % int(val) class PcCountPair(ParamValue): + # This parameter stores a Program Counter address and the a count value for + # the Program Counter address cxx_type = "PcCountPair" cmd_line_settable = True diff --git a/src/python/pybind11/core.cc b/src/python/pybind11/core.cc index dcbea99761..9673478c47 100644 --- a/src/python/pybind11/core.cc +++ b/src/python/pybind11/core.cc @@ -172,7 +172,10 @@ init_pc(py::module_ &m_native) .def(py::init()) .def("__eq__", &PcCountPair::operator==) .def("__str__", &PcCountPair::to_string) - + // TODO: add __hash__ that matches the python __hash__ to enable + // hashing from the C++ class to the Python class. + // Currently the C++ class and the Python class use different hashing + // functions .def("getPC", &PcCountPair::getPC) .def("getCount", &PcCountPair::getCount) ; From c47378a470d4de788df6bef1624443aea84a7b3c Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Wed, 14 Dec 2022 15:19:06 -0800 Subject: [PATCH 13/20] fixed a bug in LoopPointRestore where it takes the start relative count instead of the end Change-Id: If84afeb42222830bb058d1addc92a5265681cd23 --- src/python/gem5/utils/looppoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/gem5/utils/looppoint.py b/src/python/gem5/utils/looppoint.py index 52eaf7d62a..eb94f6ab35 100644 --- a/src/python/gem5/utils/looppoint.py +++ b/src/python/gem5/utils/looppoint.py @@ -371,7 +371,7 @@ def profile_restore( targets.append(start) if("relative" not in region["simulation"]["end"]): fatal(f"region {rid} doesn't have relative count info\n") - end = PcCountPair(region["simulation"]["start"]["pc"], + end = PcCountPair(region["simulation"]["end"]["pc"], region["simulation"]["end"]["relative"]) region_id[end]=rid targets.append(end) From 434966356de49e24cc320c759627a1696bf9d253 Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Thu, 15 Dec 2022 10:32:06 -0800 Subject: [PATCH 14/20] changed LoopPointFilePath and CheckPointDir format Change-Id: I0a9f7ec241b664be49c5cab66bef39ce80079cd1 --- src/python/gem5/utils/looppoint.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/python/gem5/utils/looppoint.py b/src/python/gem5/utils/looppoint.py index eb94f6ab35..227f4c6279 100644 --- a/src/python/gem5/utils/looppoint.py +++ b/src/python/gem5/utils/looppoint.py @@ -91,6 +91,9 @@ def update_relatives(self) -> None: rid = self._regions[temp_pair] region = self._json_file[rid]["simulation"] if("warmup" in self._json_file[rid]): + # if this region has a warmup interval, + # then update the relative count for the + # start of the simulation region start = region["start"]["pc"] temp = region["start"]["global"] - self._manager.get_pc_count(start) self._json_file[rid]["simulation"]["start"]["relative"] = int(temp) @@ -144,14 +147,14 @@ def get_targets(self) -> List[PcCountPair]: class LoopPointCheckpoint(LoopPoint): def __init__( self, - LoopPointFilePath: Path, + _looppoint_file_path: Path, if_csv: bool ) -> None: """ This class is specifically designed to take in the LoopPoint data file and generate the information needed to take checkpoints for LoopPoint regions(warmup region+simulation region) - :param LoopPointFilePath: the director of the LoopPoint data file + :param _looppoint_file_path: the director of the LoopPoint data file :param if_csv: if the file is a csv file, then it is True. If the file is a json file, then it is False """ @@ -162,14 +165,14 @@ def __init__( if(if_csv): self.profile_csv( - LoopPointFilePath, + _looppoint_file_path, _targets, _json_file, _region_id ) else: self.profile_json( - LoopPointFilePath, + _looppoint_file_path, _targets, _json_file, _region_id @@ -301,15 +304,15 @@ def profile_json( class LoopPointRestore(LoopPoint): def __init__( - self, LoopPointFilePath: Path, CheckPointDir: Path + self, _looppoint_file_path: Path, _checkpoint_dir: Path ) -> None: """ This class is specifically designed to take in the LoopPoint data file and generator information needed to restore a checkpoint taken by the LoopPointCheckPoint. - :param LoopPointFilePath: a json file generated by gem5 that has all the + :param _looppoint_file_path: a json file generated by gem5 that has all the LoopPoint data information - :param CheckPointDir: the director of the checkpoint taken by the gem5 + :param _checkpoint_dir: the director of the checkpoint taken by the gem5 standard library looppoint_save_checkpoint_generator """ @@ -319,8 +322,8 @@ def __init__( _region_id = {} self.profile_restore( - LoopPointFilePath, - CheckPointDir, + _looppoint_file_path, + _checkpoint_dir, _targets, _json_file, _region_id From 36adc378aae63c1a1bafb01389f5760cd8ca2bbd Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Thu, 15 Dec 2022 10:46:54 -0800 Subject: [PATCH 15/20] added LoopPoint documentation --- documentation.md | 315 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 documentation.md diff --git a/documentation.md b/documentation.md new file mode 100644 index 0000000000..d18514be5b --- /dev/null +++ b/documentation.md @@ -0,0 +1,315 @@ +# LoopPoint Documentation + + +1. [Take Checkpoint](#take-checkpoint) + - [LoopPointCheckpoint Module](#looppointcheckpoint-module) + - [looppoint_save_checkpoint_generator](#looppoint_save_checkpoint_generator) + - [Example Script](#take-checkpoint-example-script) +2. [Restore Checkpoint](#restore-checkpoint) + - [Example Script](#restore-checkpoint-example-script) +3. [Json File](#json-file) + - [structure](#structure) + - [components](#components) + - [Example](#example) +4. [Others](#others) + - [PcCountPair](#pccountpair) + - [Debug Flag](#degub-flags) + + +The main methodology of performing LoopPoint sampling in the gem5 simulator is taking and restoring checkpoints for LoopPoint significant regions. + +This can be done easily with the standard library LoopPoint modules. + +## Take Checkpoint +In the gem5 standard library, there is a `LoopPointCheckpoint` module designed specifically to take checkpoints for LoopPoint with the `set_se_looppoint_workload` module and the `looppoint_save_checkpoint_generator` generator. + + +### LoopPointCheckpoint module +The `LoopPointCheckpoint` module requires two parameters: `_looppoint_file_path` and `if_csv`. + +`_looppoint_file_path` is a Path object that contains the path of the LoopPoint data file. The data file can be the csv file generated by Pin, or the json file generated by gem5. + +`if_csv` is a boolean object that is True if the file is a csv file, or False if the file is a json file. + +With these two inputs, the `LoopPointCheckpoint` module is able to generate and store information needed for the LoopPoint workload. + +Outputting the information by calling the `output_json_file` in the LoopPoint module in the Python script is strongly recommended and the json file is needed to restore the checkpoint later with the `LoopPointRestore` module. + +After creating a `LoopPointCheckpoint` object with the required inputs, this object can be passed into the `set_se_looppoint_workload`. + +If the LoopPoint information is correctly passed into the cores, there will be `SIMPOINT_END` exit events generated when the target Program Counter address has been executed the number of times the LoopPoint data specifies. + +When the exit event is raised, the `looppoint_save_checkpoint_generator` can evaluate the (PC,count) pair and determine if it is going to take a checkpoint at it or not. + +### looppoint_save_checkpoint_generator +The `looppoint_save_checkpoint_generator` requires three parameters: `checkpoint_dir`, `looppoint`, and `update_relatives`. + +`checkpoint_dir` is a Path object that contains the path to the directory the checkpoints should be stored at. + +`looppoint` is the LoopPoint object(the `LoopPointCheckPoint` object created above) that will be used to identify the current (PC, count) pair the simulation reaches, the corresponding region id of this pair, and update the relative counts in the region if needed. + +`update_relatives` is a boolean object that is True if the relative count in the region should be updated, otherwise, it should be False. If you do not have relative counts in your LoopPoint data file, then it should be True. Relative count information is needed in the json file to restore checkpoints for LoopPoint. It is default as True. + +When the simulation encountered all the (PC, count) pairs in the LoopPoint data file, then it will raises an `PCCOUNTTRACK_END` exit event. With the standard library feature, the simulation can exit the simulation loop when this exit event is encountered. + + +### Take Checkpoint Example Script +``` Python +from gem5.simulate.exit_event import ExitEvent +from gem5.simulate.simulator import Simulator +from gem5.resources.resource import Resource +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.memory.single_channel import SingleChannelDDR3_1600 +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.components.processors.cpu_types import CPUTypes +from gem5.isas import ISA +from pathlib import Path +# LoopPoint related modules +from gem5.utils.looppoint import LoopPointCheckpoint +from gem5.simulate.exit_event_generators import looppoint_save_checkpoint_generator, exit_generator + +# When taking checkpoints, we can use simpler configurations, for example NoCache for cache_hierarchy and ATOMIC CPUs. +# Most of the configurations can be changed in the restore script. +# There are some exceptions, for example the num_cores and the size of memory must match in both the taking checkpoint script and the restore script. +cache_hierarchy = NoCache() + +memory = SingleChannelDDR3_1600(size="2GB") + +processor = SimpleProcessor( + cpu_type=CPUTypes.ATOMIC, + isa=ISA.X86, + num_cores=9, +) + +board = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +dir = Path("LoopPoint-CheckPoints/") +dir.mkdir(exist_ok=True) + +# ---------------LoopPoint part begins--------------- +# LoopPoint object created +looppoint = LoopPointCheckpoint( + _looppoint_file_path=Path("LoopPoint.csv"), + if_csv=True +) + +board.set_se_looppoint_workload( + binary= Resource("example-binary"), + looppoint = looppoint +) + +simulator = Simulator( + board=board, + on_exit_event ={ + # setup exit event generator + ExitEvent.SIMPOINT_BEGIN : + looppoint_save_checkpoint_generator( + checkpoint_dir=dir, + looppoint=looppoint, + update_relatives=True + ), + # exit the simulation loop when PCCOUNTRACK_END + # is encountered + ExitEvent.PCCOUNTTRACK_END : exit_generator() + } +) + +simulator.run() +# output json file +looppoint.output_json_file() +# ---------------LoopPoint part ends--------------- +``` + +## Restore Checkpoint +Checkpoints taken with the methodology above can be restored fairly straight forward with the `LoopPointRestore` and `set_se_looppoint_workload` module similar to how the checkpoints are taken. + +### LoopPointRestore module +The `LoopPointRestor` module requires two parameters: the `_looppoint_file_path` and `_checkpoint_dir`. + +`_looppoint_file_path` is a Path object that contains the path to the LoopPoint json file that is generated with the checkpoint taking script. + +`_checkpoint_dir` is the Path that contains the path to the checkpoint directory. + +With these two inputs, the module is able to get the information needed to restore checkpoints taken for LoopPoint. + +After passing the `LoopPointRestore` object into `set_se_looppoint_workload` module, the simulation will raise `SIMPOINT_BEGIN` exit event when it encounters the (PC, count) pair. You can setup an exit event generator with the `Simulator` module in the standard library to perform different action to the exit event. + +### Restore Checkpoint Example Script +``` Python +from gem5.simulate.exit_event import ExitEvent +from gem5.simulate.simulator import Simulator +from gem5.components.cachehierarchies.classic.\ + private_l1_private_l2_cache_hierarchy import ( + PrivateL1PrivateL2CacheHierarchy, +) +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.memory import DualChannelDDR4_2400 +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.components.processors.cpu_types import CPUTypes +from gem5.isas import ISA +from gem5.resources.resource import Resource +from pathlib import Path +from gem5.simulate.exit_event_generators import dump_reset_generator +# LoopPoint related module +from gem5.utils.looppoint import LoopPointRestore + +# Using a more complex cache hierarchy than the one +# used in the taking checkpoint script +cache_hierarchy = PrivateL1PrivateL2CacheHierarchy( + l1d_size="32kB", + l1i_size="32kB", + l2_size="256kB", +) + +memory = DualChannelDDR4_2400(size="2GB") + +processor = SimpleProcessor( + cpu_type=CPUTypes.O3, + isa=ISA.X86, + num_cores=9, +) + +board = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +# ---------------LoopPoint part begins--------------- +# LoopPoint object created +looppoint = LoopPointRestore( + _looppoint_file_path=Path("LoopPoint.json"), + _checkpoint_dir=Path("LoopPoint-CheckPoints/cpt.Region1") +) + +board.set_se_looppoint_workload( + binary= Resource("example-binary"), + looppoint=looppoint +) + +simulator = Simulator( + board=board, + checkpoint_path=args.checkpoint, + on_exit_event ={ + # setup generator to perform a statistic dump and reset + # when the SIMPOINT_BEGIN exit event is encountered + ExitEvent.SIMPOINT_BEGIN : dump_reset_generator(), + # exit the simulation loop when PCCOUNTRACK_END + # is encountered + ExitEvent.PCCOUNTTRACK_END: exit_generator() + } +) + +simulator.run() +# ---------------LoopPoint part ends--------------- +``` + +## Json File + + +### structure + +Below is the structure of the json file +``` +region id + |___ simulation + | |___ start + | | |___ pc + | | |___ global + | | |___ relative + | |___ end + | |___ pc + | |___ global + | |___ relative + |___ warmup # optional to region + | |___ start + | | |___ pc + | | |___ global + | | |___ relative + | |___ end + | |___ pc + | |___ global + | |___ relative + |___ multiplier + +``` + +### components + +`region id` represent the specific region that is selected by the LoopPoint sampling methodology. Each of this specific region has a simulation region, but it might or might not has a warmup region. + +`simulation` is the region that should be simulate in detail. + +`warmup` is the warmup region that should be used to warmup before performing detail simulation on the simulation region. + +`pc` is the Program Counter address. + +`global` is the global count that starts counting from the beginning of the workload. + +`relative` is the relative count that starts counting from the beginning of the (PC, count) pair where the checkpoint is taken. + +`multiplier` is the weight that should be applied on the data for this region to calculate the runtime with the LoopPoint sampling methodology. + +### Example + +``` Json + "1": { + "simulation": { + "start": { + "pc": 4221392, + "global": 211076617, + "relative": 15326617 + }, + "end": { + "pc": 4221392, + "global": 219060252, + "relative": 23310252 + } + }, + "multiplier": 4.0, + "warmup": { + "start": { + "pc": 4221056, + "count": 23520614 + }, + "end": { + "pc": 4221392, + "count": 211076617 + } + } + }, + "2": { + "simulation": { + "start": { + "pc": 4206672, + "global": 1 + }, + "end": { + "pc": 4221392, + "global": 6861604, + "relative": 6861604 + } + }, + "multiplier": 1.0 + } +``` + +## Others + +### PcCountPair + +`PcCountPair` is a special parameter for storing a Program Counter address (`PcCountPair::pc`) and an integer value of count(`PcCountPair::count`). The `PcCountPair` object can communicate between Python and C++, and can be used in the same way. However, the hashing functions between Python and C++ of this parameter are different. It is known that the C++ `PcCountPair` object can not be used as the key to find a value in the Python hash table. + +### Debug Flag + +`PcCountTracker` is a debug flag for the `PcCountTrackerManager` SimObject, which is the underneath object that is responsible for tracking the Program Counter address and the number of times they have been executed. It can be enabled in the command line. For example: + + build/X86/gem5.opt --debug-flags=PcCountTracker config_script.py + From f587c9b7b1f7c166a079c9789a88a802f52efd5f Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Thu, 15 Dec 2022 13:18:01 -0800 Subject: [PATCH 16/20] changed parameter names for looppoint input file and checkpoint dir in looppoint.py --- documentation.md | 16 ++++++++-------- src/python/gem5/utils/looppoint.py | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/documentation.md b/documentation.md index d18514be5b..2e1d2e9dcb 100644 --- a/documentation.md +++ b/documentation.md @@ -25,9 +25,9 @@ In the gem5 standard library, there is a `LoopPointCheckpoint` module designed s ### LoopPointCheckpoint module -The `LoopPointCheckpoint` module requires two parameters: `_looppoint_file_path` and `if_csv`. +The `LoopPointCheckpoint` module requires two parameters: `looppoint_file` and `if_csv`. -`_looppoint_file_path` is a Path object that contains the path of the LoopPoint data file. The data file can be the csv file generated by Pin, or the json file generated by gem5. +`looppoint_file` is a Path object that contains the path of the LoopPoint data file. The data file can be the csv file generated by Pin, or the json file generated by gem5. `if_csv` is a boolean object that is True if the file is a csv file, or False if the file is a json file. @@ -95,7 +95,7 @@ dir.mkdir(exist_ok=True) # ---------------LoopPoint part begins--------------- # LoopPoint object created looppoint = LoopPointCheckpoint( - _looppoint_file_path=Path("LoopPoint.csv"), + looppoint_file=Path("LoopPoint.csv"), if_csv=True ) @@ -130,11 +130,11 @@ looppoint.output_json_file() Checkpoints taken with the methodology above can be restored fairly straight forward with the `LoopPointRestore` and `set_se_looppoint_workload` module similar to how the checkpoints are taken. ### LoopPointRestore module -The `LoopPointRestor` module requires two parameters: the `_looppoint_file_path` and `_checkpoint_dir`. +The `LoopPointRestor` module requires two parameters: the `looppoint_file` and `checkpoint_path`. -`_looppoint_file_path` is a Path object that contains the path to the LoopPoint json file that is generated with the checkpoint taking script. +`looppoint_file` is a Path object that contains the path to the LoopPoint json file that is generated with the checkpoint taking script. -`_checkpoint_dir` is the Path that contains the path to the checkpoint directory. +`checkpoint_path` is the Path that contains the path to the checkpoint directory. With these two inputs, the module is able to get the information needed to restore checkpoints taken for LoopPoint. @@ -185,8 +185,8 @@ board = SimpleBoard( # ---------------LoopPoint part begins--------------- # LoopPoint object created looppoint = LoopPointRestore( - _looppoint_file_path=Path("LoopPoint.json"), - _checkpoint_dir=Path("LoopPoint-CheckPoints/cpt.Region1") + looppoint_file=Path("LoopPoint.json"), + checkpoint_path=Path("LoopPoint-CheckPoints/cpt.Region1") ) board.set_se_looppoint_workload( diff --git a/src/python/gem5/utils/looppoint.py b/src/python/gem5/utils/looppoint.py index 227f4c6279..4d3f630802 100644 --- a/src/python/gem5/utils/looppoint.py +++ b/src/python/gem5/utils/looppoint.py @@ -147,14 +147,14 @@ def get_targets(self) -> List[PcCountPair]: class LoopPointCheckpoint(LoopPoint): def __init__( self, - _looppoint_file_path: Path, + looppoint_file: Path, if_csv: bool ) -> None: """ This class is specifically designed to take in the LoopPoint data file and generate the information needed to take checkpoints for LoopPoint regions(warmup region+simulation region) - :param _looppoint_file_path: the director of the LoopPoint data file + :param looppoint_file: the director of the LoopPoint data file :param if_csv: if the file is a csv file, then it is True. If the file is a json file, then it is False """ @@ -165,14 +165,14 @@ def __init__( if(if_csv): self.profile_csv( - _looppoint_file_path, + looppoint_file, _targets, _json_file, _region_id ) else: self.profile_json( - _looppoint_file_path, + looppoint_file, _targets, _json_file, _region_id @@ -304,15 +304,15 @@ def profile_json( class LoopPointRestore(LoopPoint): def __init__( - self, _looppoint_file_path: Path, _checkpoint_dir: Path + self, looppoint_file: Path, checkpoint_path: Path ) -> None: """ This class is specifically designed to take in the LoopPoint data file and generator information needed to restore a checkpoint taken by the LoopPointCheckPoint. - :param _looppoint_file_path: a json file generated by gem5 that has all the + :param looppoint_file: a json file generated by gem5 that has all the LoopPoint data information - :param _checkpoint_dir: the director of the checkpoint taken by the gem5 + :param checkpoint_path: the director of the checkpoint taken by the gem5 standard library looppoint_save_checkpoint_generator """ @@ -322,8 +322,8 @@ def __init__( _region_id = {} self.profile_restore( - _looppoint_file_path, - _checkpoint_dir, + looppoint_file, + checkpoint_path, _targets, _json_file, _region_id From 6fd552d22349a63dc3050d56f84d58fb3b78e174 Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Thu, 15 Dec 2022 13:31:38 -0800 Subject: [PATCH 17/20] used pre-commit --- src/base/types.hh | 4 +- src/cpu/probes/PcCountTracker.py | 11 +- src/cpu/probes/SConscript | 4 +- src/cpu/probes/pc_count_tracker.cc | 8 +- src/cpu/probes/pc_count_tracker.hh | 6 +- src/cpu/probes/pc_count_tracker_manager.cc | 14 +- src/cpu/probes/pc_count_tracker_manager.hh | 12 +- .../components/boards/se_binary_workload.py | 2 +- .../components/processors/abstract_core.py | 5 +- .../components/processors/base_cpu_core.py | 16 +- .../gem5/simulate/exit_event_generators.py | 11 +- src/python/gem5/utils/looppoint.py | 253 ++++++++++-------- src/python/m5/params.py | 21 +- src/python/pybind11/core.cc | 2 +- 14 files changed, 196 insertions(+), 173 deletions(-) diff --git a/src/base/types.hh b/src/base/types.hh index fc6ab937ae..24494e2cea 100644 --- a/src/base/types.hh +++ b/src/base/types.hh @@ -151,7 +151,7 @@ class PcCountPair public: /** Explicit constructor assigning the pc and count values*/ - explicit constexpr PcCountPair(uint64_t _pc, int _count) : + explicit constexpr PcCountPair(uint64_t _pc, int _count) : pc(_pc), count(_count) {} /** Default constructor for parameter classes*/ @@ -180,7 +180,7 @@ class PcCountPair std::string to_string() const { - std::string s = "(" + std::to_string(pc) + std::string s = "(" + std::to_string(pc) + ", " + std::to_string(count) + ")"; return s; } diff --git a/src/cpu/probes/PcCountTracker.py b/src/cpu/probes/PcCountTracker.py index e27ac9506e..982020f20f 100644 --- a/src/cpu/probes/PcCountTracker.py +++ b/src/cpu/probes/PcCountTracker.py @@ -29,19 +29,21 @@ from m5.objects.Probe import ProbeListenerObject from m5.objects import SimObject + class PcCountTrackerManager(SimObject): - + type = "PcCountTrackerManager" cxx_header = "cpu/probes/pc_count_tracker_manager.hh" cxx_class = "gem5::PcCountTrackerManager" - + cxx_exports = [ PyBindMethod("get_pc_count"), - PyBindMethod("get_current_pc_count_pair") + PyBindMethod("get_current_pc_count_pair"), ] targets = VectorParam.PcCountPair("the target PC Count pairs") - + + class PcCountTracker(ProbeListenerObject): type = "PcCountTracker" @@ -51,4 +53,3 @@ class PcCountTracker(ProbeListenerObject): targets = VectorParam.PcCountPair("the target PC Count pairs") core = Param.BaseCPU("the connected cpu") ptmanager = Param.PcCountTrackerManager("the PcCountTracker manager") - \ No newline at end of file diff --git a/src/cpu/probes/SConscript b/src/cpu/probes/SConscript index 48c83aaa49..8731eb376b 100644 --- a/src/cpu/probes/SConscript +++ b/src/cpu/probes/SConscript @@ -27,9 +27,9 @@ Import('*') if not env['CONF']['USE_NULL_ISA']: - SimObject('PcCountTracker.py', + SimObject('PcCountTracker.py', sim_objects=['PcCountTracker','PcCountTrackerManager']) Source('pc_count_tracker.cc') Source('pc_count_tracker_manager.cc') - + DebugFlag('PcCountTracker') diff --git a/src/cpu/probes/pc_count_tracker.cc b/src/cpu/probes/pc_count_tracker.cc index 3429d8fe9e..2e71bcf769 100644 --- a/src/cpu/probes/pc_count_tracker.cc +++ b/src/cpu/probes/pc_count_tracker.cc @@ -45,7 +45,7 @@ namespace gem5 } } - void + void PcCountTracker::regProbeListeners() { typedef ProbeListenerArg PcCountTrackerListener; @@ -54,16 +54,16 @@ namespace gem5 // connect the probe listener with the probe "RetriedInstsPC" in the // corresponding core. // when "RetiredInstsPC" notifies the probe listener, then the function - // 'check_pc' is automatically called + // 'check_pc' is automatically called } void PcCountTracker::check_pc(const Addr& pc) { if(targetPC.find(pc) != targetPC.end()) { - // if the PC is one of the target PCs, then notify the + // if the PC is one of the target PCs, then notify the // PcCounterTrackerManager by calling its `check_count` function manager->check_count(pc); } } -} \ No newline at end of file +} diff --git a/src/cpu/probes/pc_count_tracker.hh b/src/cpu/probes/pc_count_tracker.hh index 84d680b541..9ff339324e 100644 --- a/src/cpu/probes/pc_count_tracker.hh +++ b/src/cpu/probes/pc_count_tracker.hh @@ -41,7 +41,7 @@ namespace gem5 class PcCountTracker : public ProbeListenerObject { public: PcCountTracker(const PcCountTrackerParams ¶ms); - + virtual void regProbeListeners(); // setup the probelistener @@ -53,7 +53,7 @@ namespace gem5 std::unordered_set targetPC; // a set of Program Counter addresses that should notify the // PcCounterTrackerManager for - + BaseCPU *cpuptr; // the core this PcCountTracker is tracking at @@ -62,4 +62,4 @@ namespace gem5 }; } -#endif // __CPU_PROBES_PC_COUNT_TRACKER_HH__ \ No newline at end of file +#endif // __CPU_PROBES_PC_COUNT_TRACKER_HH__ diff --git a/src/cpu/probes/pc_count_tracker_manager.cc b/src/cpu/probes/pc_count_tracker_manager.cc index 58535421cb..998df16208 100644 --- a/src/cpu/probes/pc_count_tracker_manager.cc +++ b/src/cpu/probes/pc_count_tracker_manager.cc @@ -31,7 +31,7 @@ namespace gem5 { PcCountTrackerManager::PcCountTrackerManager( - const PcCountTrackerManagerParams &p) + const PcCountTrackerManagerParams &p) : SimObject(p) { currentPair = PcCountPair(0,0); @@ -49,8 +49,8 @@ namespace gem5 DPRINTF(PcCountTracker, "total %i PCs in counter\n", counter.size()); } - - void + + void PcCountTrackerManager::check_count(Addr pc) { if(ifListNotEmpty) { @@ -58,7 +58,7 @@ namespace gem5 // increment the counter of the encountered PC address by 1 if(targetPair.empty()) { - // if all target PC Count pairs are encountered + // if all target PC Count pairs are encountered if(curTick() > lastTick) { // if the current Tick is not equal to the Tick the last // exit event was raised @@ -71,13 +71,13 @@ namespace gem5 ifListNotEmpty = false; } } - + currentPair = PcCountPair(pc,count); // update the current PC Count pair if(targetPair.find(currentPair) != targetPair.end()) { // if the current PC Count pair is one of the target pairs DPRINTF(PcCountTracker, - "pc:%s encountered\n", + "pc:%s encountered\n", currentPair.to_string()); lastTick = curTick(); @@ -93,4 +93,4 @@ namespace gem5 } } -} \ No newline at end of file +} diff --git a/src/cpu/probes/pc_count_tracker_manager.hh b/src/cpu/probes/pc_count_tracker_manager.hh index cb2982a639..7c6f84117a 100644 --- a/src/cpu/probes/pc_count_tracker_manager.hh +++ b/src/cpu/probes/pc_count_tracker_manager.hh @@ -39,7 +39,7 @@ namespace gem5 { - + class PcCountTrackerManager : public SimObject { public: @@ -53,15 +53,15 @@ namespace gem5 std::unordered_map counter; // a counter that stores all the target PC addresses and the number // of times the target PC has been executed - std::unordered_set targetPair; // a set that stores all the PC Count pairs that should raise an // exit event at - + PcCountPair currentPair; // the current PC Count pair. Tick lastTick; - // the Tick when an exit event was last raised. It it used to + // the Tick when an exit event was last raised. It it used to // avoid rasing two exit event at the same Tick bool ifListNotEmpty; // when all the PC Count pairs in the `targetPair` are encountered, @@ -77,7 +77,7 @@ namespace gem5 return -1; } // this function returns the corresponding value of count for the - // inputted Program Counter address. If the PC address does not + // inputted Program Counter address. If the PC address does not // exist in the counter, then it returns a -1. PcCountPair get_current_pc_count_pair() const { return currentPair; @@ -87,4 +87,4 @@ namespace gem5 } -#endif // __CPU_PROBES_PC_COUNT_TRACKER_MANAGER_HH__ \ No newline at end of file +#endif // __CPU_PROBES_PC_COUNT_TRACKER_MANAGER_HH__ diff --git a/src/python/gem5/components/boards/se_binary_workload.py b/src/python/gem5/components/boards/se_binary_workload.py index 356e7e7625..9fa1393ed3 100644 --- a/src/python/gem5/components/boards/se_binary_workload.py +++ b/src/python/gem5/components/boards/se_binary_workload.py @@ -213,4 +213,4 @@ def get_looppoint(self) -> LoopPoint: """ if getattr(self, "_looppoint_object", None): return self._looppoint_object - raise Exception("This board does not have a looppoint set.") \ No newline at end of file + raise Exception("This board does not have a looppoint set.") diff --git a/src/python/gem5/components/processors/abstract_core.py b/src/python/gem5/components/processors/abstract_core.py index 8c9ffdc33a..4b0883320a 100644 --- a/src/python/gem5/components/processors/abstract_core.py +++ b/src/python/gem5/components/processors/abstract_core.py @@ -159,9 +159,6 @@ def _set_inst_stop_any_thread( @abstractmethod def add_pc_tracker_probe( - self, - targetPair: List[PcCountPair], - manager: PcCountTrackerManager + self, targetPair: List[PcCountPair], manager: PcCountTrackerManager ) -> None: raise NotImplementedError - \ No newline at end of file diff --git a/src/python/gem5/components/processors/base_cpu_core.py b/src/python/gem5/components/processors/base_cpu_core.py index b0f4eec4a9..c75c0029cf 100644 --- a/src/python/gem5/components/processors/base_cpu_core.py +++ b/src/python/gem5/components/processors/base_cpu_core.py @@ -34,12 +34,12 @@ from ...utils.requires import requires from m5.objects import ( - BaseMMU, - Port, - BaseCPU, - Process, + BaseMMU, + Port, + BaseCPU, + Process, PcCountTracker, - PcCountTrackerManager, + PcCountTrackerManager, ) from m5.params import PcCountPair @@ -180,12 +180,10 @@ def _set_inst_stop_any_thread( @overrides(AbstractCore) def add_pc_tracker_probe( - self, - target_pair: List[PcCountPair], - manager: PcCountTrackerManager + self, target_pair: List[PcCountPair], manager: PcCountTrackerManager ) -> None: pair_tracker = PcCountTracker() pair_tracker.targets = target_pair pair_tracker.core = self.core pair_tracker.ptmanager = manager - self.core.probeListener = pair_tracker \ No newline at end of file + self.core.probeListener = pair_tracker diff --git a/src/python/gem5/simulate/exit_event_generators.py b/src/python/gem5/simulate/exit_event_generators.py index 3fa96edfb1..fd0fee9b9f 100644 --- a/src/python/gem5/simulate/exit_event_generators.py +++ b/src/python/gem5/simulate/exit_event_generators.py @@ -169,13 +169,14 @@ def simpoints_save_checkpoint_generator( else: yield True + def looppoint_save_checkpoint_generator( checkpoint_dir: Path, looppoint: LoopPoint, update_relatives: bool = True ): """ A generator for taking a checkpoint for LoopPoint. It will save the - checkpoints in the checkpoint_dir path with the Region id. - (i.e. "cpt.Region10) It only takes a checkpoint if the current PC Count + checkpoints in the checkpoint_dir path with the Region id. + (i.e. "cpt.Region10) It only takes a checkpoint if the current PC Count pair is a significant PC Count Pair. This is determined in the LoopPoint module. The simulation loop continues after exiting this generator. :param checkpoint_dir: where to save the checkpoints @@ -186,12 +187,12 @@ def looppoint_save_checkpoint_generator( """ while True: region = looppoint.get_current_region() - # if it is a significant PC Count pair, then the get_current_region() + # if it is a significant PC Count pair, then the get_current_region() # will return an integer greater than 0. By significant PC Count pair, # it means the PC Count pair that indicates where to take the # checkpoint at. This is determined in the LoopPoint module. - if(region != -1): - if(update_relatives): + if region != -1: + if update_relatives: looppoint.update_relatives() m5.checkpoint((checkpoint_dir / f"cpt.Region{region}").as_posix()) yield False diff --git a/src/python/gem5/utils/looppoint.py b/src/python/gem5/utils/looppoint.py index 4d3f630802..259eaeb884 100644 --- a/src/python/gem5/utils/looppoint.py +++ b/src/python/gem5/utils/looppoint.py @@ -35,20 +35,22 @@ import re import json + class LoopPoint: """ This LoopPoint class is used to manage the information needed for LoopPoint in workload - + """ + def __init__( self, targets: List[PcCountPair], - regions: Dict[PcCountPair,int], - json_file: Dict[int, Dict] + regions: Dict[PcCountPair, int], + json_file: Dict[int, Dict], ) -> None: """ - :param targets: a list of PcCountPair that are used to generate exit + :param targets: a list of PcCountPair that are used to generate exit event at when the PcCountTrackerManager encounter this PcCountPair in execution :param regions: a dictionary used to find the corresponding region id @@ -57,15 +59,15 @@ def __init__( updated at the correct count :param json_file: all the LoopPoint data including relative counts and multiplier are stored in this parameter. It can be outputted as a json - file. + file. """ - + self._manager = PcCountTrackerManager() self._manager.targets = targets self._targets = targets self._regions = regions self._json_file = json_file - + def setup_processor( self, processor: AbstractProcessor, @@ -78,7 +80,7 @@ def setup_processor( """ for core in processor.get_cores(): core.add_pc_tracker_probe(self._targets, self._manager) - + def update_relatives(self) -> None: """ This function is used to update the relative count for restore used. @@ -87,25 +89,27 @@ def update_relatives(self) -> None: """ current_pair = self._manager.get_current_pc_count_pair() temp_pair = PcCountPair(current_pair.getPC(), current_pair.getCount()) - if(temp_pair in self._regions): + if temp_pair in self._regions: rid = self._regions[temp_pair] region = self._json_file[rid]["simulation"] - if("warmup" in self._json_file[rid]): + if "warmup" in self._json_file[rid]: # if this region has a warmup interval, - # then update the relative count for the + # then update the relative count for the # start of the simulation region start = region["start"]["pc"] - temp = region["start"]["global"] - self._manager.get_pc_count(start) - self._json_file[rid]["simulation"]["start"]["relative"] = int(temp) + temp = region["start"]["global"] - self._manager.get_pc_count( + start + ) + self._json_file[rid]["simulation"]["start"]["relative"] = int( + temp + ) end = region["end"]["pc"] temp = region["end"]["global"] - self._manager.get_pc_count(end) self._json_file[rid]["simulation"]["end"]["relative"] = int(temp) - + def output_json_file( - self, - input_indent: int = 4, - filename: str = "LoopPoint.json" - ) -> Dict[int, Dict]: + self, input_indent: int = 4, filename: str = "LoopPoint.json" + ) -> Dict[int, Dict]: """ This function is used to output the _json_file into a json file :param input_indent: the indent value of the json file @@ -113,77 +117,64 @@ def output_json_file( """ with open(filename, "w") as file: json.dump(self._json_file, file, indent=input_indent) - + def get_current_region(self) -> int: """ - This function returns the region id if the current PC Count pair is + This function returns the region id if the current PC Count pair is significant(e.x. beginning of the checkpoint), otherwise, it returns a '-1' to indicate the current PC Count pair is not significant """ current_pair = self._manager.get_current_pc_count_pair() temp_pair = PcCountPair(current_pair.getPC(), current_pair.getCount()) - if(temp_pair in self._regions): + if temp_pair in self._regions: return self._regions[temp_pair] return -1 - + def get_current_pair(self) -> PcCountPair: """ This function returns the current PC Count pair """ return self._manager.get_current_pc_count_pair() - - def get_regions(self) -> Dict[PcCountPair,int]: + + def get_regions(self) -> Dict[PcCountPair, int]: """ This function returns the complete dictionary of _regions """ return self._regions - - def get_targets(self) -> List[PcCountPair]: + + def get_targets(self) -> List[PcCountPair]: """ This function returns the complete list of _targets """ return self._targets + class LoopPointCheckpoint(LoopPoint): - def __init__( - self, - looppoint_file: Path, - if_csv: bool - ) -> None: + def __init__(self, looppoint_file: Path, if_csv: bool) -> None: """ This class is specifically designed to take in the LoopPoint data file - and generate the information needed to take checkpoints for LoopPoint + and generate the information needed to take checkpoints for LoopPoint regions(warmup region+simulation region) :param looppoint_file: the director of the LoopPoint data file :param if_csv: if the file is a csv file, then it is True. If the file is a json file, then it is False """ - + _json_file = {} - _targets = [] + _targets = [] _region_id = {} - - if(if_csv): - self.profile_csv( - looppoint_file, - _targets, - _json_file, - _region_id - ) + + if if_csv: + self.profile_csv(looppoint_file, _targets, _json_file, _region_id) else: - self.profile_json( - looppoint_file, - _targets, - _json_file, - _region_id - ) - + self.profile_json(looppoint_file, _targets, _json_file, _region_id) + super().__init__( _targets, _region_id, _json_file, ) - + def profile_csv( self, looppoint_file_path: Path, @@ -200,9 +191,9 @@ def profile_csv( :param region_id: a dictionary for all the significant PcCountPair and its corresponding region id """ - - # This section is hard-coded to parse the data in the csv file. - # The csv file is assumed to have a constant format. + + # This section is hard-coded to parse the data in the csv file. + # The csv file is assumed to have a constant format. with open(looppoint_file_path, newline="") as csvfile: spamreader = csv.reader(csvfile, delimiter=" ", quotechar="|") for row in spamreader: @@ -210,53 +201,79 @@ def profile_csv( if row[0] == "cluster": # if it is a simulation region line = row[4].split(",") - start = PcCountPair(int(line[3],16),int(line[6])) - end = PcCountPair(int(line[7],16),int(line[10])) - if(int(line[2]) in json_file): + start = PcCountPair(int(line[3], 16), int(line[6])) + end = PcCountPair(int(line[7], 16), int(line[10])) + if int(line[2]) in json_file: # if this region was created in the json_file - json_file[int(line[2])]["simulation"]={"start": {"pc": int(line[3],16)}} + json_file[int(line[2])]["simulation"] = { + "start": {"pc": int(line[3], 16)} + } else: - json_file[int(line[2])] = {"simulation": {"start": {"pc": int(line[3],16)}}} - json_file[int(line[2])]["simulation"]["start"]["global"] = int(line[6]) - json_file[int(line[2])]["simulation"]["end"] = {"pc" : int(line[7],16)} - json_file[int(line[2])]["simulation"]["end"]["global"] = int(line[10]) + json_file[int(line[2])] = { + "simulation": { + "start": {"pc": int(line[3], 16)} + } + } + json_file[int(line[2])]["simulation"]["start"][ + "global" + ] = int(line[6]) + json_file[int(line[2])]["simulation"]["end"] = { + "pc": int(line[7], 16) + } + json_file[int(line[2])]["simulation"]["end"][ + "global" + ] = int(line[10]) json_file[int(line[2])]["multiplier"] = float(line[14]) targets.append(start) targets.append(end) - # store all the PC Count pairs from the file to the + # store all the PC Count pairs from the file to the # targets list elif row[0] == "Warmup": line = row[3].split(",") - start = PcCountPair(int(line[3],16),int(line[6])) - end = PcCountPair(int(line[7],16),int(line[10])) - if(int(line[0]) in json_file): - json_file[int(line[0])]["warmup"] = {"start": {"pc": int(line[3],16)}} + start = PcCountPair(int(line[3], 16), int(line[6])) + end = PcCountPair(int(line[7], 16), int(line[10])) + if int(line[0]) in json_file: + json_file[int(line[0])]["warmup"] = { + "start": {"pc": int(line[3], 16)} + } else: - json_file[int(line[0])] = {"warmup": {"start": {"pc": int(line[3],16)}}} - json_file[int(line[0])]["warmup"]["start"]["count"] = int(line[6]) - json_file[int(line[0])]["warmup"]["end"] = {"pc": int(line[7],16)} - json_file[int(line[0])]["warmup"]["end"]["count"] = int(line[10]) + json_file[int(line[0])] = { + "warmup": {"start": {"pc": int(line[3], 16)}} + } + json_file[int(line[0])]["warmup"]["start"][ + "count" + ] = int(line[6]) + json_file[int(line[0])]["warmup"]["end"] = { + "pc": int(line[7], 16) + } + json_file[int(line[0])]["warmup"]["end"][ + "count" + ] = int(line[10]) targets.append(start) targets.append(end) - # store all the PC Count pairs from the file to the + # store all the PC Count pairs from the file to the # targets list - + for rid, region in json_file.items(): # this loop iterates all the regions and find the significant PC # Count pair for the region - if("warmup" in region): + if "warmup" in region: # if the region has a warmup interval, then the checkpoint # should be taken at the start of the warmup interval - start = PcCountPair(region["warmup"]["start"]["pc"], - region["warmup"]["start"]["count"]) + start = PcCountPair( + region["warmup"]["start"]["pc"], + region["warmup"]["start"]["count"], + ) else: - # if the region does not have a warmup interval, then the + # if the region does not have a warmup interval, then the # checkpoint should be taken at the start of the simulation # region - start = PcCountPair(region["simulation"]["start"]["pc"], - region["simulation"]["start"]["global"]) + start = PcCountPair( + region["simulation"]["start"]["pc"], + region["simulation"]["start"]["global"], + ) region_id[start] = rid - + def profile_json( self, looppoint_file_path: Path, @@ -265,7 +282,7 @@ def profile_json( region_id: Dict[PcCountPair, int], ) -> None: """ - This function profiles the json LoopPoint data file into three + This function profiles the json LoopPoint data file into three variables to take correct checkpoints for LoopPoint :param looppoint_file_path: the director of the LoopPoint data file :param targets: a list of PcCountPair @@ -273,27 +290,35 @@ def profile_json( :param region_id: a dictionary for all the significant PcCountPair and its corresponding region id """ - + with open(looppoint_file_path) as file: json_file = json.load(file) # load all json information into the json_file variable for rid, region in json_file.items(): # iterates all regions - sim_start = PcCountPair(region["simulation"]["start"]["pc"], - region["simulation"]["start"]["global"]) + sim_start = PcCountPair( + region["simulation"]["start"]["pc"], + region["simulation"]["start"]["global"], + ) targets.append(sim_start) # store all PC Count pairs in the file into targets list - end = PcCountPair(region["simulation"]["end"]["pc"], - region["simulation"]["end"]["global"]) + end = PcCountPair( + region["simulation"]["end"]["pc"], + region["simulation"]["end"]["global"], + ) targets.append(end) - if("warmup" in region): + if "warmup" in region: # if there is a warmup in the region, then the checkpoint # should be taken at the start of the warmup interval - start = PcCountPair(region["warmup"]["start"]["pc"], - region["warmup"]["start"]["count"]) + start = PcCountPair( + region["warmup"]["start"]["pc"], + region["warmup"]["start"]["count"], + ) targets.append(start) - end = PcCountPair(region["warmup"]["end"]["pc"], - region["warmup"]["end"]["count"]) + end = PcCountPair( + region["warmup"]["end"]["pc"], + region["warmup"]["end"]["count"], + ) targets.append(end) else: # if there is not a warmup interval in the region, then the @@ -302,33 +327,28 @@ def profile_json( start = sim_start region_id[start] = rid + class LoopPointRestore(LoopPoint): - def __init__( - self, looppoint_file: Path, checkpoint_path: Path - ) -> None: + def __init__(self, looppoint_file: Path, checkpoint_path: Path) -> None: """ This class is specifically designed to take in the LoopPoint data file and generator information needed to restore a checkpoint taken by the LoopPointCheckPoint. - :param looppoint_file: a json file generated by gem5 that has all the + :param looppoint_file: a json file generated by gem5 that has all the LoopPoint data information :param checkpoint_path: the director of the checkpoint taken by the gem5 standard library looppoint_save_checkpoint_generator - + """ - + _json_file = {} - _targets = [] + _targets = [] _region_id = {} - + self.profile_restore( - looppoint_file, - checkpoint_path, - _targets, - _json_file, - _region_id + looppoint_file, checkpoint_path, _targets, _json_file, _region_id ) - + super().__init__( _targets, _region_id, @@ -357,25 +377,28 @@ def profile_restore( # finds out the region id from the directory name with open(looppoint_file_path) as file: json_file = json.load(file) - if(rid not in json_file): + if rid not in json_file: # if the region id does not exist in the LoopPoint data file # raise a fatal message fatal(f"{rid} is not a valid region\n") region = json_file[rid] - if("warmup" in region): - if("relative" not in region["simulation"]["start"]): + if "warmup" in region: + if "relative" not in region["simulation"]["start"]: # if there are not relative counts for the PC Count pair # then it means there is not enough information to restore # this checkpoint fatal(f"region {rid} doesn't have relative count info\n") - start = PcCountPair(region["simulation"]["start"]["pc"], - region["simulation"]["start"]["relative"]) - region_id[start]=rid + start = PcCountPair( + region["simulation"]["start"]["pc"], + region["simulation"]["start"]["relative"], + ) + region_id[start] = rid targets.append(start) - if("relative" not in region["simulation"]["end"]): + if "relative" not in region["simulation"]["end"]: fatal(f"region {rid} doesn't have relative count info\n") - end = PcCountPair(region["simulation"]["end"]["pc"], - region["simulation"]["end"]["relative"]) - region_id[end]=rid + end = PcCountPair( + region["simulation"]["end"]["pc"], + region["simulation"]["end"]["relative"], + ) + region_id[end] = rid targets.append(end) - \ No newline at end of file diff --git a/src/python/m5/params.py b/src/python/m5/params.py index aee8226744..a25bec42ea 100644 --- a/src/python/m5/params.py +++ b/src/python/m5/params.py @@ -852,35 +852,38 @@ def pretty_print(self, value): except TypeError: val = int(value) return "0x%x" % int(val) - + + class PcCountPair(ParamValue): # This parameter stores a Program Counter address and the a count value for # the Program Counter address cxx_type = "PcCountPair" cmd_line_settable = True - + def __init__(self, _pc, _count): self.pc = _pc self.count = _count def getPC(self): return self.pc - + def getCount(self): return self.count - + def getValue(self): from _m5.pc import PcCountPair + return PcCountPair(self.pc, self.count) - + def __str__(self): return "(%i,%i)" % (self.pc, self.count) - + def __eq__(self, other): - return (self.pc == other.pc and self.count == other.count) - + return self.pc == other.pc and self.count == other.count + def __hash__(self): - return hash((int(self.pc),int(self.count))) + return hash((int(self.pc), int(self.count))) + class AddrRange(ParamValue): cxx_type = "AddrRange" diff --git a/src/python/pybind11/core.cc b/src/python/pybind11/core.cc index 9673478c47..98a859664b 100644 --- a/src/python/pybind11/core.cc +++ b/src/python/pybind11/core.cc @@ -172,7 +172,7 @@ init_pc(py::module_ &m_native) .def(py::init()) .def("__eq__", &PcCountPair::operator==) .def("__str__", &PcCountPair::to_string) - // TODO: add __hash__ that matches the python __hash__ to enable + // TODO: add __hash__ that matches the python __hash__ to enable // hashing from the C++ class to the Python class. // Currently the C++ class and the Python class use different hashing // functions From 85a6e696ab4c2a73aa112a11d2bcd7695335b22f Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Thu, 15 Dec 2022 13:41:35 -0800 Subject: [PATCH 18/20] addded print_all_targets in debug-flags --- src/cpu/probes/pc_count_tracker_manager.cc | 2 ++ src/cpu/probes/pc_count_tracker_manager.hh | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/cpu/probes/pc_count_tracker_manager.cc b/src/cpu/probes/pc_count_tracker_manager.cc index 998df16208..0794ecf4b2 100644 --- a/src/cpu/probes/pc_count_tracker_manager.cc +++ b/src/cpu/probes/pc_count_tracker_manager.cc @@ -48,6 +48,8 @@ namespace gem5 } DPRINTF(PcCountTracker, "total %i PCs in counter\n", counter.size()); + DPRINTF(PcCountTracker, + "all targets: \n%s", print_all_targets()); } void diff --git a/src/cpu/probes/pc_count_tracker_manager.hh b/src/cpu/probes/pc_count_tracker_manager.hh index 7c6f84117a..369a6fccca 100644 --- a/src/cpu/probes/pc_count_tracker_manager.hh +++ b/src/cpu/probes/pc_count_tracker_manager.hh @@ -53,6 +53,7 @@ namespace gem5 std::unordered_map counter; // a counter that stores all the target PC addresses and the number // of times the target PC has been executed + std::unordered_set targetPair; // a set that stores all the PC Count pairs that should raise an @@ -60,9 +61,11 @@ namespace gem5 PcCountPair currentPair; // the current PC Count pair. + Tick lastTick; // the Tick when an exit event was last raised. It it used to // avoid rasing two exit event at the same Tick + bool ifListNotEmpty; // when all the PC Count pairs in the `targetPair` are encountered, // and the PCCOUNTTRACK_END exit event is raised, this boolean @@ -79,10 +82,22 @@ namespace gem5 // this function returns the corresponding value of count for the // inputted Program Counter address. If the PC address does not // exist in the counter, then it returns a -1. + PcCountPair get_current_pc_count_pair() const { return currentPair; } // this function returns the current PC Count pair + + std::string print_all_targets() const { + std::string s; + for(auto itr = targetPair.begin(); + itr != targetPair.end(); ++itr) { + s += itr->to_string(); + s += "\n"; + } + return s; + } + // this function print all targets }; } From 991c9456ffbf6ceb8b851cd1bb7bd1df14167417 Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Thu, 15 Dec 2022 16:59:42 -0800 Subject: [PATCH 19/20] removed exit event PCCOUNTTRACK_END and added exit_when_empty option for LoopPoint generator --- src/cpu/probes/pc_count_tracker_manager.cc | 20 ++++--------------- src/cpu/probes/pc_count_tracker_manager.hh | 4 ---- src/python/gem5/simulate/exit_event.py | 3 --- .../gem5/simulate/exit_event_generators.py | 19 ++++++++++++++++-- 4 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/cpu/probes/pc_count_tracker_manager.cc b/src/cpu/probes/pc_count_tracker_manager.cc index 0794ecf4b2..42a66edaf0 100644 --- a/src/cpu/probes/pc_count_tracker_manager.cc +++ b/src/cpu/probes/pc_count_tracker_manager.cc @@ -36,7 +36,6 @@ namespace gem5 { currentPair = PcCountPair(0,0); ifListNotEmpty = true; - lastTick = 0; for (int i = 0 ; i < p.targets.size() ; i++) { // initialize the counter for the inputted PC Count pair @@ -61,17 +60,9 @@ namespace gem5 if(targetPair.empty()) { // if all target PC Count pairs are encountered - if(curTick() > lastTick) { - // if the current Tick is not equal to the Tick the last - // exit event was raised - // this is used to avoid overlapping exit events - DPRINTF(PcCountTracker, - "all targets are encountered. Last Tick:%i\n", lastTick); - - exitSimLoop("reached the end of the PcCountPair list"); - // raise the PCCOUNTTRACK_END exit event - ifListNotEmpty = false; - } + DPRINTF(PcCountTracker, + "all targets are encountered.\n"); + ifListNotEmpty = false; } currentPair = PcCountPair(pc,count); @@ -79,11 +70,8 @@ namespace gem5 if(targetPair.find(currentPair) != targetPair.end()) { // if the current PC Count pair is one of the target pairs DPRINTF(PcCountTracker, - "pc:%s encountered\n", - currentPair.to_string()); + "pc:%s encountered\n", currentPair.to_string()); - lastTick = curTick(); - // record the Tick when the SIMPOINT_BEGIN exit event is raised exitSimLoopNow("simpoint starting point found"); // raise the SIMPOINT_BEGIN exit event diff --git a/src/cpu/probes/pc_count_tracker_manager.hh b/src/cpu/probes/pc_count_tracker_manager.hh index 369a6fccca..f1e4a18f7a 100644 --- a/src/cpu/probes/pc_count_tracker_manager.hh +++ b/src/cpu/probes/pc_count_tracker_manager.hh @@ -62,10 +62,6 @@ namespace gem5 PcCountPair currentPair; // the current PC Count pair. - Tick lastTick; - // the Tick when an exit event was last raised. It it used to - // avoid rasing two exit event at the same Tick - bool ifListNotEmpty; // when all the PC Count pairs in the `targetPair` are encountered, // and the PCCOUNTTRACK_END exit event is raised, this boolean diff --git a/src/python/gem5/simulate/exit_event.py b/src/python/gem5/simulate/exit_event.py index 7285427cf8..1e14fdd11a 100644 --- a/src/python/gem5/simulate/exit_event.py +++ b/src/python/gem5/simulate/exit_event.py @@ -49,7 +49,6 @@ class ExitEvent(Enum): ) SIMPOINT_BEGIN = "simpoint begins" MAX_INSTS = "number of instructions reached" - PCCOUNTTRACK_END = "pc count pair tracking ends" @classmethod def translate_exit_status(cls, exit_string: str) -> "ExitEvent": @@ -97,8 +96,6 @@ def translate_exit_status(cls, exit_string: str) -> "ExitEvent": elif exit_string.endswith("is finished updating the memory.\n"): # This is for the gups generator exit event return ExitEvent.EXIT - elif exit_string == "reached the end of the PcCountPair list": - return ExitEvent.PCCOUNTTRACK_END raise NotImplementedError( "Exit event '{}' not implemented".format(exit_string) ) diff --git a/src/python/gem5/simulate/exit_event_generators.py b/src/python/gem5/simulate/exit_event_generators.py index fd0fee9b9f..294f457189 100644 --- a/src/python/gem5/simulate/exit_event_generators.py +++ b/src/python/gem5/simulate/exit_event_generators.py @@ -171,7 +171,10 @@ def simpoints_save_checkpoint_generator( def looppoint_save_checkpoint_generator( - checkpoint_dir: Path, looppoint: LoopPoint, update_relatives: bool = True + checkpoint_dir: Path, + looppoint: LoopPoint, + update_relatives: bool = True, + exit_when_empty: bool = True, ): """ A generator for taking a checkpoint for LoopPoint. It will save the @@ -184,8 +187,17 @@ def looppoint_save_checkpoint_generator( :param update_relative: if the generator should update the relative count information in the output json file, then it should be True. It is default as True. + :param exit_when_empty: if the generator should exit the simulation loop if + all PC paris have been discovered, then it should be True. It is default as + True. """ - while True: + if exit_when_empty: + total_pairs = len(looppoint.get_targets()) + else: + total_pairs = -1 + # it will never equal to 0 if exit_when_empty is false + + while total_pairs != 0: region = looppoint.get_current_region() # if it is a significant PC Count pair, then the get_current_region() # will return an integer greater than 0. By significant PC Count pair, @@ -195,4 +207,7 @@ def looppoint_save_checkpoint_generator( if update_relatives: looppoint.update_relatives() m5.checkpoint((checkpoint_dir / f"cpt.Region{region}").as_posix()) + total_pairs -= 1 yield False + + yield True From 47d980d9de40b5208e24768ce26d58a536bae914 Mon Sep 17 00:00:00 2001 From: Jason Lowe-Power Date: Tue, 3 Jan 2023 15:51:27 -0800 Subject: [PATCH 20/20] cpu: fixup some style stuff for loopoints Change-Id: Id00174d016c8cb75ba34310761d3584c352a36bb Signed-off-by: Jason Lowe-Power --- src/base/types.hh | 4 +- src/cpu/probes/PcCountTracker.py | 10 ++- src/cpu/probes/SConscript | 16 ++--- src/cpu/probes/pc_count_tracker.cc | 61 ++++++++++--------- src/cpu/probes/pc_count_tracker.hh | 36 +++++------ src/cpu/probes/pc_count_tracker_manager.cc | 2 +- src/cpu/probes/pc_count_tracker_manager.hh | 19 ++++-- .../components/boards/se_binary_workload.py | 2 +- .../components/processors/abstract_core.py | 2 +- src/python/gem5/utils/looppoint.py | 1 - 10 files changed, 86 insertions(+), 67 deletions(-) diff --git a/src/base/types.hh b/src/base/types.hh index 24494e2cea..c7d90e8cdb 100644 --- a/src/base/types.hh +++ b/src/base/types.hh @@ -158,9 +158,9 @@ class PcCountPair PcCountPair() : pc(0), count(0) {} /** Returns the Program Counter address*/ - constexpr uint64_t getPC () const {return pc;} + constexpr uint64_t getPC() const { return pc; } /** Returns the count of the Program*/ - constexpr int getCount() const {return count;} + constexpr int getCount() const { return count; } /** Greater than comparison*/ constexpr bool diff --git a/src/cpu/probes/PcCountTracker.py b/src/cpu/probes/PcCountTracker.py index 982020f20f..78c0855cad 100644 --- a/src/cpu/probes/PcCountTracker.py +++ b/src/cpu/probes/PcCountTracker.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022 The Regents of the University of California +# Copyright (c) 2023 The Regents of the University of California # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -31,6 +31,10 @@ class PcCountTrackerManager(SimObject): + """This class manages global PC-count pair tracking. + + + """ type = "PcCountTrackerManager" cxx_header = "cpu/probes/pc_count_tracker_manager.hh" @@ -45,6 +49,10 @@ class PcCountTrackerManager(SimObject): class PcCountTracker(ProbeListenerObject): + """This probe listener tracks the number of times a particular pc has been + executed. It needs to be connected to a manager to track the global + information. + """ type = "PcCountTracker" cxx_header = "cpu/probes/pc_count_tracker.hh" diff --git a/src/cpu/probes/SConscript b/src/cpu/probes/SConscript index 8731eb376b..c96ca78a0c 100644 --- a/src/cpu/probes/SConscript +++ b/src/cpu/probes/SConscript @@ -24,12 +24,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Import('*') +Import("*") -if not env['CONF']['USE_NULL_ISA']: - SimObject('PcCountTracker.py', - sim_objects=['PcCountTracker','PcCountTrackerManager']) - Source('pc_count_tracker.cc') - Source('pc_count_tracker_manager.cc') +if not env["CONF"]["USE_NULL_ISA"]: + SimObject( + "PcCountTracker.py", + sim_objects=["PcCountTracker", "PcCountTrackerManager"], + ) + Source("pc_count_tracker.cc") + Source("pc_count_tracker_manager.cc") - DebugFlag('PcCountTracker') + DebugFlag("PcCountTracker") diff --git a/src/cpu/probes/pc_count_tracker.cc b/src/cpu/probes/pc_count_tracker.cc index 2e71bcf769..d17c79af9a 100644 --- a/src/cpu/probes/pc_count_tracker.cc +++ b/src/cpu/probes/pc_count_tracker.cc @@ -31,39 +31,40 @@ namespace gem5 { - PcCountTracker::PcCountTracker(const PcCountTrackerParams &p) + +PcCountTracker::PcCountTracker(const PcCountTrackerParams &p) : ProbeListenerObject(p), - cpuptr(p.core), - manager(p.ptmanager) - { - if (!cpuptr || !manager) { - fatal("%s is NULL", !cpuptr ? "CPU":"PcCountTrackerManager"); - } - for (int i = 0; i < p.targets.size(); i++) { - // initialize the set of targeting Program Counter addresses - targetPC.insert(p.targets[i].getPC()); - } + cpuptr(p.core), + manager(p.ptmanager) +{ + if (!cpuptr || !manager) { + fatal("%s is NULL", !cpuptr ? "CPU": "PcCountTrackerManager"); } - - void - PcCountTracker::regProbeListeners() { - - typedef ProbeListenerArg PcCountTrackerListener; - listeners.push_back(new PcCountTrackerListener(this, "RetiredInstsPC", - &PcCountTracker::check_pc)); - // connect the probe listener with the probe "RetriedInstsPC" in the - // corresponding core. - // when "RetiredInstsPC" notifies the probe listener, then the function - // 'check_pc' is automatically called + for (int i = 0; i < p.targets.size(); i++) { + // initialize the set of targeting Program Counter addresses + targetPC.insert(p.targets[i].getPC()); } +} - void - PcCountTracker::check_pc(const Addr& pc) { - if(targetPC.find(pc) != targetPC.end()) { - // if the PC is one of the target PCs, then notify the - // PcCounterTrackerManager by calling its `check_count` function - manager->check_count(pc); - } - } +void +PcCountTracker::regProbeListeners() +{ + // connect the probe listener with the probe "RetriedInstsPC" in the + // corresponding core. + // when "RetiredInstsPC" notifies the probe listener, then the function + // 'check_pc' is automatically called + typedef ProbeListenerArg PcCountTrackerListener; + listeners.push_back(new PcCountTrackerListener(this, "RetiredInstsPC", + &PcCountTracker::checkPc)); +} +void +PcCountTracker::checkPc(const Addr& pc) { + if (targetPC.find(pc) != targetPC.end()) { + // if the PC is one of the target PCs, then notify the + // PcCounterTrackerManager by calling its `check_count` function + manager->check_count(pc); + } } + +} // namespace gem5 diff --git a/src/cpu/probes/pc_count_tracker.hh b/src/cpu/probes/pc_count_tracker.hh index 9ff339324e..b2527f8a01 100644 --- a/src/cpu/probes/pc_count_tracker.hh +++ b/src/cpu/probes/pc_count_tracker.hh @@ -38,28 +38,30 @@ namespace gem5 { - class PcCountTracker : public ProbeListenerObject { - public: - PcCountTracker(const PcCountTrackerParams ¶ms); - virtual void regProbeListeners(); - // setup the probelistener +class PcCountTracker : public ProbeListenerObject +{ + public: + PcCountTracker(const PcCountTrackerParams ¶ms); + + // setup the probelistener + virtual void regProbeListeners(); - void check_pc(const Addr& pc); - // this function is called when the probelistener receives signal - // from the probe + // this function is called when the probelistener receives signal + // from the probe + void checkPc(const Addr& pc); - private: - std::unordered_set targetPC; - // a set of Program Counter addresses that should notify the - // PcCounterTrackerManager for + private: + // a set of Program Counter addresses that should notify the + // PcCounterTrackerManager for + std::unordered_set targetPC; - BaseCPU *cpuptr; - // the core this PcCountTracker is tracking at + // the core this PcCountTracker is tracking at + BaseCPU *cpuptr; - PcCountTrackerManager *manager; - // the PcCounterTrackerManager - }; + // the PcCounterTrackerManager + PcCountTrackerManager *manager; +}; } #endif // __CPU_PROBES_PC_COUNT_TRACKER_HH__ diff --git a/src/cpu/probes/pc_count_tracker_manager.cc b/src/cpu/probes/pc_count_tracker_manager.cc index 42a66edaf0..efbea378e5 100644 --- a/src/cpu/probes/pc_count_tracker_manager.cc +++ b/src/cpu/probes/pc_count_tracker_manager.cc @@ -61,7 +61,7 @@ namespace gem5 if(targetPair.empty()) { // if all target PC Count pairs are encountered DPRINTF(PcCountTracker, - "all targets are encountered.\n"); + "all targets are encountered.\n"); ifListNotEmpty = false; } diff --git a/src/cpu/probes/pc_count_tracker_manager.hh b/src/cpu/probes/pc_count_tracker_manager.hh index f1e4a18f7a..04912e145e 100644 --- a/src/cpu/probes/pc_count_tracker_manager.hh +++ b/src/cpu/probes/pc_count_tracker_manager.hh @@ -55,7 +55,7 @@ namespace gem5 // of times the target PC has been executed std::unordered_set targetPair; + PcCountPair::HashFunction> targetPair; // a set that stores all the PC Count pairs that should raise an // exit event at @@ -69,8 +69,10 @@ namespace gem5 // from functioning. This is default as true. public: - int get_pc_count(Addr pc) const { - if(counter.find(pc) != counter.end()) { + int + get_pc_count(Addr pc) const + { + if (counter.find(pc) != counter.end()) { return counter.find(pc)->second; } return -1; @@ -79,15 +81,20 @@ namespace gem5 // inputted Program Counter address. If the PC address does not // exist in the counter, then it returns a -1. - PcCountPair get_current_pc_count_pair() const { + PcCountPair + get_current_pc_count_pair() const + { return currentPair; } // this function returns the current PC Count pair - std::string print_all_targets() const { + std::string + print_all_targets() const + { std::string s; for(auto itr = targetPair.begin(); - itr != targetPair.end(); ++itr) { + itr != targetPair.end(); + ++itr) { s += itr->to_string(); s += "\n"; } diff --git a/src/python/gem5/components/boards/se_binary_workload.py b/src/python/gem5/components/boards/se_binary_workload.py index 9fa1393ed3..253e796ad2 100644 --- a/src/python/gem5/components/boards/se_binary_workload.py +++ b/src/python/gem5/components/boards/se_binary_workload.py @@ -175,7 +175,7 @@ def set_se_looppoint_workload( self, binary: AbstractResource, arguments: List[str] = [], - looppoint: Union[AbstractResource, LoopPoint] = None, + looppoint: Optional[Union[AbstractResource, LoopPoint]] = None, checkpoint: Optional[Union[Path, AbstractResource]] = None, ) -> None: """Set up the system to run a LoopPoint workload. diff --git a/src/python/gem5/components/processors/abstract_core.py b/src/python/gem5/components/processors/abstract_core.py index 4b0883320a..8259df8a8b 100644 --- a/src/python/gem5/components/processors/abstract_core.py +++ b/src/python/gem5/components/processors/abstract_core.py @@ -159,6 +159,6 @@ def _set_inst_stop_any_thread( @abstractmethod def add_pc_tracker_probe( - self, targetPair: List[PcCountPair], manager: PcCountTrackerManager + self, target_pair: List[PcCountPair], manager: PcCountTrackerManager ) -> None: raise NotImplementedError diff --git a/src/python/gem5/utils/looppoint.py b/src/python/gem5/utils/looppoint.py index 259eaeb884..8933306b42 100644 --- a/src/python/gem5/utils/looppoint.py +++ b/src/python/gem5/utils/looppoint.py @@ -40,7 +40,6 @@ class LoopPoint: """ This LoopPoint class is used to manage the information needed for LoopPoint in workload - """ def __init__(