/* * 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 #include #include #include #include #include #include #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 ScopedCheckpointSection(CP &cp, const char *name) { pushName(name); nameOut(cp); } template 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 path; }; // // The base implementations use to_number for parsing and '<<' for // displaying, suitable for integer types. // template bool parseParam(const std::string &s, T &value) { return to_number(s, value); } template void showParam(CheckpointOut &os, const T &value) { os << value; } template bool parseParam(const std::string &s, BitUnionType &value) { auto storage = static_cast>(value); auto res = to_number(s, storage); value = storage; return res; } template void showParam(CheckpointOut &os, const BitUnionType &value) { auto storage = static_cast>(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(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 void paramOut(CheckpointOut &os, const std::string &name, const T ¶m) { os << name << "="; showParam(os, param); os << "\n"; } template 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 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 void arrayParamOut(CheckpointOut &os, const std::string &name, const std::vector ¶m) { typename std::vector::size_type size = param.size(); os << name << "="; if (size > 0) showParam(os, param[0]); for (typename std::vector::size_type i = 1; i < size; ++i) { os << " "; showParam(os, param[i]); } os << "\n"; } template void arrayParamOut(CheckpointOut &os, const std::string &name, const std::list ¶m) { typename std::list::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 void arrayParamOut(CheckpointOut &os, const std::string &name, const std::set ¶m) { typename std::set::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 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"; } template 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::parse(). // it would be nice to unify these somehow... std::vector tokens; tokenize(tokens, str, ' '); // Need this if we were doing a vector // value.resize(tokens.size()); if (tokens.size() != size) { fatal("Array size mismatch on %s:%s'\n", section, name); } for (std::vector::size_type i = 0; i < tokens.size(); i++) { // need to parse into local variable to handle vector, // 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 void arrayParamIn(CheckpointIn &cp, const std::string &name, std::vector ¶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::parse(). // it would be nice to unify these somehow... std::vector tokens; tokenize(tokens, str, ' '); // Need this if we were doing a vector // value.resize(tokens.size()); param.resize(tokens.size()); for (std::vector::size_type i = 0; i < tokens.size(); i++) { // need to parse into local variable to handle vector, // 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 void arrayParamIn(CheckpointIn &cp, const std::string &name, std::list ¶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 tokens; tokenize(tokens, str, ' '); for (std::vector::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 void arrayParamIn(CheckpointIn &cp, const std::string &name, std::set ¶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 tokens; tokenize(tokens, str, ' '); for (std::vector::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(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(sptr); \ } while (0) #endif // __SERIALIZE_HH__