From a49996a8c75721d8bfde36fb6ce70f4673be7940 Mon Sep 17 00:00:00 2001 From: stevenknight Date: Thu, 13 Jan 2005 18:28:05 +0000 Subject: [PATCH] Fix --debug=memoizer so it actually prints some stats on Python 2.x. git-svn-id: http://scons.tigris.org/svn/scons/trunk@1216 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- src/engine/SCons/Memoize.py | 70 +++++++++++++++-------------- src/engine/SCons/Script/Main.py | 1 - src/engine/SCons/Script/__init__.py | 19 ++++++++ test/option/debug-memoizer.py | 42 +++++++++++++++-- 4 files changed, 95 insertions(+), 37 deletions(-) diff --git a/src/engine/SCons/Memoize.py b/src/engine/SCons/Memoize.py index 1dc95cb9..55a10bac 100644 --- a/src/engine/SCons/Memoize.py +++ b/src/engine/SCons/Memoize.py @@ -378,8 +378,8 @@ class CounterEntry: import UserDict class Counter(UserDict.UserDict): - def __call__(self, klass, code): - k = (klass.__name__, id(code)) + def __call__(self, obj, methname): + k = obj.__class__.__name__ + '.' + methname try: return self[k] except KeyError: @@ -390,18 +390,13 @@ CacheCount = Counter() CacheCountSelf = Counter() CacheCountOne = Counter() -Code_to_Name = {} - def Dump(): items = CacheCount.items() + CacheCountSelf.items() + CacheCountOne.items() - def keyify(t): - return Code_to_Name[(t[0], t[1])] - items = map(lambda t, k=keyify: (k(t[0]), t[1]), items) items.sort() for k, v in items: print " %7d hits %7d misses %s()" % (v.hit, v.miss, k) -def Count_cache_get(func, cdict, args, kw): +def Count_cache_get(name, func, cdict, args, kw): """Called instead of name to see if this method call's return value has been cached. If it has, just return the cached value; if not, call the actual method and cache the return.""" @@ -410,7 +405,7 @@ def Count_cache_get(func, cdict, args, kw): ckey = obj._MeMoIZeR_Key + ':' + _MeMoIZeR_gen_key(args, kw) - c = CacheCount(obj.__class__, func) + c = CacheCount(obj, name) rval = cdict.get(ckey, "_MeMoIZeR") if rval is "_MeMoIZeR": rval = cdict[ckey] = apply(func, args, kw) @@ -420,7 +415,7 @@ def Count_cache_get(func, cdict, args, kw): return rval -def Count_cache_get_self(func, cdict, self): +def Count_cache_get_self(name, func, cdict, self): """Called instead of func(self) to see if this method call's return value has been cached. If it has, just return the cached value; if not, call the actual method and cache the return. @@ -429,7 +424,7 @@ def Count_cache_get_self(func, cdict, self): ckey = self._MeMoIZeR_Key - c = CacheCountSelf(self.__class__, func) + c = CacheCountSelf(self, name) rval = cdict.get(ckey, "_MeMoIZeR") if rval is "_MeMoIZeR": rval = cdict[ckey] = func(self) @@ -439,7 +434,7 @@ def Count_cache_get_self(func, cdict, self): return rval -def Count_cache_get_one(func, cdict, self, arg): +def Count_cache_get_one(name, func, cdict, self, arg): """Called instead of func(self, arg) to see if this method call's return value has been cached. If it has, just return the cached value; if not, call the actual method and cache the return. @@ -449,7 +444,7 @@ def Count_cache_get_one(func, cdict, self, arg): ckey = self._MeMoIZeR_Key + ':' + \ (getattr(arg, "_MeMoIZeR_Key", None) or repr(arg)) - c = CacheCountOne(self.__class__, func) + c = CacheCountOne(self, name) rval = cdict.get(ckey, "_MeMoIZeR") if rval is "_MeMoIZeR": rval = cdict[ckey] = func(self, arg) @@ -465,14 +460,30 @@ MCG_dict = { 'MCGO' : Memoizer_cache_get_one, } +MCG_lambda = "lambda *args, **kw: MCG(methcode, methcached, args, kw)" +MCGS_lambda = "lambda self: MCGS(methcode, methcached, self)" +MCGO_lambda = "lambda self, arg: MCGO(methcode, methcached, self, arg)" + def EnableCounting(): + """Enable counting of Memoizer hits and misses by overriding the + globals that hold the non-counting versions of the functions and + lambdas we call with the counting versions. + """ global MCG_dict + global MCG_lambda + global MCGS_lambda + global MCGO_lambda + MCG_dict = { 'MCG' : Count_cache_get, 'MCGS' : Count_cache_get_self, 'MCGO' : Count_cache_get_one, } + MCG_lambda = "lambda *args, **kw: MCG(methname, methcode, methcached, args, kw)" + MCGS_lambda = "lambda self: MCGS(methname, methcode, methcached, self)" + MCGO_lambda = "lambda self, arg: MCGO(methname, methcode, methcached, self, arg)" + class _Memoizer_Simple: @@ -585,32 +596,25 @@ def memoize_classdict(klass, modelklass, new_klassdict, cacheable, resetting): new_klassdict['_MeMoIZeR_converted'] = 1 for name,code in cacheable.items(): - Code_to_Name[(klass.__name__, id(code))] = klass.__name__ + '.' + name eval_dict = { + 'methname' : name, 'methcode' : code, 'methcached' : {}, } eval_dict.update(MCG_dict) - if code.func_code.co_argcount == 1 and \ - not code.func_code.co_flags & 0xC: - compiled = \ - compile("\n"*1 + - "lambda self: MCGS(methcode, methcached, self)", - whoami('cache_get_self', name), - "eval") - elif code.func_code.co_argcount == 2 and \ - not code.func_code.co_flags & 0xC: - compiled = \ - compile("\n"*2 + - "lambda self, arg: MCGO(methcode, methcached, self, arg)", - whoami('cache_get_one', name), - "eval") + fc = code.func_code + if fc.co_argcount == 1 and not fc.co_flags & 0xC: + compiled = compile("\n"*1 + MCGS_lambda, + whoami('cache_get_self', name), + "eval") + elif fc.co_argcount == 2 and not fc.co_flags & 0xC: + compiled = compile("\n"*2 + MCGO_lambda, + whoami('cache_get_one', name), + "eval") else: - compiled = \ - compile("\n"*3 + - "lambda *args, **kw: MCG(methcode, methcached, args, kw)", - whoami('cache_get', name), - "eval") + compiled = compile("\n"*3 + MCG_lambda, + whoami('cache_get', name), + "eval") newmethod = eval(compiled, eval_dict, {}) new_klassdict[name] = newmethod diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index f39375a9..71f6d618 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -434,7 +434,6 @@ def _set_globals(options): if "includes" in debug_values: print_includes = 1 if "memoizer" in debug_values: - SCons.Memoize.EnableCounting() print_memoizer = 1 if "memory" in debug_values: memory_stats = [] diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index d94fee2c..7368c8c8 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -39,9 +39,28 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import time start_time = time.time() +import os import string +import sys import UserList +# Special chicken-and-egg handling of the "--debug=memoizer" flags: +# SCons.Memoize contains a metaclass implementation that affects how +# the other classes are instantiated. The Memoizer handles optional +# counting of the hits and misses by using a different, parallel set of +# functions, so we don't slow down normal operation any more than we +# have to. But if we wait to enable the counting until we've parsed +# the command line options normally, it will be too late, because the +# Memoizer will have already analyzed the classes that it's Memoizing +# and bound the non-counting versions of the functions. So we have to +# use a special-case, up-front check for the "--debug=memoizer" flag +# and turn on Memoizer counting, if desired, before we import any of +# the other modules that use it. +sconsflags = string.split(os.environ.get('SCONSFLAGS', '')) +if "--debug=memoizer" in sys.argv + sconsflags: + import SCons.Memoize + SCons.Memoize.EnableCounting() + import SCons.Action import SCons.Builder import SCons.Environment diff --git a/test/option/debug-memoizer.py b/test/option/debug-memoizer.py index 2b79c118..4249ca6b 100644 --- a/test/option/debug-memoizer.py +++ b/test/option/debug-memoizer.py @@ -28,6 +28,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" Test calling the --debug=memoizer option. """ +import os import string import TestSCons @@ -43,11 +44,46 @@ env.Cat('file.out', 'file.in') test.write('file.in', "file.in\n") -test.run(arguments = '--debug=memoizer') +# The banner, and a list of representative method names that we expect +# to show up in the output. Of course, this depends on keeping those +# names in the implementation, so if we change them, we'll have to +# change this test... +expect = [ + "Memoizer (memory cache) hits and misses", + "Dir.exists()", + "Executor.get_contents()", + "File._save_str()", + "SConsEnvironment.get_calculator()", +] -expect = "Memoizer (memory cache) hits and misses" -test.fail_test(string.find(test.stdout(), expect) == -1) +test.run(arguments = '--debug=memoizer') +stdout = test.stdout() +missing = filter(lambda e, s=stdout: string.find(s, e) == -1, expect) +if missing: + print "Missing the following strings in the command line --debug=memoizer output:" + print " " + string.join(missing, "\n ") + print "STDOUT ============" + print stdout + test.fail_test(1) test.must_match('file.out', "file.in\n") + + +test.unlink("file.out") + +os.environ['SCONSFLAGS'] = '--debug=memoizer' + +test.run() +stdout = test.stdout() +missing = filter(lambda e, s=stdout: string.find(s, e) == -1, expect) +if missing: + print "Missing the following strings in the SCONSFLAGS=--debug=memoizer output:" + print " " + string.join(missing, "\n ") + print "STDOUT ============" + print stdout + test.fail_test(1) + + + test.pass_test() -- 2.26.2