diff --git a/fsl/__init__.py b/fsl/__init__.py index 5613aec3f6abe8cc23c3494f00b4405b75a38727..790be29422ef1af2b4dcada525dd11ca6022e4da 100644 --- a/fsl/__init__.py +++ b/fsl/__init__.py @@ -59,28 +59,41 @@ import argparse import subprocess -log = logging.getLogger(__name__) - - # make matplotlib quiet warnings.filterwarnings('ignore', module='matplotlib') warnings.filterwarnings('ignore', module='mpl_toolkits') +# My own custom logging level for tracing memory related events +logging.MEMORY = 15 +def logmemory(self, message, *args, **kwargs): + if self.isEnabledFor(logging.MEMORY): + self._log(logging.MEMORY, message, args, **kwargs) + +logging.Logger.memory = logmemory +logging.addLevelName(logging.MEMORY, 'MEMORY') + # There's a bug in OpenGL.GL.shaders (which has been fixed in # the latest version) - it calls logging.basicConfig(), and # thus screws up our own logging. We overcome this by configuring # the root logger before OpenGL.GL.shaders is imported (which # occurs when fsl.fslview.gl.slicecanvas.SliceCanvas is imported). -logging.basicConfig( - format='%(levelname)8.8s ' - '%(filename)20.20s ' - '%(lineno)4d: ' - '%(funcName)-15.15s - ' - '%(message)s') + +logFormatter = logging.Formatter('%(levelname)8.8s ' + '%(filename)20.20s ' + '%(lineno)4d: ' + '%(funcName)-15.15s - ' + '%(message)s') +logHandler = logging.StreamHandler() +logHandler.setFormatter(logFormatter) + + log = logging.getLogger('fsl') +log.addHandler(logHandler) + + import fsl.tools as tools @@ -176,6 +189,10 @@ def parseArgs(argv, allTools): parser.add_argument( '-n', '--noisy', metavar='MODULE', action='append', help='Make the specified module noisy') + + parser.add_argument( + '-m', '--memory', action='store_true', + help='Output memory events (implied if -v is set)') parser.add_argument( '-w', '--wxinspect', action='store_true', @@ -242,12 +259,23 @@ def parseArgs(argv, allTools): # Configure any logging verbosity # settings specified by the user + if namespace.verbose is None: + if namespace.memory: + class MemFilter(object): + def filter(self, record): + if record.levelno == logging.MEMORY: return 1 + else: return 0 + + log.setLevel(logging.MEMORY) + log.handlers[0].addFilter(MemFilter()) + log.memory('Added filter for MEMORY messages') + if namespace.verbose == 1: log.setLevel(logging.DEBUG) # make some noisy things quiet - logging.getLogger('fsl.fslview.gl') .setLevel(logging.WARNING) - logging.getLogger('fsl.fslview.views').setLevel(logging.WARNING) + logging.getLogger('fsl.fslview.gl') .setLevel(logging.MEMORY) + logging.getLogger('fsl.fslview.views').setLevel(logging.MEMORY) logging.getLogger('props') .setLevel(logging.WARNING) logging.getLogger('pwidgets') .setLevel(logging.WARNING) elif namespace.verbose == 2: diff --git a/fsl/data/image.py b/fsl/data/image.py index cb4bdf27a57e403a0e253ba9e5bf401695f0c67b..f27dfdb404a63b25ddad4bdd7e43fc52ea17fcd8 100644 --- a/fsl/data/image.py +++ b/fsl/data/image.py @@ -168,6 +168,12 @@ class Image(props.HasProperties): if len(self.shape) < 3 or len(self.shape) > 4: raise RuntimeError('Only 3D or 4D images are supported') + log.memory('{}.init ({})'.format(type(self).__name__, id(self))) + + + def __del__(self): + log.memory('{}.del ({})'.format(type(self).__name__, id(self))) + def loadData(self): """Loads the image data from the file. This method only needs to diff --git a/fsl/data/model.py b/fsl/data/model.py index d286c65720542b81a1d0a7bc4cecea4bcb51aea2..542ed38d65a4ad900823f198ca304d1ae417b056 100644 --- a/fsl/data/model.py +++ b/fsl/data/model.py @@ -5,9 +5,15 @@ # Author: Paul McCarthy <pauldmccarthy@gmail.com> # +import logging + import os.path as op import numpy as np + +log = logging.getLogger(__name__) + + ALLOWED_EXTENSIONS = ['.vtk'] EXTENSION_DESCRIPTIONS = ['VTK polygon model file'] @@ -78,6 +84,12 @@ class Model(object): self.vertices = np.array(data, dtype=np.float32) self.indices = indices + log.memory('{}.init ({})'.format(type(self).__name__, id(self))) + + + def __del__(self): + log.memory('{}.del ({})'.format(type(self).__name__, id(self))) + def __repr__(self): return '{}({}, {})'.format(type(self).__name__, diff --git a/fsl/fslview/displaycontext/display.py b/fsl/fslview/displaycontext/display.py index 4d930bd3131f35ebcc6a4ed3c6b073e33e47efd4..bb38e7af0f61dd21b9a76039c2bb94438c8ae6eb 100644 --- a/fsl/fslview/displaycontext/display.py +++ b/fsl/fslview/displaycontext/display.py @@ -47,6 +47,12 @@ class DisplayOpts(props.SyncableHasProperties): self.overlayType = display.overlayType self.name = '{}_{}'.format(type(self).__name__, id(self)) + log.memory('{}.init ({})'.format(type(self).__name__, id(self))) + + + def __del__(self): + log.memory('{}.del ({})'.format(type(self).__name__, id(self))) + def destroy(self): """If overridden, this method should be called by the subclass @@ -200,7 +206,12 @@ class Display(props.SyncableHasProperties): self.__displayOpts = None self.__overlayTypeChanged() + log.memory('{}.init ({})'.format(type(self).__name__, id(self))) + + def __del__(self): + log.memory('{}.del ({})'.format(type(self).__name__, id(self))) + def destroy(self): """This method should be called when this ``Display`` instance diff --git a/fsl/fslview/gl/globject.py b/fsl/fslview/gl/globject.py index ce1463265bd4a0a8cc69cd4c0b663ddc06fb4d45..6a143bd43ed33bb0813b54cf5e8b8991f5ff2b8a 100644 --- a/fsl/fslview/gl/globject.py +++ b/fsl/fslview/gl/globject.py @@ -12,6 +12,7 @@ mappings between overlay objects and their corresponding OpenGL representation. """ +import logging import numpy as np @@ -19,6 +20,9 @@ import routines as glroutines import fsl.utils.transform as transform +log = logging.getLogger(__name__) + + def createGLObject(overlay, display): """Create :class:`GLObject` instance for the given overlay, as specified by the :attr:`.Display.overlayType` property. @@ -73,7 +77,7 @@ class GLObject(object): self.zax = 2 self.__updateListeners = {} - + def addUpdateListener(self, name, listener): """Adds a listener function which will be called whenever this @@ -225,6 +229,12 @@ class GLImageObject(GLObject): self.display = display self.displayOpts = display.getDisplayOpts() + log.memory('{}.init ({})'.format(type(self).__name__, id(self))) + + + def __del__(self): + log.memory('{}.del ({})'.format(type(self).__name__, id(self))) + def destroy(self): """If this method is overridden, it should be called by the subclass