1/*
2 * Copyright (c) 2015 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2002-2005 The Regents of The University of Michigan
15 * Copyright (c) 2013 Advanced Micro Devices, Inc.
16 * Copyright (c) 2013 Mark D. Hill and David A. Wood
17 * All rights reserved.
18 *
19 * Redistribution and use in source and binary forms, with or without
20 * modification, are permitted provided that the following conditions are
21 * met: redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer;
23 * redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in the
25 * documentation and/or other materials provided with the distribution;
26 * neither the name of the copyright holders nor the names of its
27 * contributors may be used to endorse or promote products derived from
28 * this software without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 *
42 * Authors: Nathan Binkert
43 *          Erik Hallnor
44 *          Steve Reinhardt
45 *          Andreas Sandberg
46 */
47
48#include "sim/serialize.hh"
49
50#include <sys/stat.h>
51#include <sys/time.h>
52#include <sys/types.h>
53
54#include <cerrno>
55#include <fstream>
56#include <list>
57#include <string>
58#include <vector>
59
60#include "base/inifile.hh"
61#include "base/output.hh"
62#include "base/trace.hh"
63#include "debug/Checkpoint.hh"
64#include "sim/eventq.hh"
65#include "sim/sim_events.hh"
66#include "sim/sim_exit.hh"
67#include "sim/sim_object.hh"
68
69// For stat reset hack
70#include "sim/stat_control.hh"
71
72using namespace std;
73
74int Serializable::ckptMaxCount = 0;
75int Serializable::ckptCount = 0;
76int Serializable::ckptPrevCount = -1;
77std::stack<std::string> Serializable::path;
78
79/////////////////////////////
80
81/// Container for serializing global variables (not associated with
82/// any serialized object).
83class Globals : public Serializable
84{
85  public:
86    Globals()
87        : unserializedCurTick(0) {}
88
89    void serialize(CheckpointOut &cp) const override;
90    void unserialize(CheckpointIn &cp) override;
91
92    Tick unserializedCurTick;
93};
94
95/// The one and only instance of the Globals class.
96Globals globals;
97
98/// The version tags for this build of the simulator, to be stored in the
99/// Globals section during serialization and compared upon unserialization.
100extern std::set<std::string> version_tags;
101
102void
103Globals::serialize(CheckpointOut &cp) const
104{
105    paramOut(cp, "curTick", curTick());
106    SERIALIZE_CONTAINER(version_tags);
107}
108
109void
110Globals::unserialize(CheckpointIn &cp)
111{
112    paramIn(cp, "curTick", unserializedCurTick);
113
114    const std::string &section(Serializable::currentSection());
115    std::string str;
116    if (!cp.find(section, "version_tags", str)) {
117        warn("**********************************************************\n");
118        warn("!!!! Checkpoint uses an old versioning scheme.        !!!!\n");
119        warn("Run the checkpoint upgrader (util/cpt_upgrader.py) on your "
120             "checkpoint\n");
121        warn("**********************************************************\n");
122        return;
123    }
124
125    std::set<std::string> cpt_tags;
126    arrayParamIn(cp, "version_tags", cpt_tags); // UNSERIALIZE_CONTAINER
127
128    bool err = false;
129    for (const auto& t : version_tags) {
130        if (cpt_tags.find(t) == cpt_tags.end()) {
131            // checkpoint is missing tag that this binary has
132            if (!err) {
133                warn("*****************************************************\n");
134                warn("!!!! Checkpoint is missing the following version tags:\n");
135                err = true;
136            }
137            warn("  %s\n", t);
138        }
139    }
140    if (err) {
141        warn("You might experience some issues when restoring and should run "
142             "the checkpoint upgrader (util/cpt_upgrader.py) on your "
143             "checkpoint\n");
144        warn("**********************************************************\n");
145    }
146
147    err = false;
148    for (const auto& t : cpt_tags) {
149        if (version_tags.find(t) == version_tags.end()) {
150            // gem5 binary is missing tag that this checkpoint has
151            if (!err) {
152                warn("*****************************************************\n");
153                warn("!!!! gem5 is missing the following version tags:\n");
154                err = true;
155            }
156            warn("  %s\n", t);
157        }
158    }
159    if (err) {
160        warn("Running a checkpoint with incompatible version tags is not "
161             "supported. While it might work, you may experience incorrect "
162             "behavior or crashes.\n");
163        warn("**********************************************************\n");
164     }
165}
166
167Serializable::Serializable()
168{
169}
170
171Serializable::~Serializable()
172{
173}
174
175void
176Serializable::serializeSection(CheckpointOut &cp, const char *name) const
177{
178    Serializable::ScopedCheckpointSection sec(cp, name);
179    serialize(cp);
180}
181
182void
183Serializable::unserializeSection(CheckpointIn &cp, const char *name)
184{
185    Serializable::ScopedCheckpointSection sec(cp, name);
186    unserialize(cp);
187}
188
189void
190Serializable::serializeAll(const string &cpt_dir)
191{
192    string dir = CheckpointIn::setDir(cpt_dir);
193    if (mkdir(dir.c_str(), 0775) == -1 && errno != EEXIST)
194            fatal("couldn't mkdir %s\n", dir);
195
196    string cpt_file = dir + CheckpointIn::baseFilename;
197    ofstream outstream(cpt_file.c_str());
198    time_t t = time(NULL);
199    if (!outstream.is_open())
200        fatal("Unable to open file %s for writing\n", cpt_file.c_str());
201    outstream << "## checkpoint generated: " << ctime(&t);
202
203    globals.serializeSection(outstream, "Globals");
204
205    SimObject::serializeAll(outstream);
206}
207
208void
209Serializable::unserializeGlobals(CheckpointIn &cp)
210{
211    globals.unserializeSection(cp, "Globals");
212
213    for (uint32_t i = 0; i < numMainEventQueues; ++i)
214        mainEventQueue[i]->setCurTick(globals.unserializedCurTick);
215}
216
217Serializable::ScopedCheckpointSection::~ScopedCheckpointSection()
218{
219    assert(!path.empty());
220    DPRINTF(Checkpoint, "Popping: %s\n", path.top());
221    path.pop();
222}
223
224void
225Serializable::ScopedCheckpointSection::pushName(const char *obj_name)
226{
227    if (path.empty()) {
228        path.push(obj_name);
229    } else {
230        path.push(csprintf("%s.%s", path.top(), obj_name));
231    }
232    DPRINTF(Checkpoint, "ScopedCheckpointSection::pushName: %s\n", obj_name);
233}
234
235void
236Serializable::ScopedCheckpointSection::nameOut(CheckpointOut &cp)
237{
238    DPRINTF(Checkpoint, "ScopedCheckpointSection::nameOut: %s\n",
239            Serializable::currentSection());
240    cp << "\n[" << Serializable::currentSection() << "]\n";
241}
242
243const std::string &
244Serializable::currentSection()
245{
246    assert(!path.empty());
247
248    return path.top();
249}
250
251const char *CheckpointIn::baseFilename = "m5.cpt";
252
253string CheckpointIn::currentDirectory;
254
255string
256CheckpointIn::setDir(const string &name)
257{
258    // use csprintf to insert curTick() into directory name if it
259    // appears to have a format placeholder in it.
260    currentDirectory = (name.find("%") != string::npos) ?
261        csprintf(name, curTick()) : name;
262    if (currentDirectory[currentDirectory.size() - 1] != '/')
263        currentDirectory += "/";
264    return currentDirectory;
265}
266
267string
268CheckpointIn::dir()
269{
270    return currentDirectory;
271}
272
273CheckpointIn::CheckpointIn(const string &cpt_dir, SimObjectResolver &resolver)
274    : db(new IniFile), objNameResolver(resolver), cptDir(setDir(cpt_dir))
275{
276    string filename = cptDir + "/" + CheckpointIn::baseFilename;
277    if (!db->load(filename)) {
278        fatal("Can't load checkpoint file '%s'\n", filename);
279    }
280}
281
282CheckpointIn::~CheckpointIn()
283{
284    delete db;
285}
286
287bool
288CheckpointIn::entryExists(const string &section, const string &entry)
289{
290    return db->entryExists(section, entry);
291}
292
293bool
294CheckpointIn::find(const string &section, const string &entry, string &value)
295{
296    return db->find(section, entry, value);
297}
298
299bool
300CheckpointIn::findObj(const string &section, const string &entry,
301                    SimObject *&value)
302{
303    string path;
304
305    if (!db->find(section, entry, path))
306        return false;
307
308    value = objNameResolver.resolveSimObject(path);
309    return true;
310}
311
312bool
313CheckpointIn::sectionExists(const string &section)
314{
315    return db->sectionExists(section);
316}
317
318void
319objParamIn(CheckpointIn &cp, const string &name, SimObject * &param)
320{
321    const string &section(Serializable::currentSection());
322    if (!cp.findObj(section, name, param)) {
323        fatal("Can't unserialize '%s:%s'\n", section, name);
324    }
325}
326
327void
328debug_serialize(const string &cpt_dir)
329{
330    Serializable::serializeAll(cpt_dir);
331}
332