1*699cd480SApple OSS Distributions""" 2*699cd480SApple OSS DistributionsA basic caching module for xnu debug macros to use. 3*699cd480SApple OSS Distributions 4*699cd480SApple OSS Distributions 5*699cd480SApple OSS DistributionsWhen to use caching? 6*699cd480SApple OSS Distributions~~~~~~~~~~~~~~~~~~~~ 7*699cd480SApple OSS Distributions 8*699cd480SApple OSS DistributionsVery often you do not need to: LLDB already provides extensive data caching. 9*699cd480SApple OSS Distributions 10*699cd480SApple OSS DistributionsThe most common things that need caching are: 11*699cd480SApple OSS Distributions- types (the gettype() function provides this) 12*699cd480SApple OSS Distributions- globals (kern.globals / kern.GetGlobalVariable() provides this) 13*699cd480SApple OSS Distributions 14*699cd480SApple OSS Distributions 15*699cd480SApple OSS DistributionsIf your macro is slow to get some data, before slapping a caching decorator, 16*699cd480SApple OSS Distributionsplease profile your code using `xnudebug profile`. Very often slowness happens 17*699cd480SApple OSS Distributionsdue to the usage of CreateValueFromExpression() which spins a full compiler 18*699cd480SApple OSS Distributionsto parse relatively trivial expressions and is easily 10-100x as slow as 19*699cd480SApple OSS Distributionsalternatives like CreateValueFromAddress(). 20*699cd480SApple OSS Distributions 21*699cd480SApple OSS DistributionsOnly use caching once you have eliminated those obvious performance hogs 22*699cd480SApple OSS Distributionsand an A/B shows meaningful speed improvements over the base line. 23*699cd480SApple OSS Distributions 24*699cd480SApple OSS Distributions 25*699cd480SApple OSS DistributionsI really need caching how does it work? 26*699cd480SApple OSS Distributions~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 27*699cd480SApple OSS Distributions 28*699cd480SApple OSS DistributionsThis module provides function decorators to easily cache the result of 29*699cd480SApple OSS Distributionsfunctions based on their parameters, while keeping the caches separated 30*699cd480SApple OSS Distributionsper lldb target. 31*699cd480SApple OSS Distributions 32*699cd480SApple OSS Distributions@cache_statically can be used to cache data once per target, 33*699cd480SApple OSS Distributionsbecause it won't change if the process is resumed and stopped again. 34*699cd480SApple OSS DistributionsFor example: types, constant values, global addresses, ... 35*699cd480SApple OSS Distributions 36*699cd480SApple OSS Distributions@cache_dynamically can be used to cache data that is expensive to compute 37*699cd480SApple OSS Distributionsbut which content depends on the state of the process. It will be 38*699cd480SApple OSS Distributionsautomatically invalidated for you if the process is resumed and 39*699cd480SApple OSS Distributionsthen stops or hits a breakpoint. 40*699cd480SApple OSS Distributions 41*699cd480SApple OSS Distributions@dyn_cached_property can be used on instances to turn a member into 42*699cd480SApple OSS Distributionsa per-target cached dynamic property that will be cleaned up when 43*699cd480SApple OSS Distributionsthe object dies or if the process is resumed and then stops 44*699cd480SApple OSS Distributionsor hits a breakpoint. 45*699cd480SApple OSS Distributions 46*699cd480SApple OSS DistributionsFunctions using these decorators, must have a `target=None` named argument 47*699cd480SApple OSS Distributionsthat no caller of those functions need to pass explicitly, the decorator 48*699cd480SApple OSS Distributionswill take care of passing the proper current target value (equivalent to 49*699cd480SApple OSS DistributionsLazyTarget.GetTarget() except more efficiently). 50*699cd480SApple OSS Distributions 51*699cd480SApple OSS Distributions 52*699cd480SApple OSS DistributionsCache Invalidation 53*699cd480SApple OSS Distributions~~~~~~~~~~~~~~~~~~ 54*699cd480SApple OSS Distributions 55*699cd480SApple OSS DistributionsThis module make the crucial assumption that no XNU lldb macro 56*699cd480SApple OSS Distributionswill step/resume/... processes as the invalidation only happens 57*699cd480SApple OSS Distributionswhen a new command is being run. 58*699cd480SApple OSS Distributions 59*699cd480SApple OSS DistributionsIf some macro needs to step/resume it has to provide its own 60*699cd480SApple OSS Distributionscache invalidation, by e.g. pushing its own ImplicitContext() 61*699cd480SApple OSS Distributionsaround such process state manipulations. 62*699cd480SApple OSS Distributions""" 63*699cd480SApple OSS Distributions 64*699cd480SApple OSS Distributions# Private Routines and objects 65*699cd480SApple OSS Distributionsfrom __future__ import absolute_import 66*699cd480SApple OSS Distributions 67*699cd480SApple OSS Distributionsfrom builtins import ( 68*699cd480SApple OSS Distributions dict, 69*699cd480SApple OSS Distributions hash, 70*699cd480SApple OSS Distributions list, 71*699cd480SApple OSS Distributions object, 72*699cd480SApple OSS Distributions tuple, 73*699cd480SApple OSS Distributions) 74*699cd480SApple OSS Distributionsfrom collections import namedtuple 75*699cd480SApple OSS Distributions 76*699cd480SApple OSS Distributionsimport functools 77*699cd480SApple OSS Distributionsimport gc 78*699cd480SApple OSS Distributionsimport inspect 79*699cd480SApple OSS Distributionsimport sys 80*699cd480SApple OSS Distributionsimport weakref 81*699cd480SApple OSS Distributions 82*699cd480SApple OSS Distributionsimport lldb 83*699cd480SApple OSS Distributions 84*699cd480SApple OSS Distributionsfrom .compat import valueint 85*699cd480SApple OSS Distributionsfrom .configuration import config 86*699cd480SApple OSS Distributionsfrom . import lldbwrap 87*699cd480SApple OSS Distributions 88*699cd480SApple OSS Distributions 89*699cd480SApple OSS Distributionsclass _Registry(list): 90*699cd480SApple OSS Distributions """ Private class that holds a list of _Cache instances """ 91*699cd480SApple OSS Distributions 92*699cd480SApple OSS Distributions __slots__ = ('name') 93*699cd480SApple OSS Distributions 94*699cd480SApple OSS Distributions def __init__(self, name): 95*699cd480SApple OSS Distributions super(_Registry, self).__init__() 96*699cd480SApple OSS Distributions self.name = name 97*699cd480SApple OSS Distributions 98*699cd480SApple OSS Distributions @property 99*699cd480SApple OSS Distributions def size(self): 100*699cd480SApple OSS Distributions return sys.getsizeof(self) 101*699cd480SApple OSS Distributions 102*699cd480SApple OSS Distributions def invalidate(self, pid): 103*699cd480SApple OSS Distributions for cache in self: 104*699cd480SApple OSS Distributions cache.invalidate(pid) 105*699cd480SApple OSS Distributions 106*699cd480SApple OSS Distributions def clear(self): 107*699cd480SApple OSS Distributions for cache in self: 108*699cd480SApple OSS Distributions cache.clear() 109*699cd480SApple OSS Distributions 110*699cd480SApple OSS Distributions def __repr__(self): 111*699cd480SApple OSS Distributions return "_Registry({}, {})".format( 112*699cd480SApple OSS Distributions self.name, super(_Registry, self).__repr__()) 113*699cd480SApple OSS Distributions 114*699cd480SApple OSS Distributions def __str__(self): 115*699cd480SApple OSS Distributions return "_Registry({}, {} caches, size {})".format( 116*699cd480SApple OSS Distributions self.name, len(self), self.size) 117*699cd480SApple OSS Distributions 118*699cd480SApple OSS Distributions 119*699cd480SApple OSS Distributionsclass _Cache(dict): 120*699cd480SApple OSS Distributions """ Private class that implements a given global function _Cache 121*699cd480SApple OSS Distributions 122*699cd480SApple OSS Distributions Those are created with the @cache_statically/@cache_dynamically 123*699cd480SApple OSS Distributions decorators 124*699cd480SApple OSS Distributions """ 125*699cd480SApple OSS Distributions 126*699cd480SApple OSS Distributions __slots__ = ('name') 127*699cd480SApple OSS Distributions 128*699cd480SApple OSS Distributions def __init__(self, name, registry): 129*699cd480SApple OSS Distributions super(_Cache, self).__init__() 130*699cd480SApple OSS Distributions self.name = name 131*699cd480SApple OSS Distributions registry.append(self) 132*699cd480SApple OSS Distributions 133*699cd480SApple OSS Distributions @property 134*699cd480SApple OSS Distributions def size(self): 135*699cd480SApple OSS Distributions return sys.getsizeof(self) 136*699cd480SApple OSS Distributions 137*699cd480SApple OSS Distributions def invalidate(self, pid): 138*699cd480SApple OSS Distributions if pid in self: del self[pid] 139*699cd480SApple OSS Distributions 140*699cd480SApple OSS Distributions def __repr__(self): 141*699cd480SApple OSS Distributions return "_Cache({}, {})".format( 142*699cd480SApple OSS Distributions self.name, super(_Cache, self).__repr__()) 143*699cd480SApple OSS Distributions 144*699cd480SApple OSS Distributions def __str__(self): 145*699cd480SApple OSS Distributions return "_Cache({}, {} entries, size {})".format( 146*699cd480SApple OSS Distributions self.name, len(self), self.size) 147*699cd480SApple OSS Distributions 148*699cd480SApple OSS Distributions 149*699cd480SApple OSS Distributions_static_registry = _Registry('static') 150*699cd480SApple OSS Distributions_dynamic_registry = _Registry('dynamic') 151*699cd480SApple OSS Distributions_dynamic_keys = {} 152*699cd480SApple OSS Distributions 153*699cd480SApple OSS Distributions_implicit_target = None 154*699cd480SApple OSS Distributions_implicit_process = None 155*699cd480SApple OSS Distributions_implicit_exe_id = None 156*699cd480SApple OSS Distributions_implicit_dynkey = None 157*699cd480SApple OSS Distributions 158*699cd480SApple OSS Distributions 159*699cd480SApple OSS Distributionsclass _DynamicKey(object): 160*699cd480SApple OSS Distributions """ Wraps a process StopID as a key that can be used as caches keys """ 161*699cd480SApple OSS Distributions 162*699cd480SApple OSS Distributions def __init__(self, exe_id, stop_id): 163*699cd480SApple OSS Distributions self.exe_id = exe_id 164*699cd480SApple OSS Distributions self.stop_id = stop_id 165*699cd480SApple OSS Distributions 166*699cd480SApple OSS Distributions def __hash__(self): 167*699cd480SApple OSS Distributions return id(self) 168*699cd480SApple OSS Distributions 169*699cd480SApple OSS Distributions 170*699cd480SApple OSS Distributionsclass LazyTarget(object): 171*699cd480SApple OSS Distributions """ A common object that lazy-evaluates and caches the lldb.SBTarget 172*699cd480SApple OSS Distributions and lldb.SBProcess for the current interactive debugging session. 173*699cd480SApple OSS Distributions """ 174*699cd480SApple OSS Distributions 175*699cd480SApple OSS Distributions @staticmethod 176*699cd480SApple OSS Distributions def _CacheUpdateAnchor(exe_id, process): 177*699cd480SApple OSS Distributions global _dynamic_keys, _dynamic_registry 178*699cd480SApple OSS Distributions 179*699cd480SApple OSS Distributions stop_id = process.GetStopID() 180*699cd480SApple OSS Distributions dyn_key = _dynamic_keys.get(exe_id) 181*699cd480SApple OSS Distributions 182*699cd480SApple OSS Distributions if dyn_key is None: 183*699cd480SApple OSS Distributions _dynamic_keys[exe_id] = dyn_key = _DynamicKey(exe_id, stop_id) 184*699cd480SApple OSS Distributions elif dyn_key.stop_id != stop_id: 185*699cd480SApple OSS Distributions _dynamic_registry.invalidate(exe_id) 186*699cd480SApple OSS Distributions _dynamic_keys[exe_id] = dyn_key = _DynamicKey(exe_id, stop_id) 187*699cd480SApple OSS Distributions gc.collect() 188*699cd480SApple OSS Distributions 189*699cd480SApple OSS Distributions return dyn_key 190*699cd480SApple OSS Distributions 191*699cd480SApple OSS Distributions @staticmethod 192*699cd480SApple OSS Distributions def _CacheGC(): 193*699cd480SApple OSS Distributions global _dynamic_keys, _dynamic_registry, _static_registry 194*699cd480SApple OSS Distributions 195*699cd480SApple OSS Distributions exe_ids = _dynamic_keys.keys() - set( 196*699cd480SApple OSS Distributions tg.GetProcess().GetUniqueID() 197*699cd480SApple OSS Distributions for tg in lldb.debugger 198*699cd480SApple OSS Distributions ) 199*699cd480SApple OSS Distributions 200*699cd480SApple OSS Distributions for exe_id in exe_ids: 201*699cd480SApple OSS Distributions _static_registry.invalidate(exe_id) 202*699cd480SApple OSS Distributions _dynamic_registry.invalidate(exe_id) 203*699cd480SApple OSS Distributions del _dynamic_keys[exe_id] 204*699cd480SApple OSS Distributions 205*699cd480SApple OSS Distributions if len(exe_ids): 206*699cd480SApple OSS Distributions gc.collect() 207*699cd480SApple OSS Distributions 208*699cd480SApple OSS Distributions @staticmethod 209*699cd480SApple OSS Distributions def _CacheGetDynamicKey(): 210*699cd480SApple OSS Distributions """ Get a _DynamicKey for the most likely current process 211*699cd480SApple OSS Distributions """ 212*699cd480SApple OSS Distributions global _implicit_dynkey 213*699cd480SApple OSS Distributions 214*699cd480SApple OSS Distributions dyn_key = _implicit_dynkey 215*699cd480SApple OSS Distributions if dyn_key is None: 216*699cd480SApple OSS Distributions process = lldbwrap.GetProcess() 217*699cd480SApple OSS Distributions exe_id = process.GetUniqueID() 218*699cd480SApple OSS Distributions dyn_key = LazyTarget._CacheUpdateAnchor(exe_id, process) 219*699cd480SApple OSS Distributions 220*699cd480SApple OSS Distributions return dyn_key 221*699cd480SApple OSS Distributions 222*699cd480SApple OSS Distributions @staticmethod 223*699cd480SApple OSS Distributions def _CacheClear(): 224*699cd480SApple OSS Distributions """ remove all cached data. 225*699cd480SApple OSS Distributions """ 226*699cd480SApple OSS Distributions global _dynamic_registry, _static_registry, _dynamic_keys 227*699cd480SApple OSS Distributions 228*699cd480SApple OSS Distributions _dynamic_registry.clear() 229*699cd480SApple OSS Distributions _static_registry.clear() 230*699cd480SApple OSS Distributions _dynamic_keys.clear() 231*699cd480SApple OSS Distributions 232*699cd480SApple OSS Distributions @staticmethod 233*699cd480SApple OSS Distributions def _CacheSize(): 234*699cd480SApple OSS Distributions """ Returns number of bytes held in cache. 235*699cd480SApple OSS Distributions returns: 236*699cd480SApple OSS Distributions int - size of cache including static and dynamic 237*699cd480SApple OSS Distributions """ 238*699cd480SApple OSS Distributions global _dynamic_registry, _static_registry 239*699cd480SApple OSS Distributions 240*699cd480SApple OSS Distributions return _dynamic_registry.size + _static_registry.size 241*699cd480SApple OSS Distributions 242*699cd480SApple OSS Distributions 243*699cd480SApple OSS Distributions @staticmethod 244*699cd480SApple OSS Distributions def GetTarget(): 245*699cd480SApple OSS Distributions """ Get the SBTarget that is the most likely current target 246*699cd480SApple OSS Distributions """ 247*699cd480SApple OSS Distributions global _implicit_target 248*699cd480SApple OSS Distributions 249*699cd480SApple OSS Distributions return _implicit_target or lldbwrap.GetTarget() 250*699cd480SApple OSS Distributions 251*699cd480SApple OSS Distributions @staticmethod 252*699cd480SApple OSS Distributions def GetProcess(): 253*699cd480SApple OSS Distributions """ Get an SBProcess for the most likely current process 254*699cd480SApple OSS Distributions """ 255*699cd480SApple OSS Distributions global _implicit_process 256*699cd480SApple OSS Distributions 257*699cd480SApple OSS Distributions return _implicit_process or lldbwrap.GetProcess() 258*699cd480SApple OSS Distributions 259*699cd480SApple OSS Distributions 260*699cd480SApple OSS Distributionsclass ImplicitContext(object): 261*699cd480SApple OSS Distributions """ This class sets up the implicit target/process 262*699cd480SApple OSS Distributions being used by the XNu lldb macros system. 263*699cd480SApple OSS Distributions 264*699cd480SApple OSS Distributions In order for lldb macros to function properly, such a context 265*699cd480SApple OSS Distributions must be used around code being run, otherwise macros will try 266*699cd480SApple OSS Distributions to infer it from the current lldb selected target which is 267*699cd480SApple OSS Distributions incorrect in certain contexts. 268*699cd480SApple OSS Distributions 269*699cd480SApple OSS Distributions typical usage is: 270*699cd480SApple OSS Distributions 271*699cd480SApple OSS Distributions with ImplicitContext(thing): 272*699cd480SApple OSS Distributions # code 273*699cd480SApple OSS Distributions 274*699cd480SApple OSS Distributions where @c thing is any of an SBExecutionContext, an SBValue, 275*699cd480SApple OSS Distributions an SBBreakpoint, an SBProcess, or an SBTarget. 276*699cd480SApple OSS Distributions """ 277*699cd480SApple OSS Distributions 278*699cd480SApple OSS Distributions __slots__ = ('target', 'process', 'exe_id', 'old_ctx') 279*699cd480SApple OSS Distributions 280*699cd480SApple OSS Distributions def __init__(self, arg): 281*699cd480SApple OSS Distributions if isinstance(arg, lldb.SBExecutionContext): 282*699cd480SApple OSS Distributions exe_ctx = lldbwrap.SBExecutionContext(arg) 283*699cd480SApple OSS Distributions target = exe_ctx.GetTarget() 284*699cd480SApple OSS Distributions process = exe_ctx.GetProcess() 285*699cd480SApple OSS Distributions elif isinstance(arg, lldb.SBValue): 286*699cd480SApple OSS Distributions target = lldbwrap.SBTarget(arg.GetTarget()) 287*699cd480SApple OSS Distributions process = target.GetProcess() 288*699cd480SApple OSS Distributions elif isinstance(arg, lldb.SBBreakpoint): 289*699cd480SApple OSS Distributions bpoint = lldbwrap.SBBreakpoint(arg) 290*699cd480SApple OSS Distributions target = bpoint.GetTarget() 291*699cd480SApple OSS Distributions process = target.GetProcess() 292*699cd480SApple OSS Distributions elif isinstance(arg, lldb.SBProcess): 293*699cd480SApple OSS Distributions process = lldbwrap.SBProcess(arg) 294*699cd480SApple OSS Distributions target = process.GetTarget() 295*699cd480SApple OSS Distributions elif isinstance(arg, lldb.SBTarget): 296*699cd480SApple OSS Distributions target = lldbwrap.SBTarget(arg) 297*699cd480SApple OSS Distributions process = target.GetProcess() 298*699cd480SApple OSS Distributions else: 299*699cd480SApple OSS Distributions raise TypeError("argument type unsupported {}".format( 300*699cd480SApple OSS Distributions arg.__class__.__name__)) 301*699cd480SApple OSS Distributions 302*699cd480SApple OSS Distributions self.target = target 303*699cd480SApple OSS Distributions self.process = process 304*699cd480SApple OSS Distributions self.exe_id = process.GetUniqueID() 305*699cd480SApple OSS Distributions self.old_ctx = None 306*699cd480SApple OSS Distributions 307*699cd480SApple OSS Distributions def __enter__(self): 308*699cd480SApple OSS Distributions global _implicit_target, _implicit_process, _implicit_exe_id 309*699cd480SApple OSS Distributions global _implicit_dynkey, _dynamic_keys 310*699cd480SApple OSS Distributions 311*699cd480SApple OSS Distributions self.old_ctx = (_implicit_target, _implicit_process, _implicit_exe_id) 312*699cd480SApple OSS Distributions 313*699cd480SApple OSS Distributions _implicit_target = self.target 314*699cd480SApple OSS Distributions _implicit_process = process = self.process 315*699cd480SApple OSS Distributions _implicit_exe_id = exe_id = self.exe_id 316*699cd480SApple OSS Distributions _implicit_dynkey = LazyTarget._CacheUpdateAnchor(exe_id, process) 317*699cd480SApple OSS Distributions 318*699cd480SApple OSS Distributions if len(_dynamic_keys) > 1: 319*699cd480SApple OSS Distributions LazyTarget._CacheGC() 320*699cd480SApple OSS Distributions 321*699cd480SApple OSS Distributions def __exit__(self, *args): 322*699cd480SApple OSS Distributions global _implicit_target, _implicit_process, _implicit_exe_id 323*699cd480SApple OSS Distributions global _implicit_dynkey, _dynamic_keys 324*699cd480SApple OSS Distributions 325*699cd480SApple OSS Distributions target, process, exe_id = self.old_ctx 326*699cd480SApple OSS Distributions self.old_ctx = None 327*699cd480SApple OSS Distributions 328*699cd480SApple OSS Distributions _implicit_target = target 329*699cd480SApple OSS Distributions _implicit_process = process 330*699cd480SApple OSS Distributions _implicit_exe_id = exe_id 331*699cd480SApple OSS Distributions 332*699cd480SApple OSS Distributions if process: 333*699cd480SApple OSS Distributions _implicit_dynkey = LazyTarget._CacheUpdateAnchor(exe_id, process) 334*699cd480SApple OSS Distributions else: 335*699cd480SApple OSS Distributions _implicit_dynkey = None 336*699cd480SApple OSS Distributions 337*699cd480SApple OSS Distributions 338*699cd480SApple OSS Distributionsclass _HashedSeq(list): 339*699cd480SApple OSS Distributions """ This class guarantees that hash() will be called no more than once 340*699cd480SApple OSS Distributions per element. This is important because the lru_cache() will hash 341*699cd480SApple OSS Distributions the key multiple times on a cache miss. 342*699cd480SApple OSS Distributions 343*699cd480SApple OSS Distributions Inspired by python3's lru_cache decorator implementation 344*699cd480SApple OSS Distributions """ 345*699cd480SApple OSS Distributions 346*699cd480SApple OSS Distributions __slots__ = 'hashvalue' 347*699cd480SApple OSS Distributions 348*699cd480SApple OSS Distributions def __init__(self, tup, hash=hash): 349*699cd480SApple OSS Distributions self[:] = tup 350*699cd480SApple OSS Distributions self.hashvalue = hash(tup) 351*699cd480SApple OSS Distributions 352*699cd480SApple OSS Distributions def __hash__(self): 353*699cd480SApple OSS Distributions return self.hashvalue 354*699cd480SApple OSS Distributions 355*699cd480SApple OSS Distributions @classmethod 356*699cd480SApple OSS Distributions def make_key(cls, args, kwds, kwd_mark = (object(),), 357*699cd480SApple OSS Distributions fasttypes = {valueint, int, str}, tuple=tuple, type=type, len=len): 358*699cd480SApple OSS Distributions 359*699cd480SApple OSS Distributions """ Inspired from python3's cache implementation """ 360*699cd480SApple OSS Distributions 361*699cd480SApple OSS Distributions key = args 362*699cd480SApple OSS Distributions if kwds: 363*699cd480SApple OSS Distributions key += kwd_mark 364*699cd480SApple OSS Distributions key += tuple(kwd.items()) 365*699cd480SApple OSS Distributions elif len(key) == 0: 366*699cd480SApple OSS Distributions return None 367*699cd480SApple OSS Distributions elif len(key) == 1 and type(key[0]) in fasttypes: 368*699cd480SApple OSS Distributions return key[0] 369*699cd480SApple OSS Distributions return cls(key) 370*699cd480SApple OSS Distributions 371*699cd480SApple OSS Distributions 372*699cd480SApple OSS Distributionsdef _cache_with_registry(fn, registry, maxsize=128, sentinel=object()): 373*699cd480SApple OSS Distributions """ Internal function """ 374*699cd480SApple OSS Distributions 375*699cd480SApple OSS Distributions nokey = False 376*699cd480SApple OSS Distributions 377*699cd480SApple OSS Distributions if hasattr(inspect, 'signature'): # PY3 378*699cd480SApple OSS Distributions sig = inspect.signature(fn) 379*699cd480SApple OSS Distributions tg = sig.parameters.get('target') 380*699cd480SApple OSS Distributions if not tg or tg.default is not None: 381*699cd480SApple OSS Distributions raise ValueError("function doesn't have a 'target=None' argument") 382*699cd480SApple OSS Distributions 383*699cd480SApple OSS Distributions nokey = len(sig.parameters) == 1 384*699cd480SApple OSS Distributions cache = _Cache(fn.__qualname__, registry) 385*699cd480SApple OSS Distributions else: 386*699cd480SApple OSS Distributions spec = inspect.getargspec(fn) 387*699cd480SApple OSS Distributions try: 388*699cd480SApple OSS Distributions index = spec.args.index('target') 389*699cd480SApple OSS Distributions offs = len(spec.args) - len(spec.defaults) 390*699cd480SApple OSS Distributions if index < offs or spec.defaults[index - offs] is not None: 391*699cd480SApple OSS Distributions raise ValueError 392*699cd480SApple OSS Distributions except: 393*699cd480SApple OSS Distributions raise ValueError("function doesn't have a 'target=None' argument") 394*699cd480SApple OSS Distributions 395*699cd480SApple OSS Distributions nokey = len(spec.args) == 1 and spec.varargs is None and spec.keywords is None 396*699cd480SApple OSS Distributions cache = _Cache(fn.__name__, registry) 397*699cd480SApple OSS Distributions 398*699cd480SApple OSS Distributions c_setdef = cache.setdefault 399*699cd480SApple OSS Distributions c_get = cache.get 400*699cd480SApple OSS Distributions make_key = _HashedSeq.make_key 401*699cd480SApple OSS Distributions getdynk = LazyTarget._CacheGetDynamicKey 402*699cd480SApple OSS Distributions gettg = LazyTarget.GetTarget 403*699cd480SApple OSS Distributions 404*699cd480SApple OSS Distributions if nokey: 405*699cd480SApple OSS Distributions def caching_wrapper(*args, **kwds): 406*699cd480SApple OSS Distributions global _implicit_exe_id, _implicit_target 407*699cd480SApple OSS Distributions 408*699cd480SApple OSS Distributions key = _implicit_exe_id or getdynk().exe_id 409*699cd480SApple OSS Distributions result = c_get(key, sentinel) 410*699cd480SApple OSS Distributions if result is not sentinel: 411*699cd480SApple OSS Distributions return result 412*699cd480SApple OSS Distributions 413*699cd480SApple OSS Distributions kwds['target'] = _implicit_target or gettg() 414*699cd480SApple OSS Distributions return c_setdef(key, fn(*args, **kwds)) 415*699cd480SApple OSS Distributions 416*699cd480SApple OSS Distributions def cached(*args, **kwds): 417*699cd480SApple OSS Distributions global _implicit_exe_id 418*699cd480SApple OSS Distributions 419*699cd480SApple OSS Distributions return c_get(_implicit_exe_id or getdynk().exe_id, sentinel) != sentinel 420*699cd480SApple OSS Distributions else: 421*699cd480SApple OSS Distributions def caching_wrapper(*args, **kwds): 422*699cd480SApple OSS Distributions global _implicit_exe_id, _implicit_target 423*699cd480SApple OSS Distributions 424*699cd480SApple OSS Distributions tg_d = c_setdef(_implicit_exe_id or getdynk().exe_id, {}) 425*699cd480SApple OSS Distributions c_key = make_key(args, kwds) 426*699cd480SApple OSS Distributions result = tg_d.get(c_key, sentinel) 427*699cd480SApple OSS Distributions if result is not sentinel: 428*699cd480SApple OSS Distributions return result 429*699cd480SApple OSS Distributions 430*699cd480SApple OSS Distributions # 431*699cd480SApple OSS Distributions # Blunt policy to avoid exploding memory, 432*699cd480SApple OSS Distributions # that is simpler than an actual LRU. 433*699cd480SApple OSS Distributions # 434*699cd480SApple OSS Distributions # TODO: be smarter? 435*699cd480SApple OSS Distributions # 436*699cd480SApple OSS Distributions if len(tg_d) >= maxsize: tg_d.clear() 437*699cd480SApple OSS Distributions 438*699cd480SApple OSS Distributions kwds['target'] = _implicit_target or gettg() 439*699cd480SApple OSS Distributions return tg_d.setdefault(c_key, fn(*args, **kwds)) 440*699cd480SApple OSS Distributions 441*699cd480SApple OSS Distributions def cached(*args, **kwds): 442*699cd480SApple OSS Distributions global _implicit_exe_id 443*699cd480SApple OSS Distributions 444*699cd480SApple OSS Distributions tg_d = c_get(_implicit_exe_id or getdynk().exe_id) 445*699cd480SApple OSS Distributions return tg_d and tg_d.get(make_key(args, kwds), sentinel) != sentinel 446*699cd480SApple OSS Distributions 447*699cd480SApple OSS Distributions caching_wrapper.cached = cached 448*699cd480SApple OSS Distributions return functools.update_wrapper(caching_wrapper, fn) 449*699cd480SApple OSS Distributions 450*699cd480SApple OSS Distributions 451*699cd480SApple OSS Distributionsdef cache_statically(fn): 452*699cd480SApple OSS Distributions """ Decorator to cache the results statically 453*699cd480SApple OSS Distributions 454*699cd480SApple OSS Distributions This basically makes the decorated function cache its result based 455*699cd480SApple OSS Distributions on its arguments with an automatic static per target cache 456*699cd480SApple OSS Distributions 457*699cd480SApple OSS Distributions The function must have a named parameter called 'target' defaulting 458*699cd480SApple OSS Distributions to None, with no clients ever passing it explicitly. It will be 459*699cd480SApple OSS Distributions passed the proper SBTarget when called. 460*699cd480SApple OSS Distributions 461*699cd480SApple OSS Distributions @cache_statically(user_function) 462*699cd480SApple OSS Distributions Cache the results of this function automatically per target, 463*699cd480SApple OSS Distributions using the arguments of the function as the cache key. 464*699cd480SApple OSS Distributions """ 465*699cd480SApple OSS Distributions 466*699cd480SApple OSS Distributions return _cache_with_registry(fn, _static_registry) 467*699cd480SApple OSS Distributions 468*699cd480SApple OSS Distributions 469*699cd480SApple OSS Distributionsdef cache_dynamically(fn): 470*699cd480SApple OSS Distributions """ Decorator to cache the results dynamically 471*699cd480SApple OSS Distributions 472*699cd480SApple OSS Distributions This basically makes the decorated function cache its result based 473*699cd480SApple OSS Distributions on its arguments with an automatic dynamic cache that is reset 474*699cd480SApple OSS Distributions every time the process state changes 475*699cd480SApple OSS Distributions 476*699cd480SApple OSS Distributions The function must have a named parameter called 'target' defaulting 477*699cd480SApple OSS Distributions to None, with no clients ever passing it explicitly. It will be 478*699cd480SApple OSS Distributions passed the proper SBTarget when called. 479*699cd480SApple OSS Distributions 480*699cd480SApple OSS Distributions @cache_dynamically(user_function) 481*699cd480SApple OSS Distributions Cache the results of this function automatically per target, 482*699cd480SApple OSS Distributions using the arguments of the function as the cache key. 483*699cd480SApple OSS Distributions """ 484*699cd480SApple OSS Distributions 485*699cd480SApple OSS Distributions return _cache_with_registry(fn, _dynamic_registry) 486*699cd480SApple OSS Distributions 487*699cd480SApple OSS Distributions 488*699cd480SApple OSS Distributionsdef dyn_cached_property(fn, sentinel=object()): 489*699cd480SApple OSS Distributions """ Decorator to make a class or method property cached per instance 490*699cd480SApple OSS Distributions 491*699cd480SApple OSS Distributions The method must have the prototype: 492*699cd480SApple OSS Distributions 493*699cd480SApple OSS Distributions def foo(self, target=None) 494*699cd480SApple OSS Distributions 495*699cd480SApple OSS Distributions and will generate the property "foo". 496*699cd480SApple OSS Distributions """ 497*699cd480SApple OSS Distributions 498*699cd480SApple OSS Distributions if hasattr(inspect, 'signature'): # PY3 499*699cd480SApple OSS Distributions if list(inspect.signature(fn).parameters) != ['self', 'target']: 500*699cd480SApple OSS Distributions raise ValueError("function signature must be (self, target=None)") 501*699cd480SApple OSS Distributions else: 502*699cd480SApple OSS Distributions spec = inspect.getargspec(fn) 503*699cd480SApple OSS Distributions if spec.args != ['self', 'target'] or \ 504*699cd480SApple OSS Distributions spec.varargs is not None or spec.keywords is not None: 505*699cd480SApple OSS Distributions raise ValueError("function signature must be (self, target=None)") 506*699cd480SApple OSS Distributions 507*699cd480SApple OSS Distributions getdynk = LazyTarget._CacheGetDynamicKey 508*699cd480SApple OSS Distributions gettg = LazyTarget.GetTarget 509*699cd480SApple OSS Distributions c_attr = "_dyn_key__" + fn.__name__ 510*699cd480SApple OSS Distributions 511*699cd480SApple OSS Distributions def dyn_cached_property_wrapper(self, target=None): 512*699cd480SApple OSS Distributions global _implicit_dynkey, _implicit_target 513*699cd480SApple OSS Distributions 514*699cd480SApple OSS Distributions cache = getattr(self, c_attr, None) 515*699cd480SApple OSS Distributions if cache is None: 516*699cd480SApple OSS Distributions cache = weakref.WeakKeyDictionary() 517*699cd480SApple OSS Distributions setattr(self, c_attr, cache) 518*699cd480SApple OSS Distributions 519*699cd480SApple OSS Distributions c_key = _implicit_dynkey or getdynk() 520*699cd480SApple OSS Distributions result = cache.get(c_key, sentinel) 521*699cd480SApple OSS Distributions if result is not sentinel: 522*699cd480SApple OSS Distributions return result 523*699cd480SApple OSS Distributions 524*699cd480SApple OSS Distributions return cache.setdefault(c_key, fn(self, _implicit_target or gettg())) 525*699cd480SApple OSS Distributions 526*699cd480SApple OSS Distributions return property(functools.update_wrapper(dyn_cached_property_wrapper, fn)) 527*699cd480SApple OSS Distributions 528*699cd480SApple OSS Distributions 529*699cd480SApple OSS DistributionsClearAllCache = LazyTarget._CacheClear 530*699cd480SApple OSS Distributions 531*699cd480SApple OSS DistributionsGetSizeOfCache = LazyTarget._CacheSize 532*699cd480SApple OSS Distributions 533*699cd480SApple OSS Distributions__all__ = [ 534*699cd480SApple OSS Distributions LazyTarget.__name__, 535*699cd480SApple OSS Distributions ImplicitContext.__name__, 536*699cd480SApple OSS Distributions 537*699cd480SApple OSS Distributions cache_statically.__name__, 538*699cd480SApple OSS Distributions cache_dynamically.__name__, 539*699cd480SApple OSS Distributions dyn_cached_property.__name__, 540*699cd480SApple OSS Distributions 541*699cd480SApple OSS Distributions ClearAllCache.__name__, 542*699cd480SApple OSS Distributions GetSizeOfCache.__name__, 543*699cd480SApple OSS Distributions] 544