From f823f6ba6fad39b34423ba062c9f732ccfd98648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciro=20Santilli=20=E5=85=AD=E5=9B=9B=E4=BA=8B=E4=BB=B6=20?= =?UTF-8?q?=E6=B3=95=E8=BD=AE=E5=8A=9F?= Date: Tue, 25 Feb 2020 00:00:00 +0000 Subject: [PATCH] gem5: further understand the entry point --- README.adoc | 112 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 5 deletions(-) diff --git a/README.adoc b/README.adoc index 34849ae..25c94c4 100644 --- a/README.adoc +++ b/README.adoc @@ -12092,7 +12092,7 @@ Each node has: * a list of child nodes, e.g. `system` is a child of `root`, and both `cpu` and `cpu_clk_domain` are children of `system` * a list of parameters, e.g. `system.semihosting` is `Null`, which means that <> was turned off -** the `type` parameter shows is present on every node, and it maps to a `Python` object that inherits from `SimObject`. + ** the `type` parameter shows is present on every node, and it maps to a `Python` object that inherits from <>. + For example, `AtomicSimpleCPU` maps is defined at https://github.com/gem5/gem5/blob/05c4c2b566ce351ab217b2bd7035562aa7a76570/src/cpu/simple/AtomicSimpleCPU.py#L45[src/cpu/simple/AtomicSimpleCPU.py]. @@ -12725,7 +12725,7 @@ To run and GDB step debug the executable, just copy the full command line from t ==== gem5 Python C++ interaction -The interaction uses the Python C extension interface https://docs.python.org/2/extending/extending.html interface through the pybind11 helper library: https://github.com/pybind/pybind11 +The interaction uses the Python C extension interface https://docs.python.org/2/extending/extending.html interface through the <> helper library: https://github.com/pybind/pybind11 The C++ executable both: @@ -12737,7 +12737,7 @@ An example of this can be found at: * https://docs.python.org/2/extending/embedding.html#extending-embedded-python * https://github.com/pybind/pybind11/tree/v2.2.3/tests/test_embed -then gem5 magic `simobject` class adds some crazy stuff on top of it further... is is a mess. in particular, it auto generates `params/` headers. TODO: why is this mess needed at all? pybind11 seems to handle constructor arguments just fine: +then gem5 magic `SimObject` class adds some crazy stuff on top of it further, is is a mess. In particular, it auto generates `params/` headers. TODO: why is this mess needed at all? pybind11 seems to handle constructor arguments just fine: * https://github.com/pybind/pybind11/blob/v2.2.3/tests/test_class.py#L77 * https://github.com/pybind/pybind11/blob/v2.2.3/tests/test_class.cpp#L41 @@ -12845,6 +12845,10 @@ BadDevice::BadDevice(Params *p) } .... +It has been found that this usage of pybind11 across hundreds of `SimObject` files accounted for 50% of the gem5 build time at one point: https://gem5.atlassian.net/browse/GEM5-366 + +To get a feeling of how `SimObject` objects are run, see: <>. + Tested on gem5 08c79a194d1a3430801c04f37d13216cc9ec1da3. ==== gem5 entry point @@ -12911,7 +12915,7 @@ The Python config files then set the entire system up in Python, and finally cal src/sim/simulate.cc .... -and that is where doSimLoop the main event loop, `doSimLoop` gets called and starts kicking off the <>. +and that is where the main event loop, `doSimLoop`, gets called and starts kicking off the <>. Tested at gem5 b4879ae5b0b6644e6836b0881e4da05c64a6550d. @@ -13035,7 +13039,7 @@ schedule(tickEvent, clockEdge(Cycles(0))); which calls `EventManager::schedule`. -`AtomicSimpleCPU` is an `EventManager` because `SimObject` inherits from it. +`AtomicSimpleCPU` is an `EventManager` because <> inherits from it. `tickEvent` is an `EventFunctionWrapper` which contains a `std::function callback;`, and is initialized in the constructor as: @@ -13044,6 +13048,98 @@ tickEvent([this]{ tick(); }, "AtomicSimpleCPU tick", false, Event::CPU_Tick_Pri), .... +The call stack above `ArmLinuxProcess64::initState` is <> fuzziness, but if we grep a bit we find the Python call point: + +src/python/m5/simulate.py + +.... +def instantiate(ckpt_dir=None): + + ... + + # Create the C++ sim objects and connect ports + for obj in root.descendants(): obj.createCCObject() + for obj in root.descendants(): obj.connectPorts() + + # Do a second pass to finish initializing the sim objects + for obj in root.descendants(): obj.init() + + ... + + # Restore checkpoint (if any) + if ckpt_dir: + ... + else: + for obj in root.descendants(): obj.initState() +.... + +As we can see, `initState` is just one stage of generic `SimObject` initialization. `root.descendants()` goes over the entire `SimObject` tree calling `initState()`. + +Finally, we see that `initState` is part of the `SimObject` C++ API: + +src/sim/sim_object.hh + +.... +class SimObject : public EventManager, public Serializable, public Drainable, + public Stats::Group +{ + + ... + + /** + * initState() is called on each SimObject when *not* restoring + * from a checkpoint. This provides a hook for state + * initializations that are only required for a "cold start". + */ + virtual void initState(); +.... + +Finally, we see that `initState` is exposed to the Python API at: + +build/ARM/python/_m5/param_SimObject.cc + +.... +module_init(py::module &m_internal) +{ + py::module m = m_internal.def_submodule("param_SimObject"); + py::class_>(m, "SimObjectParams") + .def_readwrite("name", &SimObjectParams::name) + .def_readwrite("eventq_index", &SimObjectParams::eventq_index) + ; + + py::class_>(m, "SimObject") + .def("init", &SimObject::init) + .def("initState", &SimObject::initState) + .def("memInvalidate", &SimObject::memInvalidate) + .def("memWriteback", &SimObject::memWriteback) + .def("regProbePoints", &SimObject::regProbePoints) + .def("regProbeListeners", &SimObject::regProbeListeners) + .def("startup", &SimObject::startup) + .def("loadState", &SimObject::loadState, py::arg("cp")) + .def("getPort", &SimObject::getPort, pybind11::return_value_policy::reference, py::arg("if_name"), py::arg("idx")) + ; + +} +.... + +which is more magical than the other param classes since `py::class_