main.cc revision 2868:6a7e69fa92d3
1/*
2 * Copyright (c) 2000-2005 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Steve Raasch
29 *          Nathan Binkert
30 *          Steve Reinhardt
31 */
32
33///
34/// @file sim/main.cc
35///
36#include <Python.h>	// must be before system headers... see Python docs
37
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <errno.h>
41#include <libgen.h>
42#include <stdlib.h>
43#include <signal.h>
44#include <getopt.h>
45
46#include <list>
47#include <string>
48#include <vector>
49
50#include "base/callback.hh"
51#include "base/inifile.hh"
52#include "base/misc.hh"
53#include "base/output.hh"
54#include "base/pollevent.hh"
55#include "base/statistics.hh"
56#include "base/str.hh"
57#include "base/time.hh"
58#include "cpu/base.hh"
59#include "cpu/smt.hh"
60#include "mem/mem_object.hh"
61#include "mem/port.hh"
62#include "sim/async.hh"
63#include "sim/builder.hh"
64#include "sim/host.hh"
65#include "sim/serialize.hh"
66#include "sim/sim_events.hh"
67#include "sim/sim_exit.hh"
68#include "sim/sim_object.hh"
69#include "sim/stat_control.hh"
70#include "sim/stats.hh"
71#include "sim/root.hh"
72
73using namespace std;
74
75// See async.h.
76volatile bool async_event = false;
77volatile bool async_dump = false;
78volatile bool async_dumpreset = false;
79volatile bool async_exit = false;
80volatile bool async_io = false;
81volatile bool async_alarm = false;
82
83/// Stats signal handler.
84void
85dumpStatsHandler(int sigtype)
86{
87    async_event = true;
88    async_dump = true;
89}
90
91void
92dumprstStatsHandler(int sigtype)
93{
94    async_event = true;
95    async_dumpreset = true;
96}
97
98/// Exit signal handler.
99void
100exitNowHandler(int sigtype)
101{
102    async_event = true;
103    async_exit = true;
104}
105
106/// Abort signal handler.
107void
108abortHandler(int sigtype)
109{
110    cerr << "Program aborted at cycle " << curTick << endl;
111
112#if TRACING_ON
113    // dump trace buffer, if there is one
114    Trace::theLog.dump(cerr);
115#endif
116}
117
118/// Simulator executable name
119char *myProgName = "";
120
121/// Show brief help message.
122void
123showBriefHelp(ostream &out)
124{
125    char *prog = basename(myProgName);
126
127    ccprintf(out, "Usage:\n");
128    ccprintf(out,
129"%s [-p <path>] [-i ] [-h] <config file>\n"
130"\n"
131" -p, --path <path>  prepends <path> to PYTHONPATH instead of using\n"
132"                    built-in zip archive.  Useful when developing/debugging\n"
133"                    changes to built-in Python libraries, as the new Python\n"
134"                    can be tested without building a new m5 binary.\n\n"
135" -i, --interactive  forces entry into interactive mode after the supplied\n"
136"                    script is executed (just like the -i option to  the\n"
137"                    Python interpreter).\n\n"
138" -h                 Prints this help\n\n"
139" <configfile>       config file name which ends in .py. (Normally you can\n"
140"                    run <configfile> --help to get help on that config files\n"
141"                    parameters.\n\n",
142             prog);
143
144}
145
146const char *briefCopyright =
147"Copyright (c) 2001-2006\n"
148"The Regents of The University of Michigan\n"
149"All Rights Reserved\n";
150
151/// Print welcome message.
152void
153sayHello(ostream &out)
154{
155    extern const char *compileDate;     // from date.cc
156
157    ccprintf(out, "M5 Simulator System\n");
158    // display copyright
159    ccprintf(out, "%s\n", briefCopyright);
160    ccprintf(out, "M5 compiled %d\n", compileDate);
161    ccprintf(out, "M5 started %s\n", Time::start);
162
163    char *host = getenv("HOSTNAME");
164    if (!host)
165        host = getenv("HOST");
166
167    if (host)
168        ccprintf(out, "M5 executing on %s\n", host);
169}
170
171
172extern "C" { void init_cc_main(); }
173
174int
175main(int argc, char **argv)
176{
177    // Saze off program name
178    myProgName = argv[0];
179
180    sayHello(cerr);
181
182    signal(SIGFPE, SIG_IGN);		// may occur on misspeculated paths
183    signal(SIGTRAP, SIG_IGN);
184    signal(SIGUSR1, dumpStatsHandler);		// dump intermediate stats
185    signal(SIGUSR2, dumprstStatsHandler);	// dump and reset stats
186    signal(SIGINT, exitNowHandler);		// dump final stats and exit
187    signal(SIGABRT, abortHandler);
188
189    Py_SetProgramName(argv[0]);
190
191    // default path to m5 python code is the currently executing
192    // file... Python ZipImporter will find embedded zip archive
193    char *pythonpath = argv[0];
194
195    bool interactive = false;
196    bool show_help = false;
197    bool getopt_done = false;
198    int opt_index = 0;
199
200    static struct option long_options[] = {
201        {"python", 1, 0, 'p'},
202        {"interactive", 0, 0, 'i'},
203        {"help", 0, 0, 'h'},
204        {0,0,0,0}
205    };
206
207    do {
208        switch (getopt_long(argc, argv, "+p:ih", long_options, &opt_index)) {
209            // -p <path> prepends <path> to PYTHONPATH instead of
210            // using built-in zip archive.  Useful when
211            // developing/debugging changes to built-in Python
212            // libraries, as the new Python can be tested without
213            // building a new m5 binary.
214          case 'p':
215            pythonpath = optarg;
216            break;
217
218            // -i forces entry into interactive mode after the
219            // supplied script is executed (just like the -i option to
220            // the Python interpreter).
221          case 'i':
222            interactive = true;
223            break;
224
225          case 'h':
226            show_help = true;
227            break;
228          case -1:
229            getopt_done = true;
230            break;
231
232          default:
233            fatal("Unrecognized option %c\n", optopt);
234        }
235    } while (!getopt_done);
236
237    if (show_help) {
238        showBriefHelp(cerr);
239        exit(1);
240    }
241
242    // Fix up argc & argv to hide arguments we just processed.
243    // getopt() sets optind to the index of the first non-processed
244    // argv element.
245    argc -= optind;
246    argv += optind;
247
248    // Set up PYTHONPATH to make sure the m5 module is found
249    string newpath(pythonpath);
250
251    char *oldpath = getenv("PYTHONPATH");
252    if (oldpath != NULL) {
253        newpath += ":";
254        newpath += oldpath;
255    }
256
257    if (setenv("PYTHONPATH", newpath.c_str(), true) == -1)
258        fatal("setenv: %s\n", strerror(errno));
259
260    // initialize embedded Python interpreter
261    Py_Initialize();
262    PySys_SetArgv(argc, argv);
263
264    // initialize SWIG 'cc_main' module
265    init_cc_main();
266
267    if (argc > 0) {
268        // extra arg(s): first is script file, remaining ones are args
269        // to script file
270        char *filename = argv[0];
271        FILE *fp = fopen(filename, "r");
272        if (!fp) {
273            fatal("cannot open file '%s'\n", filename);
274        }
275
276        PyRun_AnyFile(fp, filename);
277    } else {
278        // no script file argument... force interactive prompt
279        interactive = true;
280    }
281
282    if (interactive) {
283        // The following code to import readline was copied from Python
284        // 2.4.3's Modules/main.c.
285        // Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006
286        // Python Software Foundation; All Rights Reserved
287        // We should only enable this if we're actually using an
288        // interactive prompt.
289        PyObject *v;
290        v = PyImport_ImportModule("readline");
291        if (v == NULL)
292            PyErr_Clear();
293        else
294            Py_DECREF(v);
295
296        PyRun_InteractiveLoop(stdin, "stdin");
297    }
298
299    // clean up Python intepreter.
300    Py_Finalize();
301}
302
303
304void
305setOutputDir(const string &dir)
306{
307    simout.setDirectory(dir);
308}
309
310
311IniFile inifile;
312
313SimObject *
314createSimObject(const string &name)
315{
316    return SimObjectClass::createObject(inifile, name);
317}
318
319
320/**
321 * Pointer to the Python function that maps names to SimObjects.
322 */
323PyObject *resolveFunc = NULL;
324
325/**
326 * Convert a pointer to the Python object that SWIG wraps around a C++
327 * SimObject pointer back to the actual C++ pointer.  See main.i.
328 */
329extern "C" SimObject *convertSwigSimObjectPtr(PyObject *);
330
331
332SimObject *
333resolveSimObject(const string &name)
334{
335    PyObject *pyPtr = PyEval_CallFunction(resolveFunc, "(s)", name.c_str());
336    if (pyPtr == NULL) {
337        PyErr_Print();
338        panic("resolveSimObject: failure on call to Python for %s", name);
339    }
340
341    SimObject *simObj = convertSwigSimObjectPtr(pyPtr);
342    if (simObj == NULL)
343        panic("resolveSimObject: failure on pointer conversion for %s", name);
344
345    return simObj;
346}
347
348
349/**
350 * Load config.ini into C++ database.  Exported to Python via SWIG;
351 * invoked from m5.instantiate().
352 */
353void
354loadIniFile(PyObject *_resolveFunc)
355{
356    resolveFunc = _resolveFunc;
357    configStream = simout.find("config.out");
358
359    // The configuration database is now complete; start processing it.
360    inifile.load("config.ini");
361
362    // Initialize statistics database
363    Stats::InitSimStats();
364}
365
366
367/**
368 * Look up a MemObject port.  Helper function for connectPorts().
369 */
370Port *
371lookupPort(SimObject *so, const std::string &name, int i)
372{
373    MemObject *mo = dynamic_cast<MemObject *>(so);
374    if (mo == NULL) {
375        warn("error casting SimObject %s to MemObject", so->name());
376        return NULL;
377    }
378
379    Port *p = mo->getPort(name, i);
380    if (p == NULL)
381        warn("error looking up port %s on object %s", name, so->name());
382    return p;
383}
384
385
386/**
387 * Connect the described MemObject ports.  Called from Python via SWIG.
388 */
389int
390connectPorts(SimObject *o1, const std::string &name1, int i1,
391             SimObject *o2, const std::string &name2, int i2)
392{
393    Port *p1 = lookupPort(o1, name1, i1);
394    Port *p2 = lookupPort(o2, name2, i2);
395
396    if (p1 == NULL || p2 == NULL) {
397        warn("connectPorts: port lookup error");
398        return 0;
399    }
400
401    p1->setPeer(p2);
402    p2->setPeer(p1);
403
404    return 1;
405}
406
407/**
408 * Do final initialization steps after object construction but before
409 * start of simulation.
410 */
411void
412finalInit()
413{
414    // Parse and check all non-config-hierarchy parameters.
415    ParamContext::parseAllContexts(inifile);
416    ParamContext::checkAllContexts();
417
418    // Echo all parameter settings to stats file as well.
419    ParamContext::showAllContexts(*configStream);
420
421    // Do a second pass to finish initializing the sim objects
422    SimObject::initAll();
423
424    // Restore checkpointed state, if any.
425#if 0
426    configHierarchy.unserializeSimObjects();
427#endif
428
429    SimObject::regAllStats();
430
431    // Check to make sure that the stats package is properly initialized
432    Stats::check();
433
434    // Reset to put the stats in a consistent state.
435    Stats::reset();
436
437    SimStartup();
438}
439
440
441/** Simulate for num_cycles additional cycles.  If num_cycles is -1
442 * (the default), do not limit simulation; some other event must
443 * terminate the loop.  Exported to Python via SWIG.
444 * @return The SimLoopExitEvent that caused the loop to exit.
445 */
446SimLoopExitEvent *
447simulate(Tick num_cycles = -1)
448{
449    warn("Entering event queue @ %d.  Starting simulation...\n", curTick);
450
451    // Fix up num_cycles.  Special default value -1 means simulate
452    // "forever"... schedule event at MaxTick just to be safe.
453    // Otherwise it's a delta for additional cycles to simulate past
454    // curTick, and thus must be non-negative.
455    if (num_cycles == -1)
456        num_cycles = MaxTick;
457    else if (num_cycles < 0)
458        fatal("simulate: num_cycles must be >= 0 (was %d)\n", num_cycles);
459    else
460        num_cycles = curTick + num_cycles;
461
462    Event *limit_event = new SimLoopExitEvent(num_cycles,
463                                              "simulate() limit reached");
464
465    while (1) {
466        // there should always be at least one event (the SimLoopExitEvent
467        // we just scheduled) in the queue
468        assert(!mainEventQueue.empty());
469        assert(curTick <= mainEventQueue.nextTick() &&
470               "event scheduled in the past");
471
472        // forward current cycle to the time of the first event on the
473        // queue
474        curTick = mainEventQueue.nextTick();
475        Event *exit_event = mainEventQueue.serviceOne();
476        if (exit_event != NULL) {
477            // hit some kind of exit event; return to Python
478            // event must be subclass of SimLoopExitEvent...
479            SimLoopExitEvent *se_event = dynamic_cast<SimLoopExitEvent *>(exit_event);
480            if (se_event == NULL)
481                panic("Bogus exit event class!");
482
483            // if we didn't hit limit_event, delete it
484            if (se_event != limit_event) {
485                assert(limit_event->scheduled());
486                limit_event->deschedule();
487                delete limit_event;
488            }
489
490            return se_event;
491        }
492
493        if (async_event) {
494            async_event = false;
495            if (async_dump) {
496                async_dump = false;
497
498                using namespace Stats;
499                SetupEvent(Dump, curTick);
500            }
501
502            if (async_dumpreset) {
503                async_dumpreset = false;
504
505                using namespace Stats;
506                SetupEvent(Dump | Reset, curTick);
507            }
508
509            if (async_exit) {
510                async_exit = false;
511                exitSimLoop("user interrupt received");
512            }
513
514            if (async_io || async_alarm) {
515                async_io = false;
516                async_alarm = false;
517                pollQueue.service();
518            }
519        }
520    }
521
522    // not reached... only exit is return on SimLoopExitEvent
523}
524
525Event *
526createCountedDrain()
527{
528    return new CountedDrainEvent();
529}
530
531void
532cleanupCountedDrain(Event *counted_drain)
533{
534    CountedDrainEvent *event =
535        dynamic_cast<CountedDrainEvent *>(counted_drain);
536    if (event == NULL) {
537        fatal("Called cleanupCountedDrain() on an event that was not "
538              "a CountedDrainEvent.");
539    }
540    assert(event->getCount() == 0);
541    delete event;
542}
543
544void
545serializeAll(const std::string &cpt_dir)
546{
547    Serializable::serializeAll(cpt_dir);
548}
549
550void
551unserializeAll(const std::string &cpt_dir)
552{
553    Serializable::unserializeAll(cpt_dir);
554}
555
556/**
557 * Queue of C++ callbacks to invoke on simulator exit.
558 */
559CallbackQueue exitCallbacks;
560
561/**
562 * Register an exit callback.
563 */
564void
565registerExitCallback(Callback *callback)
566{
567    exitCallbacks.add(callback);
568}
569
570BaseCPU *
571convertToBaseCPUPtr(SimObject *obj)
572{
573    BaseCPU *ptr = dynamic_cast<BaseCPU *>(obj);
574
575    if (ptr == NULL)
576        warn("Casting to BaseCPU pointer failed");
577    return ptr;
578}
579
580/**
581 * Do C++ simulator exit processing.  Exported to SWIG to be invoked
582 * when simulator terminates via Python's atexit mechanism.
583 */
584void
585doExitCleanup()
586{
587    exitCallbacks.process();
588    exitCallbacks.clear();
589
590    cout.flush();
591
592    ParamContext::cleanupAllContexts();
593
594    // print simulation stats
595    Stats::DumpNow();
596}
597