pythonobjects.py (16999B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\devtools\script\pythonobjects.py 2 from __future__ import print_function 3 import gc 4 import weakref 5 import sys 6 import pprint 7 import blue 8 import log 9 import stackless 10 from carbon.common.script.sys.serviceConst import ROLE_QA 11 from carbon.common.script.util.format import FmtDate 12 from carbon.common.script.util.htmlwriter import Swing 13 from carbonui import uiconst 14 from carbonui.control.checkbox import Checkbox 15 from carbonui.control.scrollentries import ScrollEntryNode, SE_GenericCore 16 from carbonui.control.singlelineedits.singleLineEditText import SingleLineEditText 17 from carbonui.primitives.base import Base 18 from carbonui.primitives.container import Container 19 from carbonui.primitives.containerAutoSize import ContainerAutoSize 20 from carbonui.control.button import Button 21 from eve.client.script.ui.control.eveLabel import EveLabelMedium 22 from eve.client.script.ui.control.eveScroll import Scroll 23 from carbonui.control.window import Window 24 from pympler import asizeof 25 26 def srepr(obj): 27 try: 28 return pprint.saferepr(obj) 29 except: 30 try: 31 t = 'for type ' + type(obj).__name__ 32 except: 33 t = "couldn't even get the typename!" 34 35 return '[srepr failed %s]' % t 36 37 38 def FindObjectByID(obID): 39 obID = int(obID) 40 objs = gc.get_objects() 41 blue_objs = blue.classes.GetWrapperList() 42 ob = None 43 try: 44 for ob in objs: 45 if id(ob) == obID: 46 return (ob,) 47 48 for ob in blue_objs: 49 if id(ob) == obID: 50 return (ob,) 51 52 return () 53 finally: 54 del objs 55 del ob 56 del blue_objs 57 58 59 def GetClassName(object): 60 typeOrClass = type(object) 61 if not isinstance(object, weakref.ProxyType): 62 try: 63 typeOrClass = object.__class__ 64 except (AttributeError, ReferenceError): 65 sys.exc_clear() 66 67 objectName = '%s.%s' % (typeOrClass.__module__, typeOrClass.__name__) 68 return objectName 69 70 71 class PythonObjectsMonitor(Window): 72 default_caption = 'Python objects' 73 default_windowID = 'pythonobjectsmonitor' 74 default_minSize = (500, 400) 75 76 def ApplyAttributes(self, attributes): 77 self._ready = False 78 Window.ApplyAttributes(self, attributes) 79 self.settingsContainer = Container(parent=self.sr.main, align=uiconst.TOTOP, top=8, height=16, padding=8) 80 self.filterEdit = SingleLineEditText(parent=self.settingsContainer, align=uiconst.TOLEFT, width=150, label='Filter:', OnReturn=self.PopulateScroll) 81 Button(parent=self.settingsContainer, label='Reset', align=uiconst.TORIGHT, func=self.Reset, padRight=8) 82 Button(parent=self.settingsContainer, label='Refresh', align=uiconst.TORIGHT, func=self.PopulateScroll, padRight=8) 83 self.onlyUiCheckbox = Checkbox(parent=self.settingsContainer, text='Only UI ', align=uiconst.TORIGHT, callback=self.PopulateScroll, padRight=20) 84 self.scroll = Scroll(parent=self.sr.main, id='pythonobjectscroll', align=uiconst.TOALL) 85 self.Reset() 86 self._ready = True 87 88 def Reset(self, *args): 89 wnd = PythonObjectClassDetails.GetIfOpen() 90 if wnd: 91 wnd.Close() 92 self.peakCount = {} 93 self.baseCount = {} 94 self.filterEdit.SetText('') 95 self.PopulateScroll() 96 97 def GetLabelForEntry(self, key, value): 98 peak = self.peakCount.get(key, 0) 99 delta = value - self.baseCount.get(key, 0) 100 if delta < 0: 101 color = '0xffff0000' 102 else: 103 color = '0xff00ff00' 104 label = '%s<t><color=%s>%d</color><t>%d<t>%d' % (key, 105 color, 106 delta, 107 value, 108 peak) 109 return (label, delta) 110 111 def RebuildScrollContents(self, objects, filter, onlyShowUI): 112 contentList = [] 113 newObjects = {} 114 for k, v in objects.iteritems(): 115 if filter and filter not in k.lower(): 116 continue 117 newObjects[k] = v 118 119 if onlyShowUI: 120 isUiByClassName = self.GetIsUiDict(newObjects) 121 else: 122 isUiByClassName = {} 123 for key, value in newObjects.iteritems(): 124 if onlyShowUI: 125 if not isUiByClassName[key]: 126 continue 127 label, delta = self.GetLabelForEntry(key, value) 128 listEntry = ScrollEntryNode(decoClass=SE_GenericCore, id=id, name=key, label=label, OnDblClick=self.ClassEntryDetails, OnGetMenu=self.EntryMenu) 129 listEntry.Set('sort_Delta', delta) 130 contentList.append(listEntry) 131 132 self.scroll.Load(contentList=contentList, headers=['Name', 133 'Delta', 134 'Count', 135 'Peak']) 136 137 def GetIsUiDict(self, objects): 138 activeObjects = gc.get_objects() 139 myDict = {} 140 for activeObject in activeObjects: 141 objectName = GetClassName(activeObject) 142 if objectName not in objects or objectName in myDict: 143 continue 144 myDict[objectName] = isinstance(activeObject, Base) 145 146 return myDict 147 148 def EntryMenu(self, entry): 149 if not session.role and ROLE_QA: 150 return [] 151 m = [('Open Details', self.ClassEntryDetails, (entry,))] 152 return m 153 154 def ClassEntryDetails(self, entry, *args): 155 if not session.role and ROLE_QA: 156 return 157 wnd = PythonObjectClassDetails.GetIfOpen() 158 if wnd: 159 wnd.SwitchItemClass(entry.name) 160 wnd.Maximize() 161 else: 162 PythonObjectClassDetails.Open(itemClass=entry.name) 163 164 def PopulateScroll(self, *args): 165 objectsByType = {} 166 for object in gc.get_objects(): 167 tp = type(object) 168 if not isinstance(object, weakref.ProxyType): 169 try: 170 tp = object.__class__ 171 except (AttributeError, ReferenceError): 172 sys.exc_clear() 173 174 objectsByType[tp] = objectsByType.get(tp, 0) + 1 175 176 objectsByModuleAndType = {} 177 for k, v in objectsByType.iteritems(): 178 name = k.__module__ + '.' + k.__name__ 179 objectsByModuleAndType[name] = objectsByModuleAndType.get(name, 0) + v 180 181 for key, value in objectsByModuleAndType.iteritems(): 182 peak = self.peakCount.get(key, 0) 183 if value > peak: 184 self.peakCount[key] = value 185 if key not in self.baseCount: 186 self.baseCount[key] = value 187 188 filter = self.filterEdit.text.lower() 189 onlyShowUi = self.onlyUiCheckbox.GetValue() 190 self.RebuildScrollContents(objectsByModuleAndType, filter, onlyShowUi) 191 192 193 class PythonObjectClassDetails(Window): 194 default_caption = 'Python Object Class Details' 195 default_windowID = 'pythonobjectclassdetails' 196 default_minSize = (500, 400) 197 198 def ApplyAttributes(self, attributes): 199 self._ready = False 200 Window.ApplyAttributes(self, attributes) 201 self.maxLen = 250 202 self.collectTimestamp = None 203 self.itemClass = attributes.itemClass 204 self.classDetailsContainer = ContainerAutoSize(parent=self.GetMainArea(), align=uiconst.TOTOP, padding=8) 205 Button(parent=self.sr.main, label='GC and Refresh', align=uiconst.TOPRIGHT, func=self.GarbageCollectAndRefresh, padRight=8) 206 self.scroll = Scroll(parent=self.sr.main, id='pythonobjectclassdetailscroll', align=uiconst.TOALL) 207 self.ShowObjectDetail() 208 self._ready = True 209 210 def Close(self, setClosed = False, *args, **kwds): 211 wnd = PythonObjectDetails.GetIfOpen() 212 if wnd: 213 wnd.Close() 214 self.scroll = None 215 self.classDetailsContainer = None 216 Window.Close(self, setClosed, *args, **kwds) 217 218 def GarbageCollectAndRefresh(self, *args): 219 import gc 220 self.scroll.Clear() 221 self.classDetailsContainer.Flush() 222 print('cleaned up: ', gc.collect()) 223 self.Refresh() 224 225 def Refresh(self): 226 self.scroll.Clear() 227 self.classDetailsContainer.Flush() 228 self.ShowObjectDetail() 229 230 def SwitchItemClass(self, itemClass): 231 if not self._ready: 232 return 233 wnd = PythonObjectDetails.GetIfOpen() 234 if wnd: 235 wnd.Close() 236 self.itemClass = itemClass 237 self._ready = False 238 self.Refresh() 239 self._ready = True 240 241 def PopulateScroll(self, objectData, destroyedCountLabel): 242 contentList = [] 243 destroyedCount = 0 244 for i, obj in enumerate(objectData): 245 objectName = self.GetObjectName(obj) 246 parentName = getattr(getattr(obj, 'parent', None), 'name', '-') 247 debugDisplayName = getattr(obj, 'debugDisplayName', None) 248 if not debugDisplayName: 249 debugDisplayName = getattr(obj, 'name', ' - ') 250 isDestroyed = getattr(obj, 'destroyed', 'N/A') 251 if isDestroyed is True: 252 destroyedCount += 1 253 label = '%s<t>%s<t>%s<t>%s' % (objectName, 254 debugDisplayName, 255 isDestroyed, 256 parentName) 257 listEntry = ScrollEntryNode(decoClass=SE_GenericCore, id=objectName, label=label, OnDblClick=self.EntryDetails, OnGetMenu=self.EntryMenu, refid=id(obj)) 258 contentList.append(listEntry) 259 if i >= self.maxLen: 260 break 261 262 self.scroll.Load(contentList=contentList, headers=['ObjectName', 263 'debugDisplayName', 264 'isDestroyed', 265 'parentName(?)']) 266 destroyedCountLabel.text = 'Destroyed count: %s' % destroyedCount 267 268 def ShowObjectDetail(self): 269 objectDetails = self.GetObjectDetails() 270 classLabel = EveLabelMedium(name='classLabel', parent=self.classDetailsContainer, text='module.class: %s' % self.itemClass, align=uiconst.TOTOP) 271 timeStamp = EveLabelMedium(name='timestamp', parent=self.classDetailsContainer, text='Collected at %s' % FmtDate(self.collectTimestamp), align=uiconst.TOTOP) 272 oCount = len(objectDetails) 273 countText = '%s Objects' % oCount 274 if oCount > self.maxLen: 275 countText += ', only first %s displayed' % self.maxLen 276 objectCount = EveLabelMedium(name='oCount', parent=self.classDetailsContainer, text=countText, align=uiconst.TOTOP) 277 destroyedCount = EveLabelMedium(name='destroyedCount', parent=self.classDetailsContainer, text='-', align=uiconst.TOTOP) 278 self.PopulateScroll(objectDetails, destroyedCount) 279 280 def EntryMenu(self, entry): 281 m = [('Open Details', self.EntryDetails, (entry,))] 282 return m 283 284 def EntryDetails(self, entry): 285 wnd = PythonObjectDetails.GetIfOpen() 286 if wnd: 287 wnd.Switch(entry.sr.node.refid) 288 wnd.Maximize() 289 else: 290 PythonObjectDetails.Open(refid=entry.sr.node.refid) 291 292 def GetObjectDetails(self): 293 self.collectTimestamp = startTime = blue.os.GetWallclockTimeNow() 294 activeObjects = gc.get_objects() 295 blueObjects = blue.classes.GetWrapperList() 296 objectDetails = [] 297 for activeObject in activeObjects: 298 objectName = GetClassName(activeObject) 299 if objectName == self.itemClass: 300 objectDetails.append(activeObject) 301 302 for activeBlueObject in blueObjects: 303 if activeBlueObject.__bluetype__ == self.itemClass: 304 objectDetails.append(activeBlueObject) 305 306 endTime = blue.os.GetWallclockTimeNow() 307 log.LogInfo('Finished fetching object details in %s Ms' % blue.os.TimeDiffInMs(startTime, endTime)) 308 return objectDetails 309 310 def GetObjectName(self, object): 311 try: 312 className = object.__class__ 313 except AttributeError: 314 className = object.__bluetype__ 315 316 objectName = '%s at 0x%.8x' % (className, id(object)) 317 objectName = '%s - Ref Count = %s - Size: %s' % (objectName, sys.getrefcount(object) - 4, asizeof.asizeof(object, limit=3)) 318 objectName = Swing(objectName) 319 return objectName 320 321 322 class PythonObjectDetails(Window): 323 default_caption = 'Python Object Details' 324 default_windowID = 'pythonobjectdetails' 325 default_minSize = (400, 400) 326 327 def ApplyAttributes(self, attributes): 328 self._ready = False 329 Window.ApplyAttributes(self, attributes) 330 self.subWindow = None 331 self.errorMessage = None 332 self.objectRefID = attributes.refid 333 self.object = self.GetObject() 334 self.classDetailsContainer = ContainerAutoSize(parent=self.GetMainArea(), align=uiconst.TOTOP, padding=8) 335 self.main = Container(name='main', parent=self.sr.main, align=uiconst.TOALL) 336 self.ShowObjectDetail() 337 self._ready = True 338 339 def Close(self, setClosed = False, *args, **kwds): 340 self.object = None 341 wnd = PythonObjectDetails.GetIfOpen(windowID='pythonobjectdetails_sub_%s' % id(self.object)) 342 if wnd: 343 wnd.Close() 344 Window.Close(self, setClosed, *args, **kwds) 345 346 def Refresh(self): 347 self.scroll.Clear() 348 self.classDetailsContainer.Flush() 349 self.main.Flush() 350 self.ShowObjectDetail() 351 352 def Switch(self, refid): 353 if not self._ready: 354 return 355 windowID = 'pythonobjectdetails_sub_%s' % id(self.object) 356 wnd = PythonObjectDetails.GetIfOpen(windowID=windowID) 357 if wnd: 358 wnd.Close() 359 self.errorMessage = None 360 self.objectRefID = refid 361 self.object = self.GetObject() 362 self.Refresh() 363 self._ready = True 364 365 def ShowObjectDetail(self): 366 self.listCont = Container(name='listCont', parent=self.main, align=uiconst.TOBOTTOM, height=140, padding=8) 367 self.objRepr = Container(name='objRepr', parent=self.main, align=uiconst.TOALL, height=20, padding=8) 368 className = GetClassName(self.object) 369 objectName = self.GetObjectName(self.object) 370 objectRepr = self.GetObjectRepr(self.object) 371 objectSizes = self.GetObjectSizes(self.object) 372 EveLabelMedium(parent=self.classDetailsContainer, text='Class name: %s' % className, align=uiconst.TOTOP, height=15) 373 EveLabelMedium(parent=self.classDetailsContainer, text='Object name: %s' % objectName, align=uiconst.TOTOP, height=15) 374 EveLabelMedium(parent=self.classDetailsContainer, text='Sizes: %s' % objectSizes, align=uiconst.TOTOP, height=20) 375 EveLabelMedium(parent=self.objRepr, text='Repr: %s' % objectRepr, align=uiconst.TOALL) 376 EveLabelMedium(parent=self.listCont, text='Incoming Refs:', align=uiconst.TOTOP, height=16) 377 self.scroll = Scroll(parent=self.listCont, id='pythonobjectrefereesscroll', align=uiconst.TOALL) 378 self.PopulateScroll() 379 380 def GetObject(self): 381 ob = FindObjectByID(self.objectRefID) 382 if not ob: 383 log.LogWarn('This Object could not be found %s' % self.objectRefID) 384 return 385 return ob[0] 386 387 def GetObjectName(self, object): 388 try: 389 className = object.__class__ 390 except AttributeError: 391 className = object.__bluetype__ 392 393 objectName = '%s at 0x%.8x' % (className, id(object)) 394 objectName = '%s - Ref Count = %s - %s' % (objectName, sys.getrefcount(object) - 4, getattr(object, 'destroyed', 'N/A')) 395 objectName = Swing(objectName) 396 return objectName 397 398 def GetObjectRepr(self, object): 399 objectRepr = Swing(srepr(object)) 400 if len(objectRepr) < 1001: 401 return objectRepr 402 return objectRepr[:1000] 403 404 def GetObjectSizes(self, object): 405 sizes = [ asizeof.asizeof(object, limit=l) for l in range(8) ] 406 return sizes 407 408 def PopulateScroll(self): 409 contentList = self.GetReferees() 410 self.scroll.Load(contentList=contentList) 411 412 def GetReferees(self): 413 refList = [] 414 referrers = gc.get_referrers(self.object) 415 known = [gc.garbage, 416 stackless.getcurrent().frame, 417 self.object, 418 self.scroll, 419 self.__dict__] 420 for ref in referrers: 421 if ref not in known: 422 try: 423 objectName = self.GetObjectName(ref) 424 refid = id(ref) 425 listEntry = ScrollEntryNode(decoClass=SE_GenericCore, id=id, name=objectName, label=objectName, OnDblClick=self.EntryDetails, OnGetMenu=self.EntryMenu, refid=refid) 426 refList.append(listEntry) 427 except Exception: 428 log.LogException('Invalid reference') 429 430 return refList 431 432 def EntryMenu(self, entry): 433 m = [('Open Details', self.EntryDetails, (entry,))] 434 return m 435 436 def EntryDetails(self, entry): 437 windowID = 'pythonobjectdetails_sub_%s' % id(self.object) 438 wnd = PythonObjectDetails.GetIfOpen(windowID=windowID) 439 if wnd: 440 wnd.Switch(entry.sr.node.refid) 441 wnd.Maximize() 442 else: 443 PythonObjectDetails.Open(refid=entry.sr.node.refid, windowID=windowID)