diff options
Diffstat (limited to 'src/python')
29 files changed, 1492 insertions, 875 deletions
diff --git a/src/python/SConscript b/src/python/SConscript index b39c9ea9c..a767545ec 100644 --- a/src/python/SConscript +++ b/src/python/SConscript @@ -34,20 +34,28 @@ Import('*') Source('swig/pyevent.cc') Source('swig/pyobject.cc') +PySource('', 'importer.py') PySource('m5', 'm5/__init__.py') PySource('m5', 'm5/SimObject.py') -PySource('m5', 'm5/attrdict.py') +PySource('m5', 'm5/config.py') PySource('m5', 'm5/convert.py') +PySource('m5', 'm5/core.py') +PySource('m5', 'm5/debug.py') PySource('m5', 'm5/event.py') PySource('m5', 'm5/main.py') -PySource('m5', 'm5/multidict.py') +PySource('m5', 'm5/options.py') PySource('m5', 'm5/params.py') PySource('m5', 'm5/proxy.py') PySource('m5', 'm5/simulate.py') PySource('m5', 'm5/smartdict.py') PySource('m5', 'm5/stats.py') PySource('m5', 'm5/ticks.py') -PySource('m5', 'm5/util.py') +PySource('m5', 'm5/trace.py') +PySource('m5.util', 'm5/util/__init__.py') +PySource('m5.util', 'm5/util/attrdict.py') +PySource('m5.util', 'm5/util/jobfile.py') +PySource('m5.util', 'm5/util/misc.py') +PySource('m5.util', 'm5/util/multidict.py') SwigSource('m5.internal', 'swig/core.i') SwigSource('m5.internal', 'swig/debug.i') diff --git a/src/python/generate.py b/src/python/generate.py deleted file mode 100644 index eead6ff5d..000000000 --- a/src/python/generate.py +++ /dev/null @@ -1,529 +0,0 @@ -# Copyright (c) 2004-2006 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 - -import imp -import py_compile -import sys -import zipfile - -from os.path import basename -from os.path import exists - -class DictImporter(object): - '''This importer takes a dictionary of arbitrary module names that - map to arbitrary filenames.''' - def __init__(self, modules, build_env): - self.modules = modules - self.installed = set() - self.build_env = build_env - - def __del__(self): - self.unload() - - def unload(self): - import sys - for module in self.installed: - del sys.modules[module] - self.installed = set() - - def find_module(self, fullname, path): - if fullname == '__scons': - return self - - if fullname == 'm5.objects': - return self - - if fullname.startswith('m5.internal'): - return None - - if fullname in self.modules and exists(self.modules[fullname]): - return self - - return None - - def load_module(self, fullname): - mod = imp.new_module(fullname) - sys.modules[fullname] = mod - self.installed.add(fullname) - - mod.__loader__ = self - if fullname == 'm5.objects': - mod.__path__ = fullname.split('.') - return mod - - if fullname == '__scons': - mod.__dict__['m5_build_env'] = self.build_env - return mod - - srcfile = self.modules[fullname] - if basename(srcfile) == '__init__.py': - mod.__path__ = fullname.split('.') - mod.__file__ = srcfile - - exec file(srcfile, 'r') in mod.__dict__ - - return mod - -class ordered_dict(dict): - def keys(self): - keys = super(ordered_dict, self).keys() - keys.sort() - return keys - - def values(self): - return [ self[key] for key in self.keys() ] - - def items(self): - return [ (key,self[key]) for key in self.keys() ] - - def iterkeys(self): - for key in self.keys(): - yield key - - def itervalues(self): - for value in self.values(): - yield value - - def iteritems(self): - for key,value in self.items(): - yield key, value - -class Generate(object): - def __init__(self, py_sources, sim_objects, build_env): - self.py_sources = py_sources - self.py_modules = {} - for source in py_sources: - self.py_modules[source.modpath] = source.srcpath - - importer = DictImporter(self.py_modules, build_env) - - # install the python importer so we can grab stuff from the source - # tree itself. - sys.meta_path[0:0] = [ importer ] - - import m5 - self.m5 = m5 - - # import all sim objects so we can populate the all_objects list - # make sure that we're working with a list, then let's sort it - sim_objects = list(sim_objects) - sim_objects.sort() - for simobj in sim_objects: - exec('from m5.objects import %s' % simobj) - - # we need to unload all of the currently imported modules so that they - # will be re-imported the next time the sconscript is run - importer.unload() - sys.meta_path.remove(importer) - - self.sim_objects = m5.SimObject.allClasses - self.enums = m5.params.allEnums - - self.params = {} - for name,obj in self.sim_objects.iteritems(): - for param in obj._params.local.values(): - if not hasattr(param, 'swig_decl'): - continue - pname = param.ptype_str - if pname not in self.params: - self.params[pname] = param - - def createSimObjectParam(self, target, source, env): - assert len(target) == 1 and len(source) == 1 - - hh_file = file(target[0].abspath, 'w') - name = str(source[0].get_contents()) - obj = self.sim_objects[name] - - print >>hh_file, obj.cxx_decl() - - # Generate Python file containing a dict specifying the current - # build_env flags. - def makeDefinesPyFile(self, target, source, env): - f = file(str(target[0]), 'w') - print >>f, "m5_build_env = ", source[0] - f.close() - - # Generate python file containing info about the M5 source code - def makeInfoPyFile(self, target, source, env): - f = file(str(target[0]), 'w') - for src in source: - data = ''.join(file(src.srcnode().abspath, 'r').xreadlines()) - print >>f, "%s = %s" % (src, repr(data)) - f.close() - - # Generate the __init__.py file for m5.objects - def makeObjectsInitFile(self, target, source, env): - f = file(str(target[0]), 'w') - print >>f, 'from params import *' - print >>f, 'from m5.SimObject import *' - for module in source: - print >>f, 'from %s import *' % module.get_contents() - f.close() - - def createSwigParam(self, target, source, env): - assert len(target) == 1 and len(source) == 1 - - i_file = file(target[0].abspath, 'w') - name = str(source[0].get_contents()) - param = self.params[name] - - for line in param.swig_decl(): - print >>i_file, line - - def createEnumStrings(self, target, source, env): - assert len(target) == 1 and len(source) == 1 - - cc_file = file(target[0].abspath, 'w') - name = str(source[0].get_contents()) - obj = self.enums[name] - - print >>cc_file, obj.cxx_def() - cc_file.close() - - def createEnumParam(self, target, source, env): - assert len(target) == 1 and len(source) == 1 - - hh_file = file(target[0].abspath, 'w') - name = str(source[0].get_contents()) - obj = self.enums[name] - - print >>hh_file, obj.cxx_decl() - - def buildParams(self, target, source, env): - names = [ s.get_contents() for s in source ] - objs = [ self.sim_objects[name] for name in names ] - out = file(target[0].abspath, 'w') - - ordered_objs = [] - obj_seen = set() - def order_obj(obj): - name = str(obj) - if name in obj_seen: - return - - obj_seen.add(name) - if str(obj) != 'SimObject': - order_obj(obj.__bases__[0]) - - ordered_objs.append(obj) - - for obj in objs: - order_obj(obj) - - enums = set() - predecls = [] - pd_seen = set() - - def add_pds(*pds): - for pd in pds: - if pd not in pd_seen: - predecls.append(pd) - pd_seen.add(pd) - - for obj in ordered_objs: - params = obj._params.local.values() - for param in params: - ptype = param.ptype - if issubclass(ptype, self.m5.params.Enum): - if ptype not in enums: - enums.add(ptype) - pds = param.swig_predecls() - if isinstance(pds, (list, tuple)): - add_pds(*pds) - else: - add_pds(pds) - - print >>out, '%module params' - - print >>out, '%{' - for obj in ordered_objs: - print >>out, '#include "params/%s.hh"' % obj - print >>out, '%}' - - for pd in predecls: - print >>out, pd - - enums = list(enums) - enums.sort() - for enum in enums: - print >>out, '%%include "enums/%s.hh"' % enum.__name__ - print >>out - - for obj in ordered_objs: - if obj.swig_objdecls: - for decl in obj.swig_objdecls: - print >>out, decl - continue - - code = '' - base = obj.get_base() - - code += '// stop swig from creating/wrapping default ctor/dtor\n' - code += '%%nodefault %s;\n' % obj.cxx_class - code += 'class %s ' % obj.cxx_class - if base: - code += ': public %s' % base - code += ' {};\n' - - klass = obj.cxx_class; - if hasattr(obj, 'cxx_namespace'): - new_code = 'namespace %s {\n' % obj.cxx_namespace - new_code += code - new_code += '}\n' - code = new_code - klass = '%s::%s' % (obj.cxx_namespace, klass) - - print >>out, code - - for obj in ordered_objs: - print >>out, '%%include "params/%s.hh"' % obj - - def makeSwigInit(self, target, source, env): - f = file(str(target[0]), 'w') - print >>f, 'extern "C" {' - for module in source: - print >>f, ' void init_%s();' % module.get_contents() - print >>f, '}' - print >>f, 'void init_swig() {' - for module in source: - print >>f, ' init_%s();' % module.get_contents() - print >>f, '}' - f.close() - - def compilePyFile(self, target, source, env): - '''Action function to compile a .py into a .pyc''' - py_compile.compile(str(source[0]), str(target[0])) - - def buildPyZip(self, target, source, env): - '''Action function to build the zip archive. Uses the - PyZipFile module included in the standard Python library.''' - - py_compiled = {} - for s in self.py_sources: - compname = str(s.compiled) - assert compname not in py_compiled - py_compiled[compname] = s - - zf = zipfile.ZipFile(str(target[0]), 'w') - for s in source: - zipname = str(s) - arcname = py_compiled[zipname].arcname - zf.write(zipname, arcname) - zf.close() - - def traceFlagsPy(self, target, source, env): - assert(len(target) == 1) - - f = file(str(target[0]), 'w') - - allFlags = [] - for s in source: - val = eval(s.get_contents()) - allFlags.append(val) - - print >>f, 'baseFlags = [' - for flag, compound, desc in allFlags: - if not compound: - print >>f, " '%s'," % flag - print >>f, " ]" - print >>f - - print >>f, 'compoundFlags = [' - print >>f, " 'All'," - for flag, compound, desc in allFlags: - if compound: - print >>f, " '%s'," % flag - print >>f, " ]" - print >>f - - print >>f, "allFlags = frozenset(baseFlags + compoundFlags)" - print >>f - - print >>f, 'compoundFlagMap = {' - all = tuple([flag for flag,compound,desc in allFlags if not compound]) - print >>f, " 'All' : %s," % (all, ) - for flag, compound, desc in allFlags: - if compound: - print >>f, " '%s' : %s," % (flag, compound) - print >>f, " }" - print >>f - - print >>f, 'flagDescriptions = {' - print >>f, " 'All' : 'All flags'," - for flag, compound, desc in allFlags: - print >>f, " '%s' : '%s'," % (flag, desc) - print >>f, " }" - - f.close() - - def traceFlagsCC(self, target, source, env): - assert(len(target) == 1) - - f = file(str(target[0]), 'w') - - allFlags = [] - for s in source: - val = eval(s.get_contents()) - allFlags.append(val) - - # file header - print >>f, ''' -/* - * DO NOT EDIT THIS FILE! Automatically generated - */ - -#include "base/traceflags.hh" - -using namespace Trace; - -const char *Trace::flagStrings[] = -{''' - - # The string array is used by SimpleEnumParam to map the strings - # provided by the user to enum values. - for flag, compound, desc in allFlags: - if not compound: - print >>f, ' "%s",' % flag - - print >>f, ' "All",' - for flag, compound, desc in allFlags: - if compound: - print >>f, ' "%s",' % flag - - print >>f, '};' - print >>f - print >>f, 'const int Trace::numFlagStrings = %d;' % (len(allFlags) + 1) - print >>f - - # - # Now define the individual compound flag arrays. There is an array - # for each compound flag listing the component base flags. - # - all = tuple([flag for flag,compound,desc in allFlags if not compound]) - print >>f, 'static const Flags AllMap[] = {' - for flag, compound, desc in allFlags: - if not compound: - print >>f, " %s," % flag - print >>f, '};' - print >>f - - for flag, compound, desc in allFlags: - if not compound: - continue - print >>f, 'static const Flags %sMap[] = {' % flag - for flag in compound: - print >>f, " %s," % flag - print >>f, " (Flags)-1" - print >>f, '};' - print >>f - - # - # Finally the compoundFlags[] array maps the compound flags - # to their individual arrays/ - # - print >>f, 'const Flags *Trace::compoundFlags[] =' - print >>f, '{' - print >>f, ' AllMap,' - for flag, compound, desc in allFlags: - if compound: - print >>f, ' %sMap,' % flag - # file trailer - print >>f, '};' - - f.close() - - def traceFlagsHH(self, target, source, env): - assert(len(target) == 1) - - f = file(str(target[0]), 'w') - - allFlags = [] - for s in source: - val = eval(s.get_contents()) - allFlags.append(val) - - # file header boilerplate - print >>f, ''' -/* - * DO NOT EDIT THIS FILE! - * - * Automatically generated from traceflags.py - */ - -#ifndef __BASE_TRACE_FLAGS_HH__ -#define __BASE_TRACE_FLAGS_HH__ - -namespace Trace { - -enum Flags {''' - - # Generate the enum. Base flags come first, then compound flags. - idx = 0 - for flag, compound, desc in allFlags: - if not compound: - print >>f, ' %s = %d,' % (flag, idx) - idx += 1 - - numBaseFlags = idx - print >>f, ' NumFlags = %d,' % idx - - # put a comment in here to separate base from compound flags - print >>f, ''' -// The remaining enum values are *not* valid indices for Trace::flags. -// They are "compound" flags, which correspond to sets of base -// flags, and are used by changeFlag.''' - - print >>f, ' All = %d,' % idx - idx += 1 - for flag, compound, desc in allFlags: - if compound: - print >>f, ' %s = %d,' % (flag, idx) - idx += 1 - - numCompoundFlags = idx - numBaseFlags - print >>f, ' NumCompoundFlags = %d' % numCompoundFlags - - # trailer boilerplate - print >>f, '''\ -}; // enum Flags - -// Array of strings for SimpleEnumParam -extern const char *flagStrings[]; -extern const int numFlagStrings; - -// Array of arraay pointers: for each compound flag, gives the list of -// base flags to set. Inidividual flag arrays are terminated by -1. -extern const Flags *compoundFlags[]; - -/* namespace Trace */ } - -#endif // __BASE_TRACE_FLAGS_HH__ -''' - - f.close() diff --git a/src/python/importer.py b/src/python/importer.py new file mode 100644 index 000000000..fe099fdb8 --- /dev/null +++ b/src/python/importer.py @@ -0,0 +1,80 @@ +# Copyright (c) 2008 The Hewlett-Packard Development Company +# 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 + +# Simple importer that allows python to import data from a dict of +# code objects. The keys are the module path, and the items are the +# filename and bytecode of the file. +class CodeImporter(object): + def __init__(self): + self.modules = {} + + def add_module(self, filename, modpath, code): + if modpath in self.modules: + raise AttributeError, "%s already found in importer" + + self.modules[modpath] = (filename, code) + + def find_module(self, fullname, path): + if fullname in self.modules: + return self + + return None + + def load_module(self, fullname): + # Because the importer is created and initialized in its own + # little sandbox (in init.cc), the globals that were available + # when the importer module was loaded and CodeImporter was + # defined are not available when load_module is actually + # called. Soooo, the imports must live here. + import imp + import os + import sys + mod = imp.new_module(fullname) + sys.modules[fullname] = mod + + try: + mod.__loader__ = self + srcfile,code = self.modules[fullname] + if os.path.basename(srcfile) == '__init__.py': + mod.__path__ = fullname.split('.') + mod.__file__ = srcfile + + exec code in mod.__dict__ + except Exception: + del sys.modules[fullname] + raise + + return mod + +# Create an importer and add it to the meta_path so future imports can +# use it. There's currently nothing in the importer, but calls to +# add_module can be used to add code. +import sys +importer = CodeImporter() +add_module = importer.add_module +sys.meta_path.append(importer) diff --git a/src/python/m5/SimObject.py b/src/python/m5/SimObject.py index 78df6bef1..8ef22be4e 100644 --- a/src/python/m5/SimObject.py +++ b/src/python/m5/SimObject.py @@ -27,12 +27,13 @@ # Authors: Steve Reinhardt # Nathan Binkert -import sys, types +import math +import sys +import types import proxy import m5 from util import * -from multidict import multidict # These utility functions have to come first because they're # referenced in params.py... otherwise they won't be defined when we @@ -64,6 +65,7 @@ from params import * # There are a few things we need that aren't in params.__all__ since # normal users don't need them from params import ParamDesc, VectorParamDesc, isNullPointer, SimObjVector +from proxy import * noDot = False try: @@ -124,7 +126,6 @@ instanceDict = {} class MetaSimObject(type): # Attributes that can be set only at initialization time init_keywords = { 'abstract' : types.BooleanType, - 'cxx_namespace' : types.StringType, 'cxx_class' : types.StringType, 'cxx_type' : types.StringType, 'cxx_predecls' : types.ListType, @@ -191,33 +192,31 @@ class MetaSimObject(type): # the following is not true is when we define the SimObject # class itself (in which case the multidicts have no parent). if isinstance(base, MetaSimObject): + cls._base = base cls._params.parent = base._params cls._ports.parent = base._ports cls._values.parent = base._values cls._port_refs.parent = base._port_refs # mark base as having been subclassed base._instantiated = True + else: + cls._base = None # default keyword values if 'type' in cls._value_dict: - _type = cls._value_dict['type'] if 'cxx_class' not in cls._value_dict: - cls._value_dict['cxx_class'] = _type + cls._value_dict['cxx_class'] = cls._value_dict['type'] - namespace = cls._value_dict.get('cxx_namespace', None) - - _cxx_class = cls._value_dict['cxx_class'] - if 'cxx_type' not in cls._value_dict: - t = _cxx_class + '*' - if namespace: - t = '%s::%s' % (namespace, t) - cls._value_dict['cxx_type'] = t + cls._value_dict['cxx_type'] = '%s *' % cls._value_dict['cxx_class'] + if 'cxx_predecls' not in cls._value_dict: # A forward class declaration is sufficient since we are # just declaring a pointer. - decl = 'class %s;' % _cxx_class - if namespace: - decl = 'namespace %s { %s }' % (namespace, decl) + class_path = cls._value_dict['cxx_class'].split('::') + class_path.reverse() + decl = 'class %s;' % class_path[0] + for ns in class_path[1:]: + decl = 'namespace %s { %s }' % (ns, decl) cls._value_dict['cxx_predecls'] = [decl] if 'swig_predecls' not in cls._value_dict: @@ -349,12 +348,6 @@ class MetaSimObject(type): def __str__(cls): return cls.__name__ - def get_base(cls): - if str(cls) == 'SimObject': - return None - - return cls.__bases__[0].type - def cxx_decl(cls): code = "#ifndef __PARAMS__%s\n" % cls code += "#define __PARAMS__%s\n\n" % cls @@ -385,22 +378,29 @@ class MetaSimObject(type): code += "\n".join(predecls2) code += "\n\n"; - base = cls.get_base() - if base: - code += '#include "params/%s.hh"\n\n' % base + if cls._base: + code += '#include "params/%s.hh"\n\n' % cls._base.type for ptype in ptypes: if issubclass(ptype, Enum): code += '#include "enums/%s.hh"\n' % ptype.__name__ code += "\n\n" + code += cls.cxx_struct(cls._base, params) + + # close #ifndef __PARAMS__* guard + code += "\n#endif\n" + return code + + def cxx_struct(cls, base, params): + if cls == SimObject: + return '#include "sim/sim_object_params.hh"\n' + # now generate the actual param struct - code += "struct %sParams" % cls + code = "struct %sParams" % cls if base: - code += " : public %sParams" % base + code += " : public %sParams" % base.type code += "\n{\n" - if cls == SimObject: - code += " virtual ~%sParams() {}\n" % cls if not hasattr(cls, 'abstract') or not cls.abstract: if 'type' in cls.__dict__: code += " %s create();\n" % cls.cxx_type @@ -409,28 +409,9 @@ class MetaSimObject(type): code += "".join([" %s\n" % d for d in decls]) code += "};\n" - # close #ifndef __PARAMS__* guard - code += "\n#endif\n" - return code - - def cxx_type_decl(cls): - base = cls.get_base() - code = '' - - if base: - code += '#include "%s_type.h"\n' % base - - # now generate dummy code for inheritance - code += "struct %s" % cls.cxx_class - if base: - code += " : public %s" % base.cxx_class - code += "\n{};\n" - return code def swig_decl(cls): - base = cls.get_base() - code = '%%module %s\n' % cls code += '%{\n' @@ -458,8 +439,8 @@ class MetaSimObject(type): code += "\n".join(predecls2) code += "\n\n"; - if base: - code += '%%import "params/%s.i"\n\n' % base + if cls._base: + code += '%%import "params/%s.i"\n\n' % cls._base.type for ptype in ptypes: if issubclass(ptype, Enum): @@ -481,7 +462,6 @@ class SimObject(object): type = 'SimObject' abstract = True - name = Param.String("Object name") swig_objdecls = [ '%include "python/swig/sim_object.i"' ] # Initialize new instance. For objects with SimObject-valued @@ -650,8 +630,9 @@ class SimObject(object): if len(value) == 1: value[0]._maybe_set_parent(self, attr) else: + width = int(math.ceil(math.log(len(value))/math.log(10))) for i,v in enumerate(value): - v._maybe_set_parent(self, "%s%d" % (attr, i)) + v._maybe_set_parent(self, "%s%0*d" % (attr, width, i)) self._values[attr] = value @@ -687,7 +668,7 @@ class SimObject(object): match_obj = self._values[pname] if found_obj != None and found_obj != match_obj: raise AttributeError, \ - 'parent.any matched more than one: %s' % obj.path + 'parent.any matched more than one: %s and %s' % (found_obj.path, match_obj.path) found_obj = match_obj return found_obj, found_obj != None @@ -722,7 +703,7 @@ class SimObject(object): self._children[child].unproxy_all() def print_ini(self, ini_file): - print >>ini_file, '[' + self.path() + ']' # .ini section header + print >>ini_file, '[' + self.path() + ']' # .ini section header instanceDict[self.path()] = self @@ -749,7 +730,7 @@ class SimObject(object): if port != None: print >>ini_file, '%s=%s' % (port_name, port.ini_str()) - print >>ini_file # blank line between objects + print >>ini_file # blank line between objects for child in child_names: self._children[child].print_ini(ini_file) @@ -760,7 +741,7 @@ class SimObject(object): cc_params_struct = getattr(m5.objects.params, '%sParams' % self.type) cc_params = cc_params_struct() - cc_params.object = self + cc_params.pyobj = self cc_params.name = str(self) param_names = self._params.keys() @@ -768,7 +749,8 @@ class SimObject(object): for param in param_names: value = self._values.get(param) if value is None: - continue + m5.fatal("%s.%s without default or user set value", + self.path(), param) value = value.getValue() if isinstance(self._params[param], VectorParamDesc): diff --git a/src/python/m5/__init__.py b/src/python/m5/__init__.py index f21bb362e..733258acf 100644 --- a/src/python/m5/__init__.py +++ b/src/python/m5/__init__.py @@ -36,8 +36,31 @@ import smartdict MaxTick = 2**63 - 1 # define this here so we can use it right away if necessary -def panic(string): - print >>sys.stderr, 'panic:', string + +def errorURL(prefix, s): + try: + import zlib + hashstr = "%x" % zlib.crc32(s) + except: + hashstr = "UnableToHash" + return "For more information see: http://www.m5sim.org/%s/%s" % \ + (prefix, hashstr) + + +# panic() should be called when something happens that should never +# ever happen regardless of what the user does (i.e., an acutal m5 +# bug). +def panic(fmt, *args): + print >>sys.stderr, 'panic:', fmt % args + print >>sys.stderr, errorURL('panic',fmt) + sys.exit(1) + +# fatal() should be called when the simulation cannot continue due to +# some condition that is the user's fault (bad configuration, invalid +# arguments, etc.) and not a simulator bug. +def fatal(fmt, *args): + print >>sys.stderr, 'fatal:', fmt % args + print >>sys.stderr, errorURL('fatal',fmt) sys.exit(1) # force scalars to one-element lists for uniformity @@ -77,22 +100,23 @@ env.update(os.environ) # importing *you*). try: import internal - running_m5 = True except ImportError: - running_m5 = False + internal = None + +import defines +build_env.update(defines.buildEnv) -if running_m5: - import defines - build_env.update(defines.m5_build_env) -else: - import __scons - build_env.update(__scons.m5_build_env) +if internal: + defines.compileDate = internal.core.compileDate + for k,v in internal.core.__dict__.iteritems(): + if k.startswith('flag_'): + setattr(defines, k[5:], v) -if running_m5: from event import * from simulate import * - from main import options + from main import options, main import stats + import core import SimObject import params diff --git a/src/python/m5/config.py b/src/python/m5/config.py new file mode 100644 index 000000000..c28f6675a --- /dev/null +++ b/src/python/m5/config.py @@ -0,0 +1,50 @@ +# Copyright (c) 2008 The Hewlett-Packard Development Company +# 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 + +import os +from os.path import isdir, isfile, join as joinpath + + +confdir = os.environ.get('M5_CONFIG') + +if not confdir: + # HOME is not set when running regressions, due to use of scons + # Execute() function. + homedir = os.environ.get('HOME') + if homedir and isdir(joinpath(homedir, '.m5')): + confdir = joinpath(homedir, '.m5') + +def get(name): + if not confdir: + return None + conffile = joinpath(confdir, name) + if not isfile(conffile): + return None + + return conffile + diff --git a/src/python/m5/core.py b/src/python/m5/core.py new file mode 100644 index 000000000..1d7985be6 --- /dev/null +++ b/src/python/m5/core.py @@ -0,0 +1,40 @@ +# Copyright (c) 2008 The Hewlett-Packard Development Company +# 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 + +import internal +from internal.core import initAll, regAllStats + +def setOutputDir(dir): + internal.core.setOutputDir(dir) + +def initAll(): + internal.core.initAll() + +def regAllStats(): + internal.core.regAllStats() + diff --git a/src/python/m5/debug.py b/src/python/m5/debug.py new file mode 100644 index 000000000..cd40b8fa3 --- /dev/null +++ b/src/python/m5/debug.py @@ -0,0 +1,31 @@ +# Copyright (c) 2008 The Hewlett-Packard Development Company +# 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 + +import internal + +from internal.debug import schedBreakCycle, setRemoteGDBPort diff --git a/src/python/m5/event.py b/src/python/m5/event.py index 2d6497464..35095599d 100644 --- a/src/python/m5/event.py +++ b/src/python/m5/event.py @@ -26,17 +26,36 @@ # # Authors: Nathan Binkert -from internal.event import create -from internal.event import SimLoopExitEvent as SimExit +import m5 +import internal.event + +from internal.event import PythonEvent, SimLoopExitEvent as SimExit + +mainq = internal.event.cvar.mainEventQueue + +def create(obj, priority=None): + if priority is None: + priority = Event.Default_Pri + return PythonEvent(obj, priority) -class ProgressEvent(object): - def __init__(self, period): - self.period = int(period) - self.schedule() - def schedule(self): - create(self, m5.curTick() + self.period) +# As a reminder, priorities found in sim/eventq.hh are stuck into the +# Event class by swig +class Event(PythonEvent): + def __init__(self, priority=None): + if priority is None: + priority = Event.Default_Pri + super(Event, self).__init__(self, priority) + +class ProgressEvent(Event): + def __init__(self, eventq, period): + super(ProgressEvent, self).__init__() + self.period = int(period) + self.eventq = eventq + self.eventq.schedule(self, m5.curTick() + self.period) def __call__(self): print "Progress! Time now %fs" % (m5.curTick()/1e12) - self.schedule() + self.eventq.schedule(self, m5.curTick() + self.period) + +__all__ = [ 'create', 'Event', 'ProgressEvent', 'SimExit', 'mainq' ] diff --git a/src/python/m5/main.py b/src/python/m5/main.py index 4bcca46d2..98019b197 100644 --- a/src/python/m5/main.py +++ b/src/python/m5/main.py @@ -28,14 +28,13 @@ import code import datetime -import optparse import os import socket import sys -from attrdict import attrdict -import defines -import traceflags +from util import attrdict +import config +from options import OptionParser __all__ = [ 'options', 'arguments', 'main' ] @@ -47,77 +46,11 @@ The Regents of The University of Michigan All Rights Reserved ''' -def print_list(items, indent=4): - line = ' ' * indent - for i,item in enumerate(items): - if len(line) + len(item) > 76: - print line - line = ' ' * indent - - if i < len(items) - 1: - line += '%s, ' % item - else: - line += item - print line - -# there's only one option parsing done, so make it global and add some -# helper functions to make it work well. -parser = optparse.OptionParser(usage=usage, version=version, - description=brief_copyright, - formatter=optparse.TitledHelpFormatter()) -parser.disable_interspersed_args() - -# current option group -group = None - -def set_group(*args, **kwargs): - '''set the current option group''' - global group - if not args and not kwargs: - group = None - else: - group = parser.add_option_group(*args, **kwargs) - -class splitter(object): - def __init__(self, split): - self.split = split - def __call__(self, option, opt_str, value, parser): - getattr(parser.values, option.dest).extend(value.split(self.split)) - -def add_option(*args, **kwargs): - '''add an option to the current option group, or global none set''' - - # if action=split, but allows the option arguments - # themselves to be lists separated by the split variable''' - - if kwargs.get('action', None) == 'append' and 'split' in kwargs: - split = kwargs.pop('split') - kwargs['default'] = [] - kwargs['type'] = 'string' - kwargs['action'] = 'callback' - kwargs['callback'] = splitter(split) - - if group: - return group.add_option(*args, **kwargs) - - return parser.add_option(*args, **kwargs) - -def bool_option(name, default, help): - '''add a boolean option called --name and --no-name. - Display help depending on which is the default''' - - tname = '--%s' % name - fname = '--no-%s' % name - dest = name.replace('-', '_') - if default: - thelp = optparse.SUPPRESS_HELP - fhelp = help - else: - thelp = help - fhelp = optparse.SUPPRESS_HELP - - add_option(tname, action="store_true", default=default, help=thelp) - add_option(fname, action="store_false", dest=dest, help=fhelp) +options = OptionParser(usage=usage, version=version, + description=brief_copyright) +add_option = options.add_option +set_group = options.set_group +usage = options.usage # Help options add_option('-A', "--authors", action="store_true", default=False, @@ -132,8 +65,16 @@ add_option('-N', "--release-notes", action="store_true", default=False, help="Show the release notes") # Options for configuring the base simulator -add_option('-d', "--outdir", metavar="DIR", default=".", +add_option('-d', "--outdir", metavar="DIR", default="m5out", help="Set the output directory to DIR [Default: %default]") +add_option('-r', "--redirect-stdout", action="store_true", default=False, + help="Redirect stdout (& stderr, without -e) to file") +add_option('-e', "--redirect-stderr", action="store_true", default=False, + help="Redirect stderr to file") +add_option("--stdout-file", metavar="FILE", default="simout", + help="Filename for -r redirection [Default: %default]") +add_option("--stderr-file", metavar="FILE", default="simerr", + help="Filename for -e redirection [Default: %default]") add_option('-i', "--interactive", action="store_true", default=False, help="Invoke the interactive interpreter after running the script") add_option("--pdb", action="store_true", default=False, @@ -147,13 +88,20 @@ add_option('-v', "--verbose", action="count", default=0, # Statistics options set_group("Statistics Options") -add_option("--stats-file", metavar="FILE", default="m5stats.txt", +add_option("--stats-file", metavar="FILE", default="stats.txt", help="Sets the output file for statistics [Default: %default]") +# Configuration Options +set_group("Configuration Options") +add_option("--dump-config", metavar="FILE", default="config.ini", + help="Dump configuration output file [Default: %default]") + # Debugging options set_group("Debugging Options") add_option("--debug-break", metavar="TIME[,TIME]", action='append', split=',', help="Cycle to create a breakpoint") +add_option("--remote-gdb-port", type='int', default=7000, + help="Remote gdb base port") # Tracing options set_group("Trace Options") @@ -168,39 +116,61 @@ add_option("--trace-file", metavar="FILE", default="cout", add_option("--trace-ignore", metavar="EXPR", action='append', split=':', help="Ignore EXPR sim objects") -options = attrdict() -arguments = [] +# Help options +set_group("Help Options") +add_option("--list-sim-objects", action='store_true', default=False, + help="List all built-in SimObjects, their parameters and default values") -def usage(exitcode=None): - parser.print_help() - if exitcode is not None: - sys.exit(exitcode) +def main(): + import core + import debug + import defines + import event + import info + import stats + import trace -def parse_args(): - _opts,args = parser.parse_args() - opts = attrdict(_opts.__dict__) + def check_tracing(): + if defines.TRACING_ON: + return - # setting verbose and quiet at the same time doesn't make sense - if opts.verbose > 0 and opts.quiet > 0: - usage(2) + fatal("Tracing is not enabled. Compile with TRACING_ON") - # store the verbosity in a single variable. 0 is default, - # negative numbers represent quiet and positive values indicate verbose - opts.verbose -= opts.quiet + # load the options.py config file to allow people to set their own + # default options + options_file = config.get('options.py') + if options_file: + scope = { 'options' : options } + execfile(options_file, scope) - del opts.quiet + arguments = options.parse_args() - options.update(opts) - arguments.extend(args) - return opts,args + if not os.path.isdir(options.outdir): + os.makedirs(options.outdir) -def main(): - import defines - import event - import info - import internal + # These filenames are used only if the redirect_std* options are set + stdout_file = os.path.join(options.outdir, options.stdout_file) + stderr_file = os.path.join(options.outdir, options.stderr_file) - parse_args() + # Print redirection notices here before doing any redirection + if options.redirect_stdout and not options.redirect_stderr: + print "Redirecting stdout and stderr to", stdout_file + else: + if options.redirect_stdout: + print "Redirecting stdout to", stdout_file + if options.redirect_stderr: + print "Redirecting stderr to", stderr_file + + # Now redirect stdout/stderr as desired + if options.redirect_stdout: + redir_fd = os.open(stdout_file, os. O_WRONLY | os.O_CREAT | os.O_TRUNC) + os.dup2(redir_fd, sys.stdout.fileno()) + if not options.redirect_stderr: + os.dup2(redir_fd, sys.stderr.fileno()) + + if options.redirect_stderr: + redir_fd = os.open(stderr_file, os. O_WRONLY | os.O_CREAT | os.O_TRUNC) + os.dup2(redir_fd, sys.stderr.fileno()) done = False @@ -208,14 +178,13 @@ def main(): done = True print 'Build information:' print - print 'compiled %s' % internal.core.cvar.compileDate; - print 'started %s' % datetime.datetime.now().ctime() - print 'executing on %s' % socket.gethostname() + print 'compiled %s' % defines.compileDate; + print "revision %s" % defines.hgRev print 'build options:' - keys = defines.m5_build_env.keys() + keys = defines.buildEnv.keys() keys.sort() for key in keys: - val = defines.m5_build_env[key] + val = defines.buildEnv[key] print ' %s = %s' % (key, val) print @@ -247,27 +216,49 @@ def main(): if options.trace_help: done = True - print "Base Flags:" - print_list(traceflags.baseFlags, indent=4) - print - print "Compound Flags:" - for flag in traceflags.compoundFlags: - if flag == 'All': - continue - print " %s:" % flag - print_list(traceflags.compoundFlagMap[flag], indent=8) + check_tracing() + trace.help() + + if options.list_sim_objects: + import SimObject + done = True + print "SimObjects:" + objects = SimObject.allClasses.keys() + objects.sort() + for name in objects: + obj = SimObject.allClasses[name] + print " %s" % obj + params = obj._params.keys() + params.sort() + for pname in params: + param = obj._params[pname] + default = getattr(param, 'default', '') + print " %s" % pname + if default: + print " default: %s" % default + print " desc: %s" % param.desc + print print if done: sys.exit(0) + # setting verbose and quiet at the same time doesn't make sense + if options.verbose > 0 and options.quiet > 0: + options.usage(2) + + verbose = options.verbose - options.quiet if options.verbose >= 0: print "M5 Simulator System" print brief_copyright print - print "M5 compiled %s" % internal.core.cvar.compileDate; - print "M5 started %s" % datetime.datetime.now().ctime() + + print "M5 compiled %s" % defines.compileDate; + print "M5 revision %s" % defines.hgRev + + print "M5 started %s" % datetime.datetime.now().strftime("%b %e %Y %X") print "M5 executing on %s" % socket.gethostname() + print "command line:", for argv in sys.argv: print argv, @@ -278,61 +269,67 @@ def main(): if arguments and not os.path.isfile(arguments[0]): print "Script %s not found" % arguments[0] - usage(2) + options.usage(2) # tell C++ about output directory - internal.core.setOutputDir(options.outdir) + core.setOutputDir(options.outdir) # update the system path with elements from the -p option sys.path[0:0] = options.path - import objects - # set stats options - internal.stats.initText(options.stats_file) + stats.initText(options.stats_file) # set debugging options + debug.setRemoteGDBPort(options.remote_gdb_port) for when in options.debug_break: - internal.debug.schedBreakCycle(int(when)) - - on_flags = [] - off_flags = [] - for flag in options.trace_flags: - off = False - if flag.startswith('-'): - flag = flag[1:] - off = True - if flag not in traceflags.allFlags: - print >>sys.stderr, "invalid trace flag '%s'" % flag - sys.exit(1) - - if off: - off_flags.append(flag) - else: - on_flags.append(flag) - - for flag in on_flags: - internal.trace.set(flag) - - for flag in off_flags: - internal.trace.clear(flag) + debug.schedBreakCycle(int(when)) + + if options.trace_flags: + check_tracing() + + on_flags = [] + off_flags = [] + for flag in options.trace_flags: + off = False + if flag.startswith('-'): + flag = flag[1:] + off = True + if flag not in trace.flags.all and flag != "All": + print >>sys.stderr, "invalid trace flag '%s'" % flag + sys.exit(1) + + if off: + off_flags.append(flag) + else: + on_flags.append(flag) + + for flag in on_flags: + trace.set(flag) + + for flag in off_flags: + trace.clear(flag) if options.trace_start: - def enable_trace(): - internal.trace.cvar.enabled = True - event.create(enable_trace, int(options.trace_start)) + check_tracing() + e = event.create(trace.enable, event.Event.Trace_Enable_Pri) + event.mainq.schedule(e, options.trace_start) else: - internal.trace.cvar.enabled = True + trace.enable() - internal.trace.output(options.trace_file) + trace.output(options.trace_file) for ignore in options.trace_ignore: - internal.trace.ignore(ignore) + check_tracing() + trace.ignore(ignore) sys.argv = arguments sys.path = [ os.path.dirname(sys.argv[0]) ] + sys.path - scope = { '__file__' : sys.argv[0], + filename = sys.argv[0] + filedata = file(filename, 'r').read() + filecode = compile(filedata, filename, 'exec') + scope = { '__file__' : filename, '__name__' : '__m5_main__' } # we want readline if we're doing anything interactive @@ -342,11 +339,24 @@ def main(): # if pdb was requested, execfile the thing under pdb, otherwise, # just do the execfile normally if options.pdb: - from pdb import Pdb - debugger = Pdb() - debugger.run('execfile("%s")' % sys.argv[0], scope) + import pdb + import traceback + + pdb = pdb.Pdb() + try: + pdb.run(filecode, scope) + except SystemExit: + print "The program exited via sys.exit(). Exit status: ", + print sys.exc_info()[1] + except: + traceback.print_exc() + print "Uncaught exception. Entering post mortem debugging" + t = sys.exc_info()[2] + while t.tb_next is not None: + t = t.tb_next + pdb.interaction(t.tb_frame,t) else: - execfile(sys.argv[0], scope) + exec filecode in scope # once the script is done if options.interactive: @@ -356,7 +366,14 @@ def main(): if __name__ == '__main__': from pprint import pprint - parse_args() + # load the options.py config file to allow people to set their own + # default options + options_file = config.get('options.py') + if options_file: + scope = { 'options' : options } + execfile(options_file, scope) + + arguments = options.parse_args() print 'opts:' pprint(options, indent=4) diff --git a/src/python/m5/options.py b/src/python/m5/options.py new file mode 100644 index 000000000..1f534a314 --- /dev/null +++ b/src/python/m5/options.py @@ -0,0 +1,142 @@ +# Copyright (c) 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 + +import optparse +import sys + +from optparse import * + +class nodefault(object): pass + +class splitter(object): + def __init__(self, split): + self.split = split + def __call__(self, option, opt_str, value, parser): + values = value.split(self.split) + dest = getattr(parser.values, option.dest) + if dest is None: + setattr(parser.values, option.dest, values) + else: + dest.extend(values) + +class OptionParser(dict): + def __init__(self, *args, **kwargs): + kwargs.setdefault('formatter', optparse.TitledHelpFormatter()) + self._optparse = optparse.OptionParser(*args, **kwargs) + self._optparse.disable_interspersed_args() + + self._allopts = {} + + # current option group + self._group = self._optparse + + def set_defaults(self, *args, **kwargs): + return self._optparse.set_defaults(*args, **kwargs) + + def set_group(self, *args, **kwargs): + '''set the current option group''' + if not args and not kwargs: + self._group = self._optparse + else: + self._group = self._optparse.add_option_group(*args, **kwargs) + + def add_option(self, *args, **kwargs): + '''add an option to the current option group, or global none set''' + + # if action=split, but allows the option arguments + # themselves to be lists separated by the split variable''' + + if kwargs.get('action', None) == 'append' and 'split' in kwargs: + split = kwargs.pop('split') + kwargs['default'] = [] + kwargs['type'] = 'string' + kwargs['action'] = 'callback' + kwargs['callback'] = splitter(split) + + option = self._group.add_option(*args, **kwargs) + dest = option.dest + if dest not in self._allopts: + self._allopts[dest] = option + + return option + + def bool_option(self, name, default, help): + '''add a boolean option called --name and --no-name. + Display help depending on which is the default''' + + tname = '--%s' % name + fname = '--no-%s' % name + dest = name.replace('-', '_') + if default: + thelp = optparse.SUPPRESS_HELP + fhelp = help + else: + thelp = help + fhelp = optparse.SUPPRESS_HELP + + topt = self.add_option(tname, action="store_true", default=default, + help=thelp) + fopt = self.add_option(fname, action="store_false", dest=dest, + help=fhelp) + + return topt,fopt + + def __getattr__(self, attr): + if attr.startswith('_'): + return super(OptionParser, self).__getattribute__(attr) + + if attr in self: + return self[attr] + + return super(OptionParser, self).__getattribute__(attr) + + def __setattr__(self, attr, value): + if attr.startswith('_'): + super(OptionParser, self).__setattr__(attr, value) + elif attr in self._allopts: + defaults = { attr : value } + self.set_defaults(**defaults) + if attr in self: + self[attr] = value + else: + super(OptionParser, self).__setattr__(attr, value) + + def parse_args(self): + opts,args = self._optparse.parse_args() + + for key,val in opts.__dict__.iteritems(): + if val is not None or key not in self: + self[key] = val + + return args + + def usage(self, exitcode=None): + self._optparse.print_help() + if exitcode is not None: + sys.exit(exitcode) + diff --git a/src/python/m5/params.py b/src/python/m5/params.py index 241d4ceaf..18eeac0d1 100644 --- a/src/python/m5/params.py +++ b/src/python/m5/params.py @@ -166,6 +166,10 @@ class ParamDesc(object): class VectorParamValue(list): __metaclass__ = MetaParamValue + def __setattr__(self, attr, value): + raise AttributeError, \ + "Not allowed to set %s on '%s'" % (attr, type(self).__name__) + def ini_str(self): return ' '.join([v.ini_str() for v in self]) @@ -323,8 +327,8 @@ class CheckedIntType(MetaParamValue): if not (hasattr(cls, 'min') and hasattr(cls, 'max')): if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): panic("CheckedInt subclass %s must define either\n" \ - " 'min' and 'max' or 'size' and 'unsigned'\n" \ - % name); + " 'min' and 'max' or 'size' and 'unsigned'\n", + name); if cls.unsigned: cls.min = 0 cls.max = 2 ** cls.size - 1 @@ -379,6 +383,13 @@ class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 class Float(ParamValue, float): cxx_type = 'double' + def __init__(self, value): + if isinstance(value, (int, long, float, NumericParamValue, Float)): + self.value = float(value) + else: + raise TypeError, "Can't convert object of type %s to Float" \ + % type(value).__name__ + def getValue(self): return float(self.value) @@ -406,7 +417,6 @@ class MemorySize32(CheckedInt): class Addr(CheckedInt): cxx_type = 'Addr' - cxx_predecls = ['#include "arch/isa_traits.hh"'] size = 64 unsigned = True def __init__(self, value): @@ -886,7 +896,7 @@ class NetworkBandwidth(float,ParamValue): class MemoryBandwidth(float,ParamValue): cxx_type = 'float' - def __new__(self, value): + def __new__(cls, value): # we want the number of ticks per byte of data val = convert.toMemoryBandwidth(value) return super(cls, MemoryBandwidth).__new__(cls, val) @@ -896,7 +906,9 @@ class MemoryBandwidth(float,ParamValue): def getValue(self): # convert to seconds per byte - value = 1.0 / float(self) + value = float(self) + if value: + value = 1.0 / float(self) # convert to ticks per byte value = ticks.fromSeconds(value) return float(value) @@ -992,6 +1004,7 @@ class PortRef(object): if self.peer and not proxy.isproxy(self.peer): print "warning: overwriting port", self, \ "value", self.peer, "with", other + self.peer.peer = None self.peer = other if proxy.isproxy(other): other.set_param_desc(PortParamDesc()) @@ -1034,6 +1047,8 @@ class PortRef(object): if self.ccConnected: # already done this return peer = self.peer + if not self.peer: # nothing to connect to + return connectPorts(self.simobj.getCCObject(), self.name, self.index, peer.simobj.getCCObject(), peer.name, peer.index) self.ccConnected = True diff --git a/src/python/m5/simulate.py b/src/python/m5/simulate.py index 3d91da368..45992fe85 100644 --- a/src/python/m5/simulate.py +++ b/src/python/m5/simulate.py @@ -33,6 +33,8 @@ import sys # import the SWIG-wrapped main C++ functions import internal +import core +import stats from main import options import SimObject import ticks @@ -46,28 +48,29 @@ def instantiate(root): root.unproxy_all() - ini_file = file(os.path.join(options.outdir, 'config.ini'), 'w') - root.print_ini(ini_file) - ini_file.close() # close config.ini + if options.dump_config: + ini_file = file(os.path.join(options.outdir, options.dump_config), 'w') + root.print_ini(ini_file) + ini_file.close() # Initialize the global statistics - internal.stats.initSimStats() + stats.initSimStats() # Create the C++ sim objects and connect ports root.createCCObject() root.connectPorts() # Do a second pass to finish initializing the sim objects - internal.core.initAll() + core.initAll() # Do a third pass to initialize statistics - internal.core.regAllStats() + core.regAllStats() - # Check to make sure that the stats package is properly initialized - internal.stats.check() + # We're done registering statistics. Enable the stats package now. + stats.enable() # Reset to put the stats in a consistent state. - internal.stats.reset() + stats.reset() def doDot(root): dot = pydot.Dot() @@ -182,3 +185,5 @@ def switchCpus(cpuList): for old_cpu, new_cpu in cpuList: new_cpu.takeOverFrom(old_cpu) + +from internal.core import disableAllListeners diff --git a/src/python/m5/stats.py b/src/python/m5/stats.py index 041a3f58d..5bd9d5f6a 100644 --- a/src/python/m5/stats.py +++ b/src/python/m5/stats.py @@ -28,9 +28,6 @@ import internal -from internal.stats import dump -from internal.stats import initSimStats -from internal.stats import reset from internal.stats import StatEvent as event def initText(filename, desc=True, compat=True): @@ -44,3 +41,19 @@ def initMySQL(host, database, user='', passwd='', project='test', name='test', internal.stats.initMySQL(host, database, user, passwd, project, name, sample) + +def initSimStats(): + internal.stats.initSimStats() + +def enable(): + internal.stats.enable() + +def dump(): + # Currently prepare happens in the dump, but we should maybe move + # that out. + + #internal.stats.prepare() + internal.stats.dump() + +def reset(): + internal.stats.reset() diff --git a/src/python/m5/trace.py b/src/python/m5/trace.py new file mode 100644 index 000000000..17aa6196c --- /dev/null +++ b/src/python/m5/trace.py @@ -0,0 +1,52 @@ +# Copyright (c) 2008 The Hewlett-Packard Development Company +# 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 + +import internal +import traceflags as flags +import util + +from internal.trace import clear, output, set, ignore + +def disable(): + internal.trace.cvar.enabled = False + +def enable(): + internal.trace.cvar.enabled = True + +def help(): + print "Base Flags:" + for flag in flags.basic: + print " %s: %s" % (flag, flags.descriptions[flag]) + print + print "Compound Flags:" + for flag in flags.compound: + if flag == 'All': + continue + print " %s: %s" % (flag, flags.descriptions[flag]) + util.print_list(flags.compoundMap[flag], indent=8) + print diff --git a/src/python/m5/util/__init__.py b/src/python/m5/util/__init__.py new file mode 100644 index 000000000..5c4a066c6 --- /dev/null +++ b/src/python/m5/util/__init__.py @@ -0,0 +1,45 @@ +# Copyright (c) 2008 The Hewlett-Packard Development Company +# 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 + +from attrdict import attrdict, optiondict +from misc import * +from multidict import multidict +import jobfile + +def print_list(items, indent=4): + line = ' ' * indent + for i,item in enumerate(items): + if len(line) + len(item) > 76: + print line + line = ' ' * indent + + if i < len(items) - 1: + line += '%s, ' % item + else: + line += item + print line diff --git a/src/python/m5/attrdict.py b/src/python/m5/util/attrdict.py index 4ee7f1b8c..56f67217b 100644 --- a/src/python/m5/attrdict.py +++ b/src/python/m5/util/attrdict.py @@ -26,16 +26,17 @@ # # Authors: Nathan Binkert -__all__ = [ 'attrdict' ] +__all__ = [ 'attrdict', 'multiattrdict', 'optiondict' ] class attrdict(dict): + """Wrap dict, so you can use attribute access to get/set elements""" def __getattr__(self, attr): if attr in self: return self.__getitem__(attr) return super(attrdict, self).__getattribute__(attr) def __setattr__(self, attr, value): - if attr in dir(self): + if attr in dir(self) or attr.startswith('_'): return super(attrdict, self).__setattr__(attr, value) return self.__setitem__(attr, value) @@ -44,6 +45,25 @@ class attrdict(dict): return self.__delitem__(attr) return super(attrdict, self).__delattr__(attr, value) +class multiattrdict(attrdict): + """Wrap attrdict so that nested attribute accesses automatically create + nested dictionaries.""" + def __getattr__(self, attr): + try: + return super(multiattrdict, self).__getattr__(attr) + except AttributeError: + d = optiondict() + setattr(self, attr, d) + return d + +class optiondict(attrdict): + """Modify attrdict so that a missing attribute just returns None""" + def __getattr__(self, attr): + try: + return super(optiondict, self).__getattr__(attr) + except AttributeError: + return None + if __name__ == '__main__': x = attrdict() x.y = 1 @@ -59,3 +79,9 @@ if __name__ == '__main__': del x.z print dir(x) print(x) + + x = multiattrdict() + x.y.z = 9 + print x + print x.y + print x.y.z diff --git a/src/python/m5/util/jobfile.py b/src/python/m5/util/jobfile.py new file mode 100644 index 000000000..c830895f6 --- /dev/null +++ b/src/python/m5/util/jobfile.py @@ -0,0 +1,472 @@ +# Copyright (c) 2005-2006 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 + +import sys + +from attrdict import optiondict +from misc import crossproduct + +class Data(object): + def __init__(self, name, desc, **kwargs): + self.name = name + self.desc = desc + self.__dict__.update(kwargs) + + def update(self, obj): + if not isinstance(obj, Data): + raise AttributeError, "can only update from Data object" + + for key,val in obj.__dict__.iteritems(): + if key.startswith('_') or key in ('name', 'desc'): + continue + + if key not in self.__dict__: + self.__dict__[key] = val + continue + + if not isinstance(val, dict): + if self.__dict__[key] == val: + continue + + raise AttributeError, \ + "%s specified more than once old: %s new: %s" % \ + (key, self.__dict__[key], val) + + d = self.__dict__[key] + for k,v in val.iteritems(): + if k in d: + raise AttributeError, \ + "%s specified more than once in %s" % (k, key) + d[k] = v + + if hasattr(self, 'system') and hasattr(obj, 'system'): + if self.system != obj.system: + raise AttributeError, \ + "conflicting values for system: '%s'/'%s'" % \ + (self.system, obj.system) + + def printinfo(self): + if self.name: + print 'name: %s' % self.name + if self.desc: + print 'desc: %s' % self.desc + try: + if self.system: + print 'system: %s' % self.system + except AttributeError: + pass + + def printverbose(self): + for key in self: + val = self[key] + if isinstance(val, dict): + import pprint + val = pprint.pformat(val) + print '%-20s = %s' % (key, val) + print + + def __contains__(self, attr): + if attr.startswith('_'): + return False + return attr in self.__dict__ + + def __getitem__(self, key): + if key.startswith('_'): + raise KeyError, "Key '%s' not found" % attr + return self.__dict__[key] + + def __iter__(self): + keys = self.__dict__.keys() + keys.sort() + for key in keys: + if not key.startswith('_'): + yield key + + def optiondict(self): + result = optiondict() + for key in self: + result[key] = self[key] + return result + + def __repr__(self): + d = {} + for key,value in self.__dict__.iteritems(): + if not key.startswith('_'): + d[key] = value + + return "<%s: %s>" % (type(self).__name__, d) + + def __str__(self): + return self.name + +class Job(Data): + def __init__(self, options): + super(Job, self).__init__('', '') + + config = options[0]._config + for opt in options: + if opt._config != config: + raise AttributeError, \ + "All options are not from the same Configuration" + + self._config = config + self._groups = [ opt._group for opt in options ] + self._options = options + + self.update(self._config) + for group in self._groups: + self.update(group) + + self._is_checkpoint = True + + for option in self._options: + self.update(option) + if not option._group._checkpoint: + self._is_checkpoint = False + + if option._suboption: + self.update(option._suboption) + self._is_checkpoint = False + + names = [ ] + for opt in self._options: + if opt.name: + names.append(opt.name) + self.name = ':'.join(names) + + descs = [ ] + for opt in self._options: + if opt.desc: + descs.append(opt.desc) + self.desc = ', '.join(descs) + + self._checkpoint = None + if not self._is_checkpoint: + opts = [] + for opt in options: + cpt = opt._group._checkpoint + if not cpt: + continue + if isinstance(cpt, Option): + opt = cpt.clone(suboptions=False) + else: + opt = opt.clone(suboptions=False) + + opts.append(opt) + + if opts: + self._checkpoint = Job(opts) + + def clone(self): + return Job(self._options) + + def printinfo(self): + super(Job, self).printinfo() + if self._checkpoint: + print 'checkpoint: %s' % self._checkpoint.name + print 'config: %s' % self._config.name + print 'groups: %s' % [ g.name for g in self._groups ] + print 'options: %s' % [ o.name for o in self._options ] + super(Job, self).printverbose() + +class SubOption(Data): + def __init__(self, name, desc, **kwargs): + super(SubOption, self).__init__(name, desc, **kwargs) + self._number = None + +class Option(Data): + def __init__(self, name, desc, **kwargs): + super(Option, self).__init__(name, desc, **kwargs) + self._suboptions = [] + self._suboption = None + self._number = None + + def __getattribute__(self, attr): + if attr == 'name': + name = self.__dict__[attr] + if self._suboption is not None: + name = '%s:%s' % (name, self._suboption.name) + return name + + if attr == 'desc': + desc = [ self.__dict__[attr] ] + if self._suboption is not None and self._suboption.desc: + desc.append(self._suboption.desc) + return ', '.join(desc) + + return super(Option, self).__getattribute__(attr) + + def suboption(self, name, desc, **kwargs): + subo = SubOption(name, desc, **kwargs) + subo._config = self._config + subo._group = self._group + subo._option = self + subo._number = len(self._suboptions) + self._suboptions.append(subo) + return subo + + def clone(self, suboptions=True): + option = Option(self.__dict__['name'], self.__dict__['desc']) + option.update(self) + option._group = self._group + option._config = self._config + option._number = self._number + if suboptions: + option._suboptions.extend(self._suboptions) + option._suboption = self._suboption + return option + + def subopts(self): + if not self._suboptions: + return [ self ] + + subopts = [] + for subo in self._suboptions: + option = self.clone() + option._suboption = subo + subopts.append(option) + + return subopts + + def printinfo(self): + super(Option, self).printinfo() + print 'config: %s' % self._config.name + super(Option, self).printverbose() + +class Group(Data): + def __init__(self, name, desc, **kwargs): + super(Group, self).__init__(name, desc, **kwargs) + self._options = [] + self._number = None + self._checkpoint = False + + def option(self, name, desc, **kwargs): + opt = Option(name, desc, **kwargs) + opt._config = self._config + opt._group = self + opt._number = len(self._options) + self._options.append(opt) + return opt + + def options(self): + return self._options + + def subopts(self): + subopts = [] + for opt in self._options: + for subo in opt.subopts(): + subopts.append(subo) + return subopts + + def printinfo(self): + super(Group, self).printinfo() + print 'config: %s' % self._config.name + print 'options: %s' % [ o.name for o in self._options ] + super(Group, self).printverbose() + +class Configuration(Data): + def __init__(self, name, desc, **kwargs): + super(Configuration, self).__init__(name, desc, **kwargs) + self._groups = [] + self._posfilters = [] + self._negfilters = [] + + def group(self, name, desc, **kwargs): + grp = Group(name, desc, **kwargs) + grp._config = self + grp._number = len(self._groups) + self._groups.append(grp) + return grp + + def groups(self): + return self._groups + + def checkchildren(self, kids): + for kid in kids: + if kid._config != self: + raise AttributeError, "child from the wrong configuration" + + def sortgroups(self, groups): + groups = [ (grp._number, grp) for grp in groups ] + groups.sort() + return [ grp[1] for grp in groups ] + + def options(self, groups=None, checkpoint=False): + if groups is None: + groups = self._groups + self.checkchildren(groups) + groups = self.sortgroups(groups) + if checkpoint: + groups = [ grp for grp in groups if grp._checkpoint ] + optgroups = [ g.options() for g in groups ] + else: + optgroups = [ g.subopts() for g in groups ] + if not optgroups: + return + for options in crossproduct(optgroups): + for opt in options: + cpt = opt._group._checkpoint + if not isinstance(cpt, bool) and cpt != opt: + if checkpoint: + break + else: + yield options + else: + if checkpoint: + yield options + + def addfilter(self, filt, pos=True): + import re + filt = re.compile(filt) + if pos: + self._posfilters.append(filt) + else: + self._negfilters.append(filt) + + def jobfilter(self, job): + for filt in self._negfilters: + if filt.match(job.name): + return False + + if not self._posfilters: + return True + + for filt in self._posfilters: + if filt.match(job.name): + return True + + return False + + def checkpoints(self, groups=None): + for options in self.options(groups, True): + job = Job(options) + if self.jobfilter(job): + yield job + + def jobs(self, groups=None): + for options in self.options(groups, False): + job = Job(options) + if self.jobfilter(job): + yield job + + def alljobs(self, groups=None): + for options in self.options(groups, True): + yield Job(options) + for options in self.options(groups, False): + yield Job(options) + + def find(self, jobname): + for job in self.alljobs(): + if job.name == jobname: + return job + else: + raise AttributeError, "job '%s' not found" % jobname + + def job(self, options): + self.checkchildren(options) + options = [ (opt._group._number, opt) for opt in options ] + options.sort() + options = [ opt[1] for opt in options ] + job = Job(options) + return job + + def printinfo(self): + super(Configuration, self).printinfo() + print 'groups: %s' % [ g.name for g in self._groups ] + super(Configuration, self).printverbose() + +def JobFile(jobfile): + from os.path import expanduser, isfile, join as joinpath + filename = expanduser(jobfile) + + # Can't find filename in the current path, search sys.path + if not isfile(filename): + for path in sys.path: + testname = joinpath(path, filename) + if isfile(testname): + filename = testname + break + else: + raise AttributeError, \ + "Could not find file '%s'" % jobfile + + data = {} + execfile(filename, data) + if 'conf' not in data: + raise ImportError, 'cannot import name conf from %s' % jobfile + return data['conf'] + +def main(conf=None): + usage = 'Usage: %s [-b] [-c] [-v]' % sys.argv[0] + if conf is None: + usage += ' <jobfile>' + + try: + import getopt + opts, args = getopt.getopt(sys.argv[1:], '-bcv') + except getopt.GetoptError: + sys.exit(usage) + + both = False + checkpoint = False + verbose = False + for opt,arg in opts: + if opt == '-b': + both = True + checkpoint = True + if opt == '-c': + checkpoint = True + if opt == '-v': + verbose = True + + if conf is None: + if len(args) != 1: + raise AttributeError, usage + conf = JobFile(args[0]) + else: + if len(args) != 0: + raise AttributeError, usage + + if both: + jobs = conf.alljobs() + elif checkpoint: + jobs = conf.checkpoints() + else: + jobs = conf.jobs() + + for job in jobs: + if verbose: + job.printinfo() + else: + cpt = '' + if job._checkpoint: + cpt = job._checkpoint.name + print job.name, cpt + +if __name__ == '__main__': + main() diff --git a/src/python/m5/util.py b/src/python/m5/util/misc.py index 28b8b1b94..094e3ed9a 100644 --- a/src/python/m5/util.py +++ b/src/python/m5/util/misc.py @@ -56,4 +56,32 @@ def applyOrMap(objOrSeq, meth, *args, **kwargs): else: return [applyMethod(o, meth, *args, **kwargs) for o in objOrSeq] +def crossproduct(items): + if not isinstance(items, (list, tuple)): + raise AttributeError, 'crossproduct works only on sequences' + if not items: + yield None + return + + current = items[0] + remainder = items[1:] + + if not hasattr(current, '__iter__'): + current = [ current ] + + for item in current: + for rem in crossproduct(remainder): + data = [ item ] + if rem: + data += rem + yield data + +def flatten(items): + if not isinstance(items, (list, tuple)): + yield items + return + + for item in items: + for flat in flatten(item): + yield flat diff --git a/src/python/m5/multidict.py b/src/python/m5/util/multidict.py index b5cd700ef..b5cd700ef 100644 --- a/src/python/m5/multidict.py +++ b/src/python/m5/util/multidict.py diff --git a/src/python/m5/util/orderdict.py b/src/python/m5/util/orderdict.py new file mode 100644 index 000000000..3f755d299 --- /dev/null +++ b/src/python/m5/util/orderdict.py @@ -0,0 +1,80 @@ +# Copyright (c) 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 + +__all__ = [ 'orderdict' ] + +class orderdict(dict): + def __init__(self, d = {}): + self._keys = d.keys() + super(orderdict, self).__init__(d) + + def __setitem__(self, key, item): + super(orderdict, self).__setitem__(key, item) + if not hasattr(self, '_keys'): + self._keys = [key,] + if key not in self._keys: + self._keys.append(key) + + def __delitem__(self, key): + super(orderdict, self).__delitem__(key) + self._keys.remove(key) + + def clear(self): + super(orderdict, self).clear() + self._keys = [] + + def items(self): + for i in self._keys: + yield i, self[i] + + def keys(self): + return self._keys + + def popitem(self): + if len(self._keys) == 0: + raise KeyError('dictionary is empty') + else: + key = self._keys[-1] + val = self[key] + del self[key] + return key, val + + def setdefault(self, key, failobj = None): + super(orderdict, self).setdefault(key, failobj) + if key not in self._keys: + self._keys.append(key) + + def update(self, d): + for key in d.keys(): + if not self.has_key(key): + self._keys.append(key) + super(orderdict, self).update(d) + + def values(self): + for i in self._keys: + yield self[i] diff --git a/src/python/swig/core.i b/src/python/swig/core.i index 770765ca4..c567bea4d 100644 --- a/src/python/swig/core.i +++ b/src/python/swig/core.i @@ -34,11 +34,27 @@ %{ #include "python/swig/pyobject.hh" +#include "base/misc.hh" +#include "base/socket.hh" #include "sim/core.hh" #include "sim/host.hh" #include "sim/startup.hh" extern const char *compileDate; + +#ifdef DEBUG +const bool flag_DEBUG = true; +#else +const bool flag_DEBUG = false; +#endif +#ifdef NDEBUG +const bool flag_NDEBUG = true; +#else +const bool flag_NDEBUG = false; +#endif +const bool flag_TRACING_ON = TRACING_ON; + +inline void disableAllListeners() { ListenSocket::disableAll(); } %} %include "stdint.i" @@ -46,11 +62,15 @@ extern const char *compileDate; %include "sim/host.hh" void setOutputDir(const std::string &dir); -void setOutputFile(const std::string &file); void SimStartup(); void doExitCleanup(); +void disableAllListeners(); +%immutable compileDate; char *compileDate; +const bool flag_DEBUG; +const bool flag_NDEBUG; +const bool flag_TRACING_ON; void setClockFrequency(Tick ticksPerSecond); @@ -63,6 +83,10 @@ void unserializeAll(const std::string &cpt_dir); void initAll(); void regAllStats(); +bool want_warn, warn_verbose; +bool want_info, info_verbose; +bool want_hack, hack_verbose; + %wrapper %{ // fix up module name to reflect the fact that it's inside the m5 package #undef SWIG_name diff --git a/src/python/swig/debug.i b/src/python/swig/debug.i index b542e9f82..1084d6936 100644 --- a/src/python/swig/debug.i +++ b/src/python/swig/debug.i @@ -31,16 +31,13 @@ %module debug %{ -// include these files when compiling debug_wrap.cc #include "sim/host.hh" +#include "sim/debug.hh" %} %include "stdint.i" %include "sim/host.hh" - -%inline %{ -extern void schedBreakCycle(Tick when); -%} +%include "sim/debug.hh" %wrapper %{ // fix up module name to reflect the fact that it's inside the m5 package diff --git a/src/python/swig/event.i b/src/python/swig/event.i index 9a2093c99..b40e59a4b 100644 --- a/src/python/swig/event.i +++ b/src/python/swig/event.i @@ -32,34 +32,65 @@ %{ #include "python/swig/pyevent.hh" - +#include "sim/host.hh" +#include "sim/eventq.hh" #include "sim/sim_events.hh" #include "sim/sim_exit.hh" #include "sim/simulate.hh" %} +#pragma SWIG nowarn=350,351 + +%extend EventQueue { + void + schedule(Event *event, Tick when) + { + // Any python event that are scheduled must have their + // internal object's refcount incremented so that the object + // sticks around while it is in the event queue. + PythonEvent *pyevent = dynamic_cast<PythonEvent *>(event); + if (pyevent) + pyevent->incref(); + $self->schedule(event, when); + } + + void + deschedule(Event *event) + { + $self->deschedule(event); + + // Now that we're removing the python object from the event + // queue, we need to decrement its reference count. + PythonEvent *pyevent = dynamic_cast<PythonEvent *>(event); + if (pyevent) + pyevent->decref(); + } +} + +%ignore EventQueue::schedule; +%ignore EventQueue::deschedule; + +%import "base/fast_alloc.hh" +%import "sim/serialize.hh" + %include "stdint.i" %include "std_string.i" %include "sim/host.hh" +%include "sim/eventq.hh" +%include "python/swig/pyevent.hh" -void create(PyObject *object, Tick when); - -class Event; -class CountedDrainEvent : public Event { - public: +struct CountedDrainEvent : public Event +{ void setCount(int _count); }; -CountedDrainEvent *createCountedDrain(); -void cleanupCountedDrain(Event *drain_event); - // minimal definition of SimExitEvent interface to wrap -class SimLoopExitEvent { +class SimLoopExitEvent : public Event +{ public: std::string getCause(); int getCode(); - SimLoopExitEvent(EventQueue *q, Tick _when, Tick _repeat, - const std::string &_cause, int c = 0); + SimLoopExitEvent(const std::string &_cause, int c, Tick _repeat = 0); }; %exception simulate { diff --git a/src/python/swig/init.hh b/src/python/swig/init.hh deleted file mode 100644 index 23d2c19a9..000000000 --- a/src/python/swig/init.hh +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2006 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 - */ - -#ifndef __PYTHON_SWIG_INIT_HH__ -#define __PYTHON_SWIG_INIT_HH__ - -void init_swig(); - -#endif // __PYTHON_SWIG_INIT_HH__ diff --git a/src/python/swig/pyevent.cc b/src/python/swig/pyevent.cc index 7f23b8874..0695ed2d3 100644 --- a/src/python/swig/pyevent.cc +++ b/src/python/swig/pyevent.cc @@ -33,21 +33,15 @@ #include "python/swig/pyevent.hh" #include "sim/async.hh" -PythonEvent::PythonEvent(PyObject *obj, Tick when, Priority priority) - : Event(&mainEventQueue, priority), object(obj) +PythonEvent::PythonEvent(PyObject *obj, Priority priority) + : Event(priority), object(obj) { if (object == NULL) panic("Passed in invalid object"); - - Py_INCREF(object); - - setFlags(AutoDelete); - schedule(when); } PythonEvent::~PythonEvent() { - Py_DECREF(object); } void @@ -66,4 +60,27 @@ PythonEvent::process() async_event = true; async_exception = true; } + + // Since the object has been removed from the event queue, its + // reference count must be decremented. + Py_DECREF(object); +} + +CountedDrainEvent * +createCountedDrain() +{ + return new CountedDrainEvent(); +} + +void +cleanupCountedDrain(Event *counted_drain) +{ + CountedDrainEvent *event = + dynamic_cast<CountedDrainEvent *>(counted_drain); + if (event == NULL) { + fatal("Called cleanupCountedDrain() on an event that was not " + "a CountedDrainEvent."); + } + assert(event->getCount() == 0); + delete event; } diff --git a/src/python/swig/pyevent.hh b/src/python/swig/pyevent.hh index 65e80e9e4..9006a0404 100644 --- a/src/python/swig/pyevent.hh +++ b/src/python/swig/pyevent.hh @@ -40,35 +40,16 @@ class PythonEvent : public Event PyObject *object; public: - PythonEvent(PyObject *obj, Tick when, Priority priority = Default_Pri); + PythonEvent(PyObject *obj, Event::Priority priority); ~PythonEvent(); + void incref() { Py_INCREF(object); } + void decref() { Py_DECREF(object); } + virtual void process(); }; -inline void -create(PyObject *object, Tick when) -{ - new PythonEvent(object, when); -} - -inline Event * -createCountedDrain() -{ - return new CountedDrainEvent(); -} - -inline void -cleanupCountedDrain(Event *counted_drain) -{ - CountedDrainEvent *event = - dynamic_cast<CountedDrainEvent *>(counted_drain); - if (event == NULL) { - fatal("Called cleanupCountedDrain() on an event that was not " - "a CountedDrainEvent."); - } - assert(event->getCount() == 0); - delete event; -} +CountedDrainEvent *createCountedDrain(); +void cleanupCountedDrain(Event *counted_drain); #endif // __PYTHON_SWIG_PYEVENT_HH__ diff --git a/src/python/swig/range.i b/src/python/swig/range.i index 40809dae4..309e6a8ba 100644 --- a/src/python/swig/range.i +++ b/src/python/swig/range.i @@ -28,6 +28,8 @@ * Authors: Nathan Binkert */ +%rename(assign) *::operator=; + %include "base/range.hh" %include "sim/host.hh" diff --git a/src/python/swig/stats.i b/src/python/swig/stats.i index d36f82dbc..284df8ff8 100644 --- a/src/python/swig/stats.i +++ b/src/python/swig/stats.i @@ -48,7 +48,8 @@ void initMySQL(std::string host, std::string database, std::string user, void StatEvent(bool dump, bool reset, Tick when = curTick, Tick repeat = 0); -void check(); +void enable(); +void prepare(); void dump(); void reset(); |