1# Copyright (c) 2016, 2019 Arm Limited
2# All rights reserved.
3#
4# The license below extends only to copyright in the software and shall
5# not be construed as granting a license to any other intellectual
6# property including but not limited to intellectual property relating
7# to a hardware implementation of the functionality of the software
8# licensed hereunder.  You may use the software subject to the license
9# terms below provided that you ensure that this notice is replicated
10# unmodified and in its entirety in all distributions of the software,
11# modified or unmodified, in source code or in binary form.
12#
13# Copyright (c) 2005 The Regents of The University of Michigan
14# All rights reserved.
15#
16# Redistribution and use in source and binary forms, with or without
17# modification, are permitted provided that the following conditions are
18# met: redistributions of source code must retain the above copyright
19# notice, this list of conditions and the following disclaimer;
20# redistributions in binary form must reproduce the above copyright
21# notice, this list of conditions and the following disclaimer in the
22# documentation and/or other materials provided with the distribution;
23# neither the name of the copyright holders nor the names of its
24# contributors may be used to endorse or promote products derived from
25# this software without specific prior written permission.
26#
27# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38#
39# Authors: Nathan Binkert
40
41from __future__ import print_function
42
43import code
44import datetime
45import os
46import socket
47import sys
48
49__all__ = [ 'options', 'arguments', 'main' ]
50
51usage="%prog [gem5 options] script.py [script options]"
52version="%prog 2.0"
53brief_copyright=\
54    "gem5 is copyrighted software; use the --copyright option for details."
55
56def _stats_help(option, opt, value, parser):
57    import m5
58    print("A stat file can either be specified as a URI or a plain")
59    print("path. When specified as a path, gem5 uses the default text ")
60    print("format.")
61    print()
62    print("The following stat formats are supported:")
63    print()
64    m5.stats.printStatVisitorTypes()
65    sys.exit(0)
66
67
68def parse_options():
69    from . import config
70    from .options import OptionParser
71
72    options = OptionParser(usage=usage, version=version,
73                           description=brief_copyright)
74    option = options.add_option
75    group = options.set_group
76
77    listener_modes = ( "on", "off", "auto" )
78
79    # Help options
80    option('-B', "--build-info", action="store_true", default=False,
81        help="Show build information")
82    option('-C', "--copyright", action="store_true", default=False,
83        help="Show full copyright information")
84    option('-R', "--readme", action="store_true", default=False,
85        help="Show the readme")
86
87    # Options for configuring the base simulator
88    option('-d', "--outdir", metavar="DIR", default="m5out",
89        help="Set the output directory to DIR [Default: %default]")
90    option('-r', "--redirect-stdout", action="store_true", default=False,
91        help="Redirect stdout (& stderr, without -e) to file")
92    option('-e', "--redirect-stderr", action="store_true", default=False,
93        help="Redirect stderr to file")
94    option("--stdout-file", metavar="FILE", default="simout",
95        help="Filename for -r redirection [Default: %default]")
96    option("--stderr-file", metavar="FILE", default="simerr",
97        help="Filename for -e redirection [Default: %default]")
98    option("--listener-mode", metavar="{on,off,auto}",
99        choices=listener_modes, default="auto",
100        help="Port (e.g., gdb) listener mode (auto: Enable if running " \
101        "interactively) [Default: %default]")
102    option("--listener-loopback-only", action="store_true", default=False,
103        help="Port listeners will only accept connections over the " \
104        "loopback device")
105    option('-i', "--interactive", action="store_true", default=False,
106        help="Invoke the interactive interpreter after running the script")
107    option("--pdb", action="store_true", default=False,
108        help="Invoke the python debugger before running the script")
109    option('-p', "--path", metavar="PATH[:PATH]", action='append', split=':',
110        help="Prepend PATH to the system path when invoking the script")
111    option('-q', "--quiet", action="count", default=0,
112        help="Reduce verbosity")
113    option('-v', "--verbose", action="count", default=0,
114        help="Increase verbosity")
115
116    # Statistics options
117    group("Statistics Options")
118    option("--stats-file", metavar="FILE", default="stats.txt",
119        help="Sets the output file for statistics [Default: %default]")
120    option("--stats-help",
121           action="callback", callback=_stats_help,
122           help="Display documentation for available stat visitors")
123
124    # Configuration Options
125    group("Configuration Options")
126    option("--dump-config", metavar="FILE", default="config.ini",
127        help="Dump configuration output file [Default: %default]")
128    option("--json-config", metavar="FILE", default="config.json",
129        help="Create JSON output of the configuration [Default: %default]")
130    option("--dot-config", metavar="FILE", default="config.dot",
131        help="Create DOT & pdf outputs of the configuration [Default: %default]")
132    option("--dot-dvfs-config", metavar="FILE", default=None,
133        help="Create DOT & pdf outputs of the DVFS configuration" + \
134             " [Default: %default]")
135
136    # Debugging options
137    group("Debugging Options")
138    option("--debug-break", metavar="TICK[,TICK]", action='append', split=',',
139        help="Create breakpoint(s) at TICK(s) " \
140             "(kills process if no debugger attached)")
141    option("--debug-help", action='store_true',
142        help="Print help on debug flags")
143    option("--debug-flags", metavar="FLAG[,FLAG]", action='append', split=',',
144        help="Sets the flags for debug output (-FLAG disables a flag)")
145    option("--debug-start", metavar="TICK", type='int',
146        help="Start debug output at TICK")
147    option("--debug-end", metavar="TICK", type='int',
148        help="End debug output at TICK")
149    option("--debug-file", metavar="FILE", default="cout",
150        help="Sets the output file for debug [Default: %default]")
151    option("--debug-ignore", metavar="EXPR", action='append', split=':',
152        help="Ignore EXPR sim objects")
153    option("--remote-gdb-port", type='int', default=7000,
154        help="Remote gdb base port (set to 0 to disable listening)")
155
156    # Help options
157    group("Help Options")
158    option("--list-sim-objects", action='store_true', default=False,
159        help="List all built-in SimObjects, their params and default values")
160
161    # load the options.py config file to allow people to set their own
162    # default options
163    options_file = config.get('options.py')
164    if options_file:
165        scope = { 'options' : options }
166        exec(compile(open(options_file).read(), options_file, 'exec'), scope)
167
168    arguments = options.parse_args()
169    return options,arguments
170
171def interact(scope):
172    banner = "gem5 Interactive Console"
173
174    ipshell = None
175    prompt_in1 = "gem5 \\#> "
176    prompt_out = "gem5 \\#: "
177
178    # Is IPython version 0.10 or earlier available?
179    try:
180        from IPython.Shell import IPShellEmbed
181        ipshell = IPShellEmbed(argv=["-prompt_in1", prompt_in1,
182                                     "-prompt_out", prompt_out],
183                               banner=banner, user_ns=scope)
184    except ImportError:
185        pass
186
187    # Is IPython version 0.11 or later available?
188    if not ipshell:
189        try:
190            import IPython
191            from IPython.config.loader import Config
192            from IPython.terminal.embed import InteractiveShellEmbed
193
194            cfg = Config()
195            cfg.PromptManager.in_template = prompt_in1
196            cfg.PromptManager.out_template = prompt_out
197            ipshell = InteractiveShellEmbed(config=cfg, user_ns=scope,
198                                            banner1=banner)
199        except ImportError:
200            pass
201
202    if ipshell:
203        ipshell()
204    else:
205        # Use the Python shell in the standard library if IPython
206        # isn't available.
207        code.InteractiveConsole(scope).interact(banner)
208
209
210def _check_tracing():
211    from . import defines
212
213    if defines.TRACING_ON:
214        return
215
216    fatal("Tracing is not enabled.  Compile with TRACING_ON")
217
218def main(*args):
219    import m5
220
221    from . import core
222    from . import debug
223    from . import defines
224    from . import event
225    from . import info
226    from . import stats
227    from . import trace
228
229    from .util import inform, fatal, panic, isInteractive
230
231    if len(args) == 0:
232        options, arguments = parse_options()
233    elif len(args) == 2:
234        options, arguments = args
235    else:
236        raise TypeError("main() takes 0 or 2 arguments (%d given)" % len(args))
237
238    m5.options = options
239
240    # Set the main event queue for the main thread.
241    event.mainq = event.getEventQueue(0)
242    event.setEventQueue(event.mainq)
243
244    if not os.path.isdir(options.outdir):
245        os.makedirs(options.outdir)
246
247    # These filenames are used only if the redirect_std* options are set
248    stdout_file = os.path.join(options.outdir, options.stdout_file)
249    stderr_file = os.path.join(options.outdir, options.stderr_file)
250
251    # Print redirection notices here before doing any redirection
252    if options.redirect_stdout and not options.redirect_stderr:
253        print("Redirecting stdout and stderr to", stdout_file)
254    else:
255        if options.redirect_stdout:
256            print("Redirecting stdout to", stdout_file)
257        if options.redirect_stderr:
258            print("Redirecting stderr to", stderr_file)
259
260    # Now redirect stdout/stderr as desired
261    if options.redirect_stdout:
262        redir_fd = os.open(stdout_file, os. O_WRONLY | os.O_CREAT | os.O_TRUNC)
263        os.dup2(redir_fd, sys.stdout.fileno())
264        if not options.redirect_stderr:
265            os.dup2(redir_fd, sys.stderr.fileno())
266
267    if options.redirect_stderr:
268        redir_fd = os.open(stderr_file, os. O_WRONLY | os.O_CREAT | os.O_TRUNC)
269        os.dup2(redir_fd, sys.stderr.fileno())
270
271    done = False
272
273    if options.build_info:
274        done = True
275        print('Build information:')
276        print()
277        print('compiled %s' % defines.compileDate)
278        print('build options:')
279        keys = list(defines.buildEnv.keys())
280        keys.sort()
281        for key in keys:
282            val = defines.buildEnv[key]
283            print('    %s = %s' % (key, val))
284        print()
285
286    if options.copyright:
287        done = True
288        print(info.COPYING)
289        print()
290
291    if options.readme:
292        done = True
293        print('Readme:')
294        print()
295        print(info.README)
296        print()
297
298    if options.debug_help:
299        done = True
300        _check_tracing()
301        debug.help()
302
303    if options.list_sim_objects:
304        from . import SimObject
305        done = True
306        print("SimObjects:")
307        objects = list(SimObject.allClasses.keys())
308        objects.sort()
309        for name in objects:
310            obj = SimObject.allClasses[name]
311            print("    %s" % obj)
312            params = list(obj._params.keys())
313            params.sort()
314            for pname in params:
315                param = obj._params[pname]
316                default = getattr(param, 'default', '')
317                print("        %s" % pname)
318                if default:
319                    print("            default: %s" % default)
320                print("            desc: %s" % param.desc)
321                print()
322            print()
323
324    if done:
325        sys.exit(0)
326
327    # setting verbose and quiet at the same time doesn't make sense
328    if options.verbose > 0 and options.quiet > 0:
329        options.usage(2)
330
331    verbose = options.verbose - options.quiet
332    if verbose >= 0:
333        print("gem5 Simulator System.  http://gem5.org")
334        print(brief_copyright)
335        print()
336
337        print("gem5 compiled %s" % defines.compileDate)
338
339        print("gem5 started %s" %
340              datetime.datetime.now().strftime("%b %e %Y %X"))
341        print("gem5 executing on %s, pid %d" %
342              (socket.gethostname(), os.getpid()))
343
344        # in Python 3 pipes.quote() is moved to shlex.quote()
345        import pipes
346        print("command line:", " ".join(map(pipes.quote, sys.argv)))
347        print()
348
349    # check to make sure we can find the listed script
350    if not arguments or not os.path.isfile(arguments[0]):
351        if arguments and not os.path.isfile(arguments[0]):
352            print("Script %s not found" % arguments[0])
353
354        options.usage(2)
355
356    # tell C++ about output directory
357    core.setOutputDir(options.outdir)
358
359    # update the system path with elements from the -p option
360    sys.path[0:0] = options.path
361
362    # set stats options
363    stats.addStatVisitor(options.stats_file)
364
365    # Disable listeners unless running interactively or explicitly
366    # enabled
367    if options.listener_mode == "off":
368        m5.disableAllListeners()
369    elif options.listener_mode == "auto":
370        if not isInteractive():
371            inform("Standard input is not a terminal, disabling listeners.")
372            m5.disableAllListeners()
373    elif options.listener_mode == "on":
374        pass
375    else:
376        panic("Unhandled listener mode: %s" % options.listener_mode)
377
378    if options.listener_loopback_only:
379        m5.listenersLoopbackOnly()
380
381    # set debugging options
382    debug.setRemoteGDBPort(options.remote_gdb_port)
383    for when in options.debug_break:
384        debug.schedBreak(int(when))
385
386    if options.debug_flags:
387        _check_tracing()
388
389        on_flags = []
390        off_flags = []
391        for flag in options.debug_flags:
392            off = False
393            if flag.startswith('-'):
394                flag = flag[1:]
395                off = True
396
397            if flag not in debug.flags:
398                print("invalid debug flag '%s'" % flag, file=sys.stderr)
399                sys.exit(1)
400
401            if off:
402                debug.flags[flag].disable()
403            else:
404                debug.flags[flag].enable()
405
406    if options.debug_start:
407        _check_tracing()
408        e = event.create(trace.enable, event.Event.Debug_Enable_Pri)
409        event.mainq.schedule(e, options.debug_start)
410    else:
411        trace.enable()
412
413    if options.debug_end:
414        _check_tracing()
415        e = event.create(trace.disable, event.Event.Debug_Enable_Pri)
416        event.mainq.schedule(e, options.debug_end)
417
418    trace.output(options.debug_file)
419
420    for ignore in options.debug_ignore:
421        _check_tracing()
422        trace.ignore(ignore)
423
424    sys.argv = arguments
425    sys.path = [ os.path.dirname(sys.argv[0]) ] + sys.path
426
427    filename = sys.argv[0]
428    filedata = open(filename, 'r').read()
429    filecode = compile(filedata, filename, 'exec')
430    scope = { '__file__' : filename,
431              '__name__' : '__m5_main__' }
432
433    # if pdb was requested, execfile the thing under pdb, otherwise,
434    # just do the execfile normally
435    if options.pdb:
436        import pdb
437        import traceback
438
439        pdb = pdb.Pdb()
440        try:
441            pdb.run(filecode, scope)
442        except SystemExit:
443            print("The program exited via sys.exit(). Exit status: ", end=' ')
444            print(sys.exc_info()[1])
445        except:
446            traceback.print_exc()
447            print("Uncaught exception. Entering post mortem debugging")
448            t = sys.exc_info()[2]
449            while t.tb_next is not None:
450                t = t.tb_next
451                pdb.interaction(t.tb_frame,t)
452    else:
453        exec(filecode, scope)
454
455    # once the script is done
456    if options.interactive:
457        interact(scope)
458
459if __name__ == '__main__':
460    from pprint import pprint
461
462    options, arguments = parse_options()
463
464    print('opts:')
465    pprint(options, indent=4)
466    print()
467
468    print('args:')
469    pprint(arguments, indent=4)
470