uby: Fix checkpointing and restore
[gem5.git] / src / mem / ruby / system / System.cc
index 6aad5bd0546ad8466021a917736c399eb6a0085b..98cf50e9c5d837bb4a099f96c6e93c4b1eb7faa1 100644 (file)
@@ -30,6 +30,7 @@
 #include <zlib.h>
 
 #include <cstdio>
+#include <list>
 
 #include "base/intmath.hh"
 #include "base/statistics.hh"
@@ -38,6 +39,7 @@
 #include "mem/ruby/common/Address.hh"
 #include "mem/ruby/network/Network.hh"
 #include "mem/ruby/system/System.hh"
+#include "mem/simple_mem.hh"
 #include "sim/eventq.hh"
 #include "sim/simulate.hh"
 
@@ -48,13 +50,16 @@ bool RubySystem::m_randomization;
 uint32_t RubySystem::m_block_size_bytes;
 uint32_t RubySystem::m_block_size_bits;
 uint32_t RubySystem::m_memory_size_bits;
+bool RubySystem::m_warmup_enabled = false;
+// To look forward to allowing multiple RubySystem instances, track the number
+// of RubySystems that need to be warmed up on checkpoint restore.
+unsigned RubySystem::m_systems_to_warmup = 0;
+bool RubySystem::m_cooldown_enabled = false;
 
 RubySystem::RubySystem(const Params *p)
-    : ClockedObject(p)
+    : ClockedObject(p), m_access_backing_store(p->access_backing_store),
+      m_cache_recorder(NULL)
 {
-    if (g_system_ptr != NULL)
-        fatal("Only one RubySystem object currently allowed.\n");
-
     m_random_seed = p->random_seed;
     srandom(m_random_seed);
     m_randomization = p->randomization;
@@ -64,35 +69,29 @@ RubySystem::RubySystem(const Params *p)
     m_block_size_bits = floorLog2(m_block_size_bytes);
     m_memory_size_bits = p->memory_size_bits;
 
-    m_warmup_enabled = false;
-    m_cooldown_enabled = false;
-
-    // Setup the global variables used in Ruby
-    g_system_ptr = this;
-
     // Resize to the size of different machine types
-    g_abs_controls.resize(MachineType_NUM);
+    m_abstract_controls.resize(MachineType_NUM);
 
     // Collate the statistics before they are printed.
     Stats::registerDumpCallback(new RubyStatsCallback(this));
     // Create the profiler
-    m_profiler = new Profiler(p);
+    m_profiler = new Profiler(p, this);
     m_phys_mem = p->phys_mem;
 }
 
 void
 RubySystem::registerNetwork(Network* network_ptr)
 {
-  m_network = network_ptr;
+    m_network = network_ptr;
 }
 
 void
 RubySystem::registerAbstractController(AbstractController* cntrl)
 {
-  m_abs_cntrl_vec.push_back(cntrl);
+    m_abs_cntrl_vec.push_back(cntrl);
 
-  MachineID id = cntrl->getMachineID();
-  g_abs_controls[id.getType()][id.getNum()] = cntrl;
+    MachineID id = cntrl->getMachineID();
+    m_abstract_controls[id.getType()][id.getNum()] = cntrl;
 }
 
 RubySystem::~RubySystem()
@@ -102,38 +101,10 @@ RubySystem::~RubySystem()
 }
 
 void
-RubySystem::writeCompressedTrace(uint8_t *raw_data, string filename,
-                                 uint64 uncompressed_trace_size)
+RubySystem::makeCacheRecorder(uint8_t *uncompressed_trace,
+                              uint64 cache_trace_size,
+                              uint64 block_size_bytes)
 {
-    // Create the checkpoint file for the memory
-    string thefile = Checkpoint::dir() + "/" + filename.c_str();
-
-    int fd = creat(thefile.c_str(), 0664);
-    if (fd < 0) {
-        perror("creat");
-        fatal("Can't open memory trace file '%s'\n", filename);
-    }
-
-    gzFile compressedMemory = gzdopen(fd, "wb");
-    if (compressedMemory == NULL)
-        fatal("Insufficient memory to allocate compression state for %s\n",
-              filename);
-
-    if (gzwrite(compressedMemory, raw_data, uncompressed_trace_size) !=
-        uncompressed_trace_size) {
-        fatal("Write failed on memory trace file '%s'\n", filename);
-    }
-
-    if (gzclose(compressedMemory)) {
-        fatal("Close failed on memory trace file '%s'\n", filename);
-    }
-    delete[] raw_data;
-}
-
-void
-RubySystem::serialize(std::ostream &os)
-{
-    m_cooldown_enabled = true;
     vector<Sequencer*> sequencer_map;
     Sequencer* sequencer_ptr = NULL;
 
@@ -152,28 +123,46 @@ RubySystem::serialize(std::ostream &os)
         }
     }
 
-    // Store the cache-block size, so we are able to restore on systems with a
-    // different cache-block size. CacheRecorder depends on the correct
-    // cache-block size upon unserializing.
-    uint64 block_size_bytes = getBlockSizeBytes();
-    SERIALIZE_SCALAR(block_size_bytes);
+    // Remove the old CacheRecorder if it's still hanging about.
+    if (m_cache_recorder != NULL) {
+        delete m_cache_recorder;
+    }
 
-    DPRINTF(RubyCacheTrace, "Recording Cache Trace\n");
     // Create the CacheRecorder and record the cache trace
-    m_cache_recorder = new CacheRecorder(NULL, 0, sequencer_map,
-                                         block_size_bytes);
+    m_cache_recorder = new CacheRecorder(uncompressed_trace, cache_trace_size,
+                                         sequencer_map, block_size_bytes);
+}
 
+void
+RubySystem::memWriteback()
+{
+    m_cooldown_enabled = true;
+
+    // Make the trace so we know what to write back.
+    DPRINTF(RubyCacheTrace, "Recording Cache Trace\n");
+    makeCacheRecorder(NULL, 0, getBlockSizeBytes());
     for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) {
         m_abs_cntrl_vec[cntrl]->recordCacheTrace(cntrl, m_cache_recorder);
     }
-
     DPRINTF(RubyCacheTrace, "Cache Trace Complete\n");
+
     // save the current tick value
     Tick curtick_original = curTick();
-    // save the event queue head
-    Event* eventq_head = eventq->replaceHead(NULL);
-    DPRINTF(RubyCacheTrace, "Recording current tick %ld and event queue\n",
-            curtick_original);
+    DPRINTF(RubyCacheTrace, "Recording current tick %ld\n", curtick_original);
+
+    // Deschedule all prior events on the event queue, but record the tick they
+    // were scheduled at so they can be restored correctly later.
+    list<pair<Event*, Tick> > original_events;
+    while (!eventq->empty()) {
+        Event *curr_head = eventq->getHead();
+        if (curr_head->isAutoDelete()) {
+            DPRINTF(RubyCacheTrace, "Event %s auto-deletes when descheduled,"
+                    " not recording\n", curr_head->name());
+        } else {
+            original_events.push_back(make_pair(curr_head, curr_head->when()));
+        }
+        eventq->deschedule(curr_head);
+    }
 
     // Schedule an event to start cache cooldown
     DPRINTF(RubyCacheTrace, "Starting cache flush\n");
@@ -181,12 +170,87 @@ RubySystem::serialize(std::ostream &os)
     simulate();
     DPRINTF(RubyCacheTrace, "Cache flush complete\n");
 
-    // Restore eventq head
-    eventq_head = eventq->replaceHead(eventq_head);
+    // Deschedule any events left on the event queue.
+    while (!eventq->empty()) {
+        eventq->deschedule(eventq->getHead());
+    }
+
     // Restore curTick
     setCurTick(curtick_original);
 
-    // Aggergate the trace entries together into a single array
+    // Restore all events that were originally on the event queue.  This is
+    // done after setting curTick back to its original value so that events do
+    // not seem to be scheduled in the past.
+    while (!original_events.empty()) {
+        pair<Event*, Tick> event = original_events.back();
+        eventq->schedule(event.first, event.second);
+        original_events.pop_back();
+    }
+
+    // No longer flushing back to memory.
+    m_cooldown_enabled = false;
+
+    // There are several issues with continuing simulation after calling
+    // memWriteback() at the moment, that stem from taking events off the
+    // queue, simulating again, and then putting them back on, whilst
+    // pretending that no time has passed.  One is that some events will have
+    // been deleted, so can't be put back.  Another is that any object
+    // recording the tick something happens may end up storing a tick in the
+    // future.  A simple warning here alerts the user that things may not work
+    // as expected.
+    warn_once("Ruby memory writeback is experimental.  Continuing simulation "
+              "afterwards may not always work as intended.");
+
+    // Keep the cache recorder around so that we can dump the trace if a
+    // checkpoint is immediately taken.
+}
+
+void
+RubySystem::writeCompressedTrace(uint8_t *raw_data, string filename,
+                                 uint64 uncompressed_trace_size)
+{
+    // Create the checkpoint file for the memory
+    string thefile = CheckpointIn::dir() + "/" + filename.c_str();
+
+    int fd = creat(thefile.c_str(), 0664);
+    if (fd < 0) {
+        perror("creat");
+        fatal("Can't open memory trace file '%s'\n", filename);
+    }
+
+    gzFile compressedMemory = gzdopen(fd, "wb");
+    if (compressedMemory == NULL)
+        fatal("Insufficient memory to allocate compression state for %s\n",
+              filename);
+
+    if (gzwrite(compressedMemory, raw_data, uncompressed_trace_size) !=
+        uncompressed_trace_size) {
+        fatal("Write failed on memory trace file '%s'\n", filename);
+    }
+
+    if (gzclose(compressedMemory)) {
+        fatal("Close failed on memory trace file '%s'\n", filename);
+    }
+    delete[] raw_data;
+}
+
+void
+RubySystem::serializeOld(CheckpointOut &cp)
+{
+    // Store the cache-block size, so we are able to restore on systems with a
+    // different cache-block size. CacheRecorder depends on the correct
+    // cache-block size upon unserializing.
+    uint64 block_size_bytes = getBlockSizeBytes();
+    SERIALIZE_SCALAR(block_size_bytes);
+
+    // Check that there's a valid trace to use.  If not, then memory won't be
+    // up-to-date and the simulation will probably fail when restoring from the
+    // checkpoint.
+    if (m_cache_recorder == NULL) {
+        fatal("Call memWriteback() before serialize() to create ruby trace");
+    }
+
+    // Aggregate the trace entries together into a single array
     uint8_t *raw_data = new uint8_t[4096];
     uint64 cache_trace_size = m_cache_recorder->aggregateRecords(&raw_data,
                                                                  4096);
@@ -196,7 +260,9 @@ RubySystem::serialize(std::ostream &os)
     SERIALIZE_SCALAR(cache_trace_file);
     SERIALIZE_SCALAR(cache_trace_size);
 
-    m_cooldown_enabled = false;
+    // Now finished with the cache recorder.
+    delete m_cache_recorder;
+    m_cache_recorder = NULL;
 }
 
 void
@@ -231,7 +297,7 @@ RubySystem::readCompressedTrace(string filename, uint8_t *&raw_data,
 }
 
 void
-RubySystem::unserialize(Checkpoint *cp, const string &section)
+RubySystem::unserialize(CheckpointIn &cp)
 {
     uint8_t *uncompressed_trace = NULL;
 
@@ -246,29 +312,15 @@ RubySystem::unserialize(Checkpoint *cp, const string &section)
 
     UNSERIALIZE_SCALAR(cache_trace_file);
     UNSERIALIZE_SCALAR(cache_trace_size);
-    cache_trace_file = cp->cptDir + "/" + cache_trace_file;
+    cache_trace_file = cp.cptDir + "/" + cache_trace_file;
 
     readCompressedTrace(cache_trace_file, uncompressed_trace,
                         cache_trace_size);
     m_warmup_enabled = true;
+    m_systems_to_warmup++;
 
-    vector<Sequencer*> sequencer_map;
-    Sequencer* t = NULL;
-    for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) {
-        sequencer_map.push_back(m_abs_cntrl_vec[cntrl]->getSequencer());
-        if (t == NULL) t = sequencer_map[cntrl];
-    }
-
-    assert(t != NULL);
-
-    for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) {
-        if (sequencer_map[cntrl] == NULL) {
-            sequencer_map[cntrl] = t;
-        }
-    }
-
-    m_cache_recorder = new CacheRecorder(uncompressed_trace, cache_trace_size,
-                                         sequencer_map, block_size_bytes);
+    // Create the cache recorder that will hang around until startup.
+    makeCacheRecorder(uncompressed_trace, cache_trace_size, block_size_bytes);
 }
 
 void
@@ -292,6 +344,7 @@ RubySystem::startup()
     // state was checkpointed.
 
     if (m_warmup_enabled) {
+        DPRINTF(RubyCacheTrace, "Starting ruby cache warmup\n");
         // save the current tick value
         Tick curtick_original = curTick();
         // save the event queue head
@@ -306,7 +359,10 @@ RubySystem::startup()
 
         delete m_cache_recorder;
         m_cache_recorder = NULL;
-        m_warmup_enabled = false;
+        m_systems_to_warmup--;
+        if (m_systems_to_warmup == 0) {
+            m_warmup_enabled = false;
+        }
 
         // Restore eventq head
         eventq_head = eventq->replaceHead(eventq_head);
@@ -321,17 +377,17 @@ RubySystem::startup()
 void
 RubySystem::RubyEvent::process()
 {
-    if (ruby_system->m_warmup_enabled) {
-        ruby_system->m_cache_recorder->enqueueNextFetchRequest();
-    }  else if (ruby_system->m_cooldown_enabled) {
-        ruby_system->m_cache_recorder->enqueueNextFlushRequest();
+    if (RubySystem::getWarmupEnabled()) {
+        m_ruby_system->m_cache_recorder->enqueueNextFetchRequest();
+    } else if (RubySystem::getCooldownEnabled()) {
+        m_ruby_system->m_cache_recorder->enqueueNextFlushRequest();
     }
 }
 
 void
 RubySystem::resetStats()
 {
-    g_ruby_start = curCycle();
+    m_start_cycle = curCycle();
 }
 
 bool