sim, arch, base: Refactor the base remote GDB class.
[gem5.git] / src / sim / drain.cc
index 90fb0c18dc331f99e3c05999965634bf7f051d67..4d46306c3f350a75ed46f381e8fdacd4fe6a981c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015 ARM Limited
+ * Copyright (c) 2012, 2015, 2017 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
  */
 
 #include "sim/drain.hh"
+
+#include <algorithm>
+
+#include "base/logging.hh"
+#include "base/trace.hh"
+#include "debug/Drain.hh"
 #include "sim/sim_exit.hh"
+#include "sim/sim_object.hh"
+
+DrainManager DrainManager::_instance;
 
 DrainManager::DrainManager()
-    : _count(0)
+    : _count(0),
+      _state(DrainState::Running)
 {
 }
 
@@ -49,25 +59,170 @@ DrainManager::~DrainManager()
 {
 }
 
+bool
+DrainManager::tryDrain()
+{
+    panic_if(_state == DrainState::Drained,
+             "Trying to drain a drained system\n");
+
+    panic_if(_count != 0,
+             "Drain counter must be zero at the start of a drain cycle\n");
+
+    DPRINTF(Drain, "Trying to drain %u objects.\n", drainableCount());
+    _state = DrainState::Draining;
+    for (auto *obj : _allDrainable) {
+        DrainState status = obj->dmDrain();
+        if (DTRACE(Drain) && status != DrainState::Drained) {
+            SimObject *temp = dynamic_cast<SimObject*>(obj);
+            if (temp)
+                DPRINTF(Drain, "Failed to drain %s\n", temp->name());
+        }
+        _count += status == DrainState::Drained ? 0 : 1;
+    }
+
+    if (_count == 0) {
+        DPRINTF(Drain, "Drain done.\n");
+        _state = DrainState::Drained;
+        return true;
+    } else {
+        DPRINTF(Drain, "Need another drain cycle. %u/%u objects not ready.\n",
+                _count, drainableCount());
+        return false;
+    }
+}
+
 void
-DrainManager::drainCycleDone()
+DrainManager::resume()
 {
-    exitSimLoop("Finished drain", 0);
+    panic_if(_state == DrainState::Running,
+             "Trying to resume a system that is already running\n");
+
+    warn_if(_state == DrainState::Draining,
+            "Resuming a system that isn't fully drained, this is untested and "
+            "likely to break\n");
+
+    panic_if(_state == DrainState::Resuming,
+             "Resuming a system that is already trying to resume. This should "
+             "never happen.\n");
+
+    panic_if(_count != 0,
+             "Resume called in the middle of a drain cycle. %u objects "
+             "left to drain.\n", _count);
+
+    // At this point in time the DrainManager and all objects will be
+    // in the the Drained state. New objects (i.e., objects created
+    // while resuming) will inherit the Resuming state from the
+    // DrainManager, which means we have to resume objects until all
+    // objects are in the Running state.
+    _state = DrainState::Resuming;
+
+    do {
+        DPRINTF(Drain, "Resuming %u objects.\n", drainableCount());
+        for (auto *obj : _allDrainable) {
+            if (obj->drainState() != DrainState::Running) {
+                assert(obj->drainState() == DrainState::Drained ||
+                       obj->drainState() == DrainState::Resuming);
+                obj->dmDrainResume();
+            }
+        }
+    } while (!allInState(DrainState::Running));
+
+    _state = DrainState::Running;
+}
+
+void
+DrainManager::preCheckpointRestore()
+{
+    panic_if(_state != DrainState::Running,
+             "preCheckpointRestore() called on a system that isn't in the "
+             "Running state.\n");
+
+    DPRINTF(Drain, "Applying pre-restore fixes to %u objects.\n",
+            drainableCount());
+    _state = DrainState::Drained;
+    for (auto *obj : _allDrainable)
+        obj->_drainState = DrainState::Drained;
+}
+
+void
+DrainManager::signalDrainDone()
+{
+    assert(_count > 0);
+    if (--_count == 0) {
+        DPRINTF(Drain, "All %u objects drained..\n", drainableCount());
+        exitSimLoop("Finished drain", 0);
+    }
+}
+
+
+void
+DrainManager::registerDrainable(Drainable *obj)
+{
+    std::lock_guard<std::mutex> lock(globalLock);
+    assert(std::find(_allDrainable.begin(), _allDrainable.end(), obj) ==
+           _allDrainable.end());
+    _allDrainable.push_back(obj);
+}
+
+void
+DrainManager::unregisterDrainable(Drainable *obj)
+{
+    std::lock_guard<std::mutex> lock(globalLock);
+    auto o = std::find(_allDrainable.begin(), _allDrainable.end(), obj);
+    assert(o != _allDrainable.end());
+    _allDrainable.erase(o);
+}
+
+bool
+DrainManager::allInState(DrainState state) const
+{
+    for (const auto *obj : _allDrainable) {
+        if (obj->drainState() != state)
+            return false;
+    }
+
+    return true;
+}
+
+size_t
+DrainManager::drainableCount() const
+{
+    std::lock_guard<std::mutex> lock(globalLock);
+    return _allDrainable.size();
 }
 
 
 
 Drainable::Drainable()
-    : _drainState(DrainState::Running)
+    : _drainManager(DrainManager::instance()),
+      _drainState(_drainManager.state())
 {
+    _drainManager.registerDrainable(this);
 }
 
 Drainable::~Drainable()
 {
+    _drainManager.unregisterDrainable(this);
+}
+
+DrainState
+Drainable::dmDrain()
+{
+    _drainState = DrainState::Draining;
+    _drainState = drain();
+    assert(_drainState == DrainState::Draining ||
+           _drainState == DrainState::Drained);
+
+    return _drainState;
 }
 
 void
-Drainable::drainResume()
+Drainable::dmDrainResume()
 {
+    panic_if(_drainState != DrainState::Drained &&
+             _drainState != DrainState::Resuming,
+             "Trying to resume an object that hasn't been drained\n");
+
     _drainState = DrainState::Running;
+    drainResume();
 }