| /* |
| * Copyright (c) 2015, 2018 ARM Limited |
| * All rights reserved |
| * |
| * The license below extends only to copyright in the software and shall |
| * not be construed as granting a license to any other intellectual |
| * property including but not limited to intellectual property relating |
| * to a hardware implementation of the functionality of the software |
| * licensed hereunder. You may use the software subject to the license |
| * terms below provided that you ensure that this notice is replicated |
| * unmodified and in its entirety in all distributions of the software, |
| * modified or unmodified, in source code or in binary form. |
| * |
| * Copyright (c) 2002-2005 The Regents of The University of Michigan |
| * 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. |
| * |
| * Authors: Nathan Binkert |
| * Erik Hallnor |
| * Steve Reinhardt |
| * Andreas Sandberg |
| */ |
| |
| /* @file |
| * Serialization Interface Declarations |
| */ |
| |
| #ifndef __SERIALIZE_HH__ |
| #define __SERIALIZE_HH__ |
| |
| |
| #include <algorithm> |
| #include <iostream> |
| #include <list> |
| #include <map> |
| #include <stack> |
| #include <set> |
| #include <vector> |
| |
| #include "base/bitunion.hh" |
| #include "base/logging.hh" |
| #include "base/str.hh" |
| |
| class IniFile; |
| class SimObject; |
| class SimObjectResolver; |
| |
| typedef std::ostream CheckpointOut; |
| |
| class CheckpointIn |
| { |
| private: |
| |
| IniFile *db; |
| |
| SimObjectResolver &objNameResolver; |
| |
| public: |
| CheckpointIn(const std::string &cpt_dir, SimObjectResolver &resolver); |
| ~CheckpointIn(); |
| |
| const std::string cptDir; |
| |
| bool find(const std::string §ion, const std::string &entry, |
| std::string &value); |
| |
| bool findObj(const std::string §ion, const std::string &entry, |
| SimObject *&value); |
| |
| |
| bool entryExists(const std::string §ion, const std::string &entry); |
| bool sectionExists(const std::string §ion); |
| |
| // The following static functions have to do with checkpoint |
| // creation rather than restoration. This class makes a handy |
| // namespace for them though. Currently no Checkpoint object is |
| // created on serialization (only unserialization) so we track the |
| // directory name as a global. It would be nice to change this |
| // someday |
| |
| private: |
| // current directory we're serializing into. |
| static std::string currentDirectory; |
| |
| public: |
| // Set the current directory. This function takes care of |
| // inserting curTick() if there's a '%d' in the argument, and |
| // appends a '/' if necessary. The final name is returned. |
| static std::string setDir(const std::string &base_name); |
| |
| // Export current checkpoint directory name so other objects can |
| // derive filenames from it (e.g., memory). The return value is |
| // guaranteed to end in '/' so filenames can be directly appended. |
| // This function is only valid while a checkpoint is being created. |
| static std::string dir(); |
| |
| // Filename for base checkpoint file within directory. |
| static const char *baseFilename; |
| }; |
| |
| /** |
| * Basic support for object serialization. |
| * |
| * Objects that support serialization should derive from this |
| * class. Such objects can largely be divided into two categories: 1) |
| * True SimObjects (deriving from SimObject), and 2) child objects |
| * (non-SimObjects). |
| * |
| * SimObjects are serialized automatically into their own sections |
| * automatically by the SimObject base class (see |
| * SimObject::serializeAll(). |
| * |
| * SimObjects can contain other serializable objects that are not |
| * SimObjects. Much like normal serialized members are not serialized |
| * automatically, these objects will not be serialized automatically |
| * and it is expected that the objects owning such serializable |
| * objects call the required serialization/unserialization methods on |
| * child objects. The preferred method to serialize a child object is |
| * to call serializeSection() on the child, which serializes the |
| * object into a new subsection in the current section. Another option |
| * is to call serialize() directly, which serializes the object into |
| * the current section. The latter is not recommended as it can lead |
| * to naming clashes between objects. |
| * |
| * @note Many objects that support serialization need to be put in a |
| * consistent state when serialization takes place. We refer to the |
| * action of forcing an object into a consistent state as |
| * 'draining'. Objects that need draining inherit from Drainable. See |
| * Drainable for more information. |
| */ |
| class Serializable |
| { |
| protected: |
| /** |
| * Scoped checkpoint section helper class |
| * |
| * This helper class creates a section within a checkpoint without |
| * the need for a separate serializeable object. It is mainly used |
| * within the Serializable class when serializing or unserializing |
| * section (see serializeSection() and unserializeSection()). It |
| * can also be used to maintain backwards compatibility in |
| * existing code that serializes structs that are not inheriting |
| * from Serializable into subsections. |
| * |
| * When the class is instantiated, it appends a name to the active |
| * path in a checkpoint. The old path is later restored when the |
| * instance is destroyed. For example, serializeSection() could be |
| * implemented by instantiating a ScopedCheckpointSection and then |
| * calling serialize() on an object. |
| */ |
| class ScopedCheckpointSection { |
| public: |
| template<class CP> |
| ScopedCheckpointSection(CP &cp, const char *name) { |
| pushName(name); |
| nameOut(cp); |
| } |
| |
| template<class CP> |
| ScopedCheckpointSection(CP &cp, const std::string &name) { |
| pushName(name.c_str()); |
| nameOut(cp); |
| } |
| |
| ~ScopedCheckpointSection(); |
| |
| ScopedCheckpointSection() = delete; |
| ScopedCheckpointSection(const ScopedCheckpointSection &) = delete; |
| ScopedCheckpointSection &operator=( |
| const ScopedCheckpointSection &) = delete; |
| ScopedCheckpointSection &operator=( |
| ScopedCheckpointSection &&) = delete; |
| |
| private: |
| void pushName(const char *name); |
| void nameOut(CheckpointOut &cp); |
| void nameOut(CheckpointIn &cp) {}; |
| }; |
| |
| public: |
| Serializable(); |
| virtual ~Serializable(); |
| |
| /** |
| * Serialize an object |
| * |
| * Output an object's state into the current checkpoint section. |
| * |
| * @param cp Checkpoint state |
| */ |
| virtual void serialize(CheckpointOut &cp) const = 0; |
| |
| /** |
| * Unserialize an object |
| * |
| * Read an object's state from the current checkpoint section. |
| * |
| * @param cp Checkpoint state |
| */ |
| virtual void unserialize(CheckpointIn &cp) = 0; |
| |
| /** |
| * Serialize an object into a new section |
| * |
| * This method creates a new section in a checkpoint and calls |
| * serialize() to serialize the current object into that |
| * section. The name of the section is appended to the current |
| * checkpoint path. |
| * |
| * @param cp Checkpoint state |
| * @param name Name to append to the active path |
| */ |
| void serializeSection(CheckpointOut &cp, const char *name) const; |
| |
| void serializeSection(CheckpointOut &cp, const std::string &name) const { |
| serializeSection(cp, name.c_str()); |
| } |
| |
| /** |
| * Unserialize an a child object |
| * |
| * This method loads a child object from a checkpoint. The object |
| * name is appended to the active path to form a fully qualified |
| * section name and unserialize() is called. |
| * |
| * @param cp Checkpoint state |
| * @param name Name to append to the active path |
| */ |
| void unserializeSection(CheckpointIn &cp, const char *name); |
| |
| void unserializeSection(CheckpointIn &cp, const std::string &name) { |
| unserializeSection(cp, name.c_str()); |
| } |
| |
| /** Get the fully-qualified name of the active section */ |
| static const std::string ¤tSection(); |
| |
| static int ckptCount; |
| static int ckptMaxCount; |
| static int ckptPrevCount; |
| static void serializeAll(const std::string &cpt_dir); |
| static void unserializeGlobals(CheckpointIn &cp); |
| |
| private: |
| static std::stack<std::string> path; |
| }; |
| |
| // |
| // The base implementations use to_number for parsing and '<<' for |
| // displaying, suitable for integer types. |
| // |
| template <class T> |
| bool |
| parseParam(const std::string &s, T &value) |
| { |
| return to_number(s, value); |
| } |
| |
| template <class T> |
| void |
| showParam(CheckpointOut &os, const T &value) |
| { |
| os << value; |
| } |
| |
| template <class T> |
| bool |
| parseParam(const std::string &s, BitUnionType<T> &value) |
| { |
| // Zero initialize storage to avoid leaking an uninitialized value |
| BitUnionBaseType<T> storage = BitUnionBaseType<T>(); |
| auto res = to_number(s, storage); |
| value = storage; |
| return res; |
| } |
| |
| template <class T> |
| void |
| showParam(CheckpointOut &os, const BitUnionType<T> &value) |
| { |
| auto storage = static_cast<BitUnionBaseType<T>>(value); |
| |
| // For a BitUnion8, the storage type is an unsigned char. |
| // Since we want to serialize a number we need to cast to |
| // unsigned int |
| os << ((sizeof(storage) == 1) ? |
| static_cast<unsigned int>(storage) : storage); |
| } |
| |
| // Treat 8-bit ints (chars) as ints on output, not as chars |
| template <> |
| inline void |
| showParam(CheckpointOut &os, const char &value) |
| { |
| os << (int)value; |
| } |
| |
| template <> |
| inline void |
| showParam(CheckpointOut &os, const signed char &value) |
| { |
| os << (int)value; |
| } |
| |
| template <> |
| inline void |
| showParam(CheckpointOut &os, const unsigned char &value) |
| { |
| os << (unsigned int)value; |
| } |
| |
| template <> |
| inline bool |
| parseParam(const std::string &s, float &value) |
| { |
| return to_number(s, value); |
| } |
| |
| template <> |
| inline bool |
| parseParam(const std::string &s, double &value) |
| { |
| return to_number(s, value); |
| } |
| |
| template <> |
| inline bool |
| parseParam(const std::string &s, bool &value) |
| { |
| return to_bool(s, value); |
| } |
| |
| // Display bools as strings |
| template <> |
| inline void |
| showParam(CheckpointOut &os, const bool &value) |
| { |
| os << (value ? "true" : "false"); |
| } |
| |
| // String requires no processing to speak of |
| template <> |
| inline bool |
| parseParam(const std::string &s, std::string &value) |
| { |
| value = s; |
| return true; |
| } |
| |
| template <class T> |
| void |
| paramOut(CheckpointOut &os, const std::string &name, const T ¶m) |
| { |
| os << name << "="; |
| showParam(os, param); |
| os << "\n"; |
| } |
| |
| template <class T> |
| void |
| paramIn(CheckpointIn &cp, const std::string &name, T ¶m) |
| { |
| const std::string §ion(Serializable::currentSection()); |
| std::string str; |
| if (!cp.find(section, name, str) || !parseParam(str, param)) { |
| fatal("Can't unserialize '%s:%s'\n", section, name); |
| } |
| } |
| |
| template <class T> |
| bool |
| optParamIn(CheckpointIn &cp, const std::string &name, |
| T ¶m, bool warn = true) |
| { |
| const std::string §ion(Serializable::currentSection()); |
| std::string str; |
| if (!cp.find(section, name, str) || !parseParam(str, param)) { |
| if (warn) |
| warn("optional parameter %s:%s not present\n", section, name); |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| template <class T> |
| void |
| arrayParamOut(CheckpointOut &os, const std::string &name, |
| const std::vector<T> ¶m) |
| { |
| typename std::vector<T>::size_type size = param.size(); |
| os << name << "="; |
| if (size > 0) |
| showParam(os, param[0]); |
| for (typename std::vector<T>::size_type i = 1; i < size; ++i) { |
| os << " "; |
| showParam(os, param[i]); |
| } |
| os << "\n"; |
| } |
| |
| template <class T> |
| void |
| arrayParamOut(CheckpointOut &os, const std::string &name, |
| const std::list<T> ¶m) |
| { |
| typename std::list<T>::const_iterator it = param.begin(); |
| |
| os << name << "="; |
| if (param.size() > 0) |
| showParam(os, *it); |
| it++; |
| while (it != param.end()) { |
| os << " "; |
| showParam(os, *it); |
| it++; |
| } |
| os << "\n"; |
| } |
| |
| template <class T> |
| void |
| arrayParamOut(CheckpointOut &os, const std::string &name, |
| const std::set<T> ¶m) |
| { |
| typename std::set<T>::const_iterator it = param.begin(); |
| |
| os << name << "="; |
| if (param.size() > 0) |
| showParam(os, *it); |
| it++; |
| while (it != param.end()) { |
| os << " "; |
| showParam(os, *it); |
| it++; |
| } |
| os << "\n"; |
| } |
| |
| template <class T> |
| void |
| arrayParamOut(CheckpointOut &os, const std::string &name, |
| const T *param, unsigned size) |
| { |
| os << name << "="; |
| if (size > 0) |
| showParam(os, param[0]); |
| for (unsigned i = 1; i < size; ++i) { |
| os << " "; |
| showParam(os, param[i]); |
| } |
| os << "\n"; |
| } |
| |
| /** |
| * Extract values stored in the checkpoint, and assign them to the provided |
| * array container. |
| * |
| * @param cp The checkpoint to be parsed. |
| * @param name Name of the container. |
| * @param param The array container. |
| * @param size The expected number of entries to be extracted. |
| */ |
| template <class T> |
| void |
| arrayParamIn(CheckpointIn &cp, const std::string &name, |
| T *param, unsigned size) |
| { |
| const std::string §ion(Serializable::currentSection()); |
| std::string str; |
| if (!cp.find(section, name, str)) { |
| fatal("Can't unserialize '%s:%s'\n", section, name); |
| } |
| |
| // code below stolen from VectorParam<T>::parse(). |
| // it would be nice to unify these somehow... |
| |
| std::vector<std::string> tokens; |
| |
| tokenize(tokens, str, ' '); |
| |
| // Need this if we were doing a vector |
| // value.resize(tokens.size()); |
| |
| fatal_if(tokens.size() != size, |
| "Array size mismatch on %s:%s (Got %u, expected %u)'\n", |
| section, name, tokens.size(), size); |
| |
| for (std::vector<std::string>::size_type i = 0; i < tokens.size(); i++) { |
| // need to parse into local variable to handle vector<bool>, |
| // for which operator[] returns a special reference class |
| // that's not the same as 'bool&', (since it's a packed |
| // vector) |
| T scalar_value; |
| if (!parseParam(tokens[i], scalar_value)) { |
| std::string err("could not parse \""); |
| |
| err += str; |
| err += "\""; |
| |
| fatal(err); |
| } |
| |
| // assign parsed value to vector |
| param[i] = scalar_value; |
| } |
| } |
| |
| template <class T> |
| void |
| arrayParamIn(CheckpointIn &cp, const std::string &name, std::vector<T> ¶m) |
| { |
| const std::string §ion(Serializable::currentSection()); |
| std::string str; |
| if (!cp.find(section, name, str)) { |
| fatal("Can't unserialize '%s:%s'\n", section, name); |
| } |
| |
| // code below stolen from VectorParam<T>::parse(). |
| // it would be nice to unify these somehow... |
| |
| std::vector<std::string> tokens; |
| |
| tokenize(tokens, str, ' '); |
| |
| // Need this if we were doing a vector |
| // value.resize(tokens.size()); |
| |
| param.resize(tokens.size()); |
| |
| for (std::vector<std::string>::size_type i = 0; i < tokens.size(); i++) { |
| // need to parse into local variable to handle vector<bool>, |
| // for which operator[] returns a special reference class |
| // that's not the same as 'bool&', (since it's a packed |
| // vector) |
| T scalar_value; |
| if (!parseParam(tokens[i], scalar_value)) { |
| std::string err("could not parse \""); |
| |
| err += str; |
| err += "\""; |
| |
| fatal(err); |
| } |
| |
| // assign parsed value to vector |
| param[i] = scalar_value; |
| } |
| } |
| |
| template <class T> |
| void |
| arrayParamIn(CheckpointIn &cp, const std::string &name, std::list<T> ¶m) |
| { |
| const std::string §ion(Serializable::currentSection()); |
| std::string str; |
| if (!cp.find(section, name, str)) { |
| fatal("Can't unserialize '%s:%s'\n", section, name); |
| } |
| param.clear(); |
| |
| std::vector<std::string> tokens; |
| tokenize(tokens, str, ' '); |
| |
| for (std::vector<std::string>::size_type i = 0; i < tokens.size(); i++) { |
| T scalar_value; |
| if (!parseParam(tokens[i], scalar_value)) { |
| std::string err("could not parse \""); |
| |
| err += str; |
| err += "\""; |
| |
| fatal(err); |
| } |
| |
| // assign parsed value to vector |
| param.push_back(scalar_value); |
| } |
| } |
| |
| template <class T> |
| void |
| arrayParamIn(CheckpointIn &cp, const std::string &name, std::set<T> ¶m) |
| { |
| const std::string §ion(Serializable::currentSection()); |
| std::string str; |
| if (!cp.find(section, name, str)) { |
| fatal("Can't unserialize '%s:%s'\n", section, name); |
| } |
| param.clear(); |
| |
| std::vector<std::string> tokens; |
| tokenize(tokens, str, ' '); |
| |
| for (std::vector<std::string>::size_type i = 0; i < tokens.size(); i++) { |
| T scalar_value; |
| if (!parseParam(tokens[i], scalar_value)) { |
| std::string err("could not parse \""); |
| |
| err += str; |
| err += "\""; |
| |
| fatal(err); |
| } |
| |
| // assign parsed value to vector |
| param.insert(scalar_value); |
| } |
| } |
| |
| void |
| debug_serialize(const std::string &cpt_dir); |
| |
| void |
| objParamIn(CheckpointIn &cp, const std::string &name, SimObject * ¶m); |
| |
| // |
| // These macros are streamlined to use in serialize/unserialize |
| // functions. It's assumed that serialize() has a parameter 'os' for |
| // the ostream, and unserialize() has parameters 'cp' and 'section'. |
| #define SERIALIZE_SCALAR(scalar) paramOut(cp, #scalar, scalar) |
| |
| #define UNSERIALIZE_SCALAR(scalar) paramIn(cp, #scalar, scalar) |
| #define UNSERIALIZE_OPT_SCALAR(scalar) optParamIn(cp, #scalar, scalar) |
| |
| // ENUMs are like SCALARs, but we cast them to ints on the way out |
| #define SERIALIZE_ENUM(scalar) paramOut(cp, #scalar, (int)scalar) |
| |
| #define UNSERIALIZE_ENUM(scalar) \ |
| do { \ |
| int tmp; \ |
| paramIn(cp, #scalar, tmp); \ |
| scalar = static_cast<decltype(scalar)>(tmp); \ |
| } while (0) |
| |
| #define SERIALIZE_ARRAY(member, size) \ |
| arrayParamOut(cp, #member, member, size) |
| |
| #define UNSERIALIZE_ARRAY(member, size) \ |
| arrayParamIn(cp, #member, member, size) |
| |
| #define SERIALIZE_CONTAINER(member) \ |
| arrayParamOut(cp, #member, member) |
| |
| #define UNSERIALIZE_CONTAINER(member) \ |
| arrayParamIn(cp, #member, member) |
| |
| #define SERIALIZE_EVENT(event) event.serializeSection(cp, #event); |
| |
| #define UNSERIALIZE_EVENT(event) \ |
| do { \ |
| event.unserializeSection(cp, #event); \ |
| eventQueue()->checkpointReschedule(&event); \ |
| } while (0) |
| |
| #define SERIALIZE_OBJ(obj) obj.serializeSection(cp, #obj) |
| #define UNSERIALIZE_OBJ(obj) obj.unserializeSection(cp, #obj) |
| |
| #define SERIALIZE_OBJPTR(objptr) paramOut(cp, #objptr, (objptr)->name()) |
| |
| #define UNSERIALIZE_OBJPTR(objptr) \ |
| do { \ |
| SimObject *sptr; \ |
| objParamIn(cp, #objptr, sptr); \ |
| objptr = dynamic_cast<decltype(objptr)>(sptr); \ |
| } while (0) |
| |
| #endif // __SERIALIZE_HH__ |