[Piglit] [PATCH] log: Replace console output with a simpler output

Jordan Justen jljusten at gmail.com
Wed Feb 5 15:40:00 PST 2014


\o/

Reviewed-by: Jordan Justen <jordan.l.justen at intel.com>

On Thu, Jan 9, 2014 at 12:02 PM, Dylan Baker <baker.dylan.c at gmail.com> wrote:
> This replaces the console spewer with a simpler console reporting
> mechanism inspired by the output of ninja. This reduces code, remove all
> singleton instances and speeds up piglit runs. There is a drawback, the
> output is much more terse than the previous implementation, giving only
> the following output:
> [<finished>/<total>] Running Test(s): <test number>
> [16008/16011] Running Test(s): 16007 16008 16009 16010
>
> This means that one can't look at the output to readily see that all
> tests are skipping or failing. However, since these problems are usually
> related to environment setup, and not to piglit doing something wrong,
> these should be moved to some kind of prerun check instead of relying on
> user attentiveness. Since the test numbers update only when they are
> completed and hung test can be spotted.
>
> Performance examples:
> Master:
> ./piglit-run.py -c tets/quick.tests foo 1065.64s user 373.48s system
> 295% cpu 8:06.35 total
>
> With this patch:
> ./piglit-run.py -c tests/quick.tests foo  1045.33s user 353.20s system
> 315% cpu 7:23.56 total
>
> Signed-off-by: Dylan Baker <baker.dylan.c at gmail.com>
> ---
>  framework/core.py     | 23 +++++++------
>  framework/log.py      | 78 +++++++++++++++++++++++++++++---------------
>  framework/patterns.py | 90 ---------------------------------------------------
>  3 files changed, 63 insertions(+), 128 deletions(-)
>  delete mode 100644 framework/patterns.py
>
> diff --git a/framework/core.py b/framework/core.py
> index 8bcda5b..aa89584 100644
> --- a/framework/core.py
> +++ b/framework/core.py
> @@ -32,7 +32,7 @@ import string
>  import sys
>  import time
>  import traceback
> -from log import log
> +from log import Log
>  from cStringIO import StringIO
>  from textwrap import dedent
>  from threads import synchronized_self
> @@ -464,7 +464,7 @@ class Test:
>      def run(self):
>          raise NotImplementedError
>
> -    def execute(self, env, path, json_writer):
> +    def execute(self, env, path, log, json_writer):
>          '''
>          Run the test.
>
> @@ -472,13 +472,12 @@ class Test:
>              Fully qualified test name as a string.  For example,
>              ``spec/glsl-1.30/preprocessor/compiler/keywords/void.frag``.
>          '''
> -        def status(msg):
> -            log(msg=msg, channel=path)
>
> +        log_current = log.get_current()
>          # Run the test
>          if env.execute:
>              try:
> -                status("running")
> +                log.log()
>                  time_start = time.time()
>                  result = self.run(env)
>                  time_end = time.time()
> @@ -499,8 +498,6 @@ class Test:
>                  result['traceback'] = \
>                      "".join(traceback.format_tb(sys.exc_info()[2]))
>
> -            status(result['result'])
> -
>              if 'subtest' in result and len(result['subtest'].keys()) > 1:
>                  for test in result['subtest'].keys():
>                      result['result'] = result['subtest'][test]
> @@ -508,7 +505,8 @@ class Test:
>              else:
>                  json_writer.write_dict_item(path, result)
>          else:
> -            status("dry-run")
> +            log.log()
> +        log.mark_complete(log_current)
>
>
>  class Group(dict):
> @@ -565,6 +563,7 @@ class TestProfile:
>          '''
>
>          self.prepare_test_list(env)
> +        log = Log(len(self.test_list))
>
>          # If concurrency is set to 'all' run all tests out of a concurrent
>          # threadpool, if it's none, then run evey test serially. otherwise mix
> @@ -572,24 +571,24 @@ class TestProfile:
>          if env.concurrent == "all":
>              pool = ThreadPool(multiprocessing.cpu_count())
>              for (path, test) in self.test_list.items():
> -                pool.add(test.execute, (env, path, json_writer))
> +                pool.add(test.execute, (env, path, log, json_writer))
>              pool.join()
>          elif env.concurrent == "none":
>              pool = ThreadPool(1)
>              for (path, test) in self.test_list.items():
> -                pool.add(test.execute, (env, path, json_writer))
> +                pool.add(test.execute, (env, path, log, json_writer))
>              pool.join()
>          else:
>              pool = ThreadPool(multiprocessing.cpu_count())
>              for (path, test) in self.test_list.items():
>                  if test.runConcurrent:
> -                    pool.add(test.execute, (env, path, json_writer))
> +                    pool.add(test.execute, (env, path, log, json_writer))
>              pool.join()
>
>              pool = ThreadPool(1)
>              for (path, test) in self.test_list.items():
>                  if not test.runConcurrent:
> -                    pool.add(test.execute, (env, path, json_writer))
> +                    pool.add(test.execute, (env, path, log, json_writer))
>              pool.join()
>
>      def remove_test(self, test_path):
> diff --git a/framework/log.py b/framework/log.py
> index 310c552..167e4ea 100644
> --- a/framework/log.py
> +++ b/framework/log.py
> @@ -1,5 +1,4 @@
> -#
> -# Copyright (c) 2010 Intel Corporation
> +# Copyright (c) 2013 Intel Corporation
>  #
>  # Permission is hereby granted, free of charge, to any person obtaining a
>  # copy of this software and associated documentation files (the "Software"),
> @@ -21,33 +20,60 @@
>  # IN THE SOFTWARE.
>  #
>
> -import logging
> -
> +import sys
>  from threads import synchronized_self
> -from patterns import Singleton
>
>
> -class Logger(Singleton):
> +class Log(object):
> +    """ Print Progress to stdout
> +
> +    Arguments:
> +    total -- The total number of tests to run.
> +
> +    """
> +    def __init__(self, total):
> +        self.__total = total
> +        self.__complete = 1
> +        self.__running = []
> +        self.__generator = (x for x in xrange(self.__total))
> +        self.__pad = len(str(self.__total))
> +
> +    def _running(self):
> +        return "Running Test(s): {}".format(
> +            " ".join([str(x).zfill(self.__pad) for x in self.__running]))
> +
> +    def _percent(self):
> +        return "[{0}/{1}]".format(str(self.__complete).zfill(self.__pad),
> +                                  str(self.__total).zfill(self.__pad))
> +
> +    @synchronized_self
> +    def mark_complete(self, value):
> +        """ Used to mark a test as complete in the log
> +
> +        Arguments:
> +        value -- the test number to mark complete
> +
> +        """
> +        # Mark running complete
> +        assert value in self.__running
> +        self.__running.remove(value)
> +
> +        # increment the number of completed tests
> +        self.__complete += 1
> +
>      @synchronized_self
> -    def __logMessage(self, logfunc, message, **kwargs):
> -        [logfunc(line, **kwargs) for line in message.split('\n')]
> +    def log(self):
> +        """ Print to the screen
> +
> +        Works by moving the cursor back to the front of the line and printing
> +        over it.
> +
> +        """
> +        sys.stdout.write("{0} {1} \r".format(self._percent(), self._running()))
>
>      @synchronized_self
> -    def getLogger(self, channel=None):
> -        if 0 == len(logging.root.handlers):
> -            logging.basicConfig(format="[%(asctime)s] :: %(message)+8s "
> -                                       ":: %(name)s",
> -                                datefmt="%c",
> -                                level=logging.INFO)
> -        if channel is None:
> -            channel = "base"
> -        logger = logging.getLogger(channel)
> -        return logger
> -
> -    def log(self, type=logging.INFO, msg="", channel=None):
> -        self.__logMessage(lambda m,
> -                          **kwargs: self.getLogger(channel).log(type,
> -                                                                m,
> -                                                                **kwargs), msg)
> -
> -log = Logger().log
> +    def get_current(self):
> +        """ Returns a new number to know what processes are running """
> +        x = self.__generator.next()
> +        self.__running.append(x)
> +        return x
> diff --git a/framework/patterns.py b/framework/patterns.py
> deleted file mode 100644
> index bcf4e7e..0000000
> --- a/framework/patterns.py
> +++ /dev/null
> @@ -1,90 +0,0 @@
> -#
> -# Copyright (c) 2010 Intel Corporation
> -#
> -# Permission is hereby granted, free of charge, to any person obtaining a
> -# copy of this software and associated documentation files (the "Software"),
> -# to deal in the Software without restriction, including without limitation
> -# the rights to use, copy, modify, merge, publish, distribute, sublicense,
> -# and/or sell copies of the Software, and to permit persons to whom the
> -# Software is furnished to do so, subject to the following conditions:
> -#
> -# The above copyright notice and this permission notice (including the next
> -# paragraph) shall be included in all copies or substantial portions of the
> -# Software.
> -#
> -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> -# IN THE SOFTWARE.
> -#
> -
> -import threading
> -
> -
> -class Singleton(object):
> -    '''
> -    Modeled after
> -    http://www.python.org/download/releases/2.2.3/descrintro/*__new__
> -
> -    A thread-safe (mostly -- see NOTE) Singleton class pattern.
> -
> -    NOTE: deleting a singleton instance (i.e. Singleton::delInstance) does not
> -    guarantee that nothing else is currently using it. To reduce this risk, a
> -    program should not hold a reference to the instance.  Rather, use the
> -    create/construct syntax (see example below) to access the instance.  Yet,
> -    this still does not guarantee that this type of usage will result in a
> -    desired effect in a multithreaded program.
> -    You've been warned so use the singleton pattern wisely!
> -
> -    Example:
> -
> -    class MySingletonClass(Singleton):
> -            def init(self):
> -                    print "in MySingletonClass::init()", self
> -
> -            def foo(self):
> -                    print "in MySingletonClass::foo()", self
> -
> -    MySingletonClass().foo()
> -    MySingletonClass().foo()
> -    MySingletonClass().foo()
> -
> -    ---> output will look something like this:
> -    in MySingletonClass::init() <__main__.MySingletonClass object at 0x7ff5b322f3d0>
> -    in MySingletonClass::foo() <__main__.MySingletonClass object at 0x7ff5b322f3d0>
> -    in MySingletonClass::foo() <__main__.MySingletonClass object at 0x7ff5b322f3d0>
> -    in MySingletonClass::foo() <__main__.MySingletonClass object at 0x7ff5b322f3d0>
> -    '''
> -
> -    lock = threading.RLock()
> -
> -    def __new__(cls, *args, **kwargs):
> -        try:
> -            cls.lock.acquire()
> -            it = cls.__dict__.get('__it__')
> -            if it is not None:
> -                return it
> -            cls.__it__ = it = object.__new__(cls)
> -            it.init(*args, **kwargs)
> -            return it
> -        finally:
> -            cls.lock.release()
> -
> -    def init(self, *args, **kwargs):
> -        '''
> -        Derived classes should override this method to do its initializations
> -        The derived class should not implement a '__init__' method.
> -        '''
> -        pass
> -
> -    @classmethod
> -    def delInstance(cls):
> -        cls.lock.acquire()
> -        try:
> -            if cls.__dict__.get('__it__') is not None:
> -                del cls.__it__
> -        finally:
> -            cls.lock.release()
> --
> 1.8.5.2
>
> _______________________________________________
> Piglit mailing list
> Piglit at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/piglit


More information about the Piglit mailing list