Home | History | Annotate | Download | only in graphics_Idle
      1 # Copyright 2014 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import logging, os, re, struct, time
      6 
      7 from autotest_lib.client.bin import test
      8 from autotest_lib.client.bin import utils
      9 from autotest_lib.client.common_lib import error
     10 from autotest_lib.client.common_lib.cros import chrome
     11 from autotest_lib.client.cros import cros_logging
     12 from autotest_lib.client.cros.graphics import graphics_utils
     13 
     14 # Kernel 3.8 to 3.14 has cur_delay_info, 3.18+ has frequency_info.
     15 CLOCK_PATHS = [
     16     '/sys/kernel/debug/dri/0/i915_frequency_info',
     17     '/sys/kernel/debug/dri/0/i915_cur_delayinfo'
     18 ]
     19 # Kernel 3.8 has i915_fbc, kernel > 3.8 i915_fbc_status.
     20 FBC_PATHS = [
     21     '/sys/kernel/debug/dri/0/i915_fbc',
     22     '/sys/kernel/debug/dri/0/i915_fbc_status'
     23 ]
     24 GEM_OBJECTS_PATHS = ['/sys/kernel/debug/dri/0/i915_gem_objects']
     25 GEM_PATHS = ['/sys/kernel/debug/dri/0/i915_gem_active']
     26 PSR_PATHS = ['/sys/kernel/debug/dri/0/i915_edp_psr_status']
     27 RC6_PATHS = ['/sys/kernel/debug/dri/0/i915_drpc_info']
     28 
     29 
     30 class graphics_Idle(graphics_utils.GraphicsTest):
     31     """Class for graphics_Idle.  See 'control' for details."""
     32     version = 1
     33     _gpu_type = None
     34     _cpu_type = None
     35     _board = None
     36 
     37     def run_once(self, arc_mode=None):
     38         # If we are in arc_mode, do not report failures to perf dashboard.
     39         if arc_mode:
     40             self._test_failure_report_enable = False
     41 
     42         # We use kiosk mode to make sure Chrome is idle.
     43         self.add_failures('Graphics_Idle')
     44         with chrome.Chrome(
     45                 logged_in=False, extra_browser_args=['--kiosk'],
     46                 arc_mode=arc_mode):
     47             # Try to protect against runaway previous tests.
     48             if not utils.wait_for_idle_cpu(20.0, 0.1):
     49                 logging.warning('Could not get idle CPU before running tests.')
     50             self._gpu_type = utils.get_gpu_family()
     51             self._cpu_type = utils.get_cpu_soc_family()
     52             self._board = utils.get_board()
     53             errors = ''
     54             errors += self.verify_graphics_dvfs()
     55             errors += self.verify_graphics_fbc()
     56             errors += self.verify_graphics_psr()
     57             errors += self.verify_graphics_gem_idle()
     58             errors += self.verify_graphics_i915_min_clock()
     59             errors += self.verify_graphics_rc6()
     60             errors += self.verify_lvds_downclock()
     61             errors += self.verify_short_blanking()
     62             if errors:
     63                 raise error.TestFail('Failed: %s' % errors)
     64         self.remove_failures('Graphics_Idle')
     65 
     66     def get_valid_path(self, paths):
     67         for path in paths:
     68             if os.path.exists(path):
     69                 return path
     70         logging.error('Error: %s not found.', ' '.join(paths))
     71         return None
     72 
     73     def handle_error(self, message, path=None):
     74         logging.error('Error: %s', message)
     75         # For debugging show the content of the file.
     76         if path is not None:
     77             with open(path, 'r') as text_file:
     78                 logging.info('Content of %s\n%s', path, text_file.read())
     79         # Dump the output of 'top'.
     80         utils.log_process_activity()
     81         return message
     82 
     83     def verify_lvds_downclock(self):
     84         """On systems which support LVDS downclock, checks the kernel log for
     85         a message that an LVDS downclock mode has been added."""
     86         logging.info('Running verify_lvds_downclock')
     87         board = utils.get_board()
     88         if not (board == 'alex' or board == 'lumpy' or board == 'stout'):
     89             return ''
     90         # Get the downclock message from the logs.
     91         reader = cros_logging.LogReader()
     92         reader.set_start_by_reboot(-1)
     93         if not reader.can_find('Adding LVDS downclock mode'):
     94             return self.handle_error('LVDS downclock quirk not applied. ')
     95         return ''
     96 
     97     def verify_short_blanking(self):
     98         """On baytrail systems with a known panel, checks the kernel log for a
     99         message that a short blanking mode has been added."""
    100         logging.info('Running verify_short_blanking')
    101         if self._gpu_type != 'baytrail' or utils.has_no_monitor():
    102             return ''
    103 
    104         # Open the EDID to find the panel model.
    105         param_path = '/sys/class/drm/card0-eDP-1/edid'
    106         if not os.path.exists(param_path):
    107             logging.error('Error: %s not found.', param_path)
    108             return self.handle_error(
    109                 'Short blanking not added (no EDID found). ')
    110 
    111         with open(param_path, 'r') as edp_edid_file:
    112             edp_edid_file.seek(8)
    113             data = edp_edid_file.read(2)
    114             manufacturer = int(struct.unpack('<H', data)[0])
    115             data = edp_edid_file.read(2)
    116             product_code = int(struct.unpack('<H', data)[0])
    117         # This is not the panel we are looking for (AUO B116XTN02.2)
    118         if manufacturer != 0xaf06 or product_code != 0x225c:
    119             return ''
    120         # Get the downclock message from the logs.
    121         reader = cros_logging.LogReader()
    122         reader.set_start_by_reboot(-1)
    123         if not reader.can_find('Modified preferred into a short blanking mode'):
    124             return self.handle_error('Short blanking not added. ')
    125         return ''
    126 
    127     def verify_graphics_rc6(self):
    128         """ On systems which support RC6 (non atom), check that we are able to
    129         get into rc6; idle before doing so, and retry every second for 20
    130         seconds."""
    131         logging.info('Running verify_graphics_rc6')
    132         # TODO(ihf): Implement on baytrail/braswell using residency counters.
    133         # But note the format changed since SNB, so this will be complex.
    134         if (utils.get_cpu_soc_family() == 'x86_64' and
    135                 self._gpu_type != 'pinetrail' and
    136                 self._gpu_type != 'baytrail' and self._gpu_type != 'braswell'):
    137             tries = 0
    138             found = False
    139             param_path = self.get_valid_path(RC6_PATHS)
    140             if not param_path:
    141                 return 'RC6_PATHS not found.'
    142             while found == False and tries < 20:
    143                 time.sleep(1)
    144                 with open(param_path, 'r') as drpc_info_file:
    145                     for line in drpc_info_file:
    146                         match = re.search(r'Current RC state: (.*)', line)
    147                         if match and match.group(1) == 'RC6':
    148                             found = True
    149                             break
    150                 tries += 1
    151             if not found:
    152                 return self.handle_error('Error: did not see the GPU in RC6.',
    153                                          param_path)
    154         return ''
    155 
    156     def verify_graphics_i915_min_clock(self):
    157         """ On i915 systems, check that we get into the lowest clock frequency;
    158         idle before doing so, and retry every second for 20 seconds."""
    159         logging.info('Running verify_graphics_i915_min_clock')
    160 
    161         # TODO(benzh): enable once crbug.com/719040 is fixed.
    162         if self._gpu_type == 'baytrail' and utils.count_cpus() == 4:
    163             logging.info('Waived min clock check due to crbug.com/719040')
    164             return ''
    165 
    166         if (utils.get_cpu_soc_family() == 'x86_64' and
    167                 self._gpu_type != 'pinetrail'):
    168             tries = 0
    169             found = False
    170             param_path = self.get_valid_path(CLOCK_PATHS)
    171             if not param_path:
    172                 return 'CLOCK_PATHS not found.'
    173             while not found and tries < 80:
    174                 time.sleep(0.25)
    175 
    176                 with open(param_path, 'r') as delayinfo_file:
    177                     for line in delayinfo_file:
    178                         # This file has a different format depending on the
    179                         # board, so we parse both. Also, it would be tedious
    180                         # to add the minimum clock for each board, so instead
    181                         # we use 650MHz which is the max of the minimum clocks.
    182                         match = re.search(r'CAGF: (.*)MHz', line)
    183                         if match and int(match.group(1)) <= 650:
    184                             found = True
    185                             break
    186 
    187                         match = re.search(r'current GPU freq: (.*) MHz', line)
    188                         if match and int(match.group(1)) <= 650:
    189                             found = True
    190                             break
    191 
    192                 tries += 1
    193 
    194             if not found:
    195                 return self.handle_error('Did not see the min i915 clock. ',
    196                                          param_path)
    197 
    198         return ''
    199 
    200     def verify_graphics_dvfs(self):
    201         """ On systems which support DVFS, check that we get into the lowest
    202         clock frequency; idle before doing so, and retry every second for 20
    203         seconds."""
    204         logging.info('Running verify_graphics_dvfs')
    205 
    206         exynos_node = '/sys/devices/11800000.mali/'
    207         rk3288_node = '/sys/devices/ffa30000.gpu/'
    208         rk3399_node = '/sys/devices/platform/ff9a0000.gpu/devfreq/ff9a0000.gpu/'
    209         mt8173_node = ('/sys/devices/soc/13000000.mfgsys-gpu/devfreq/'
    210                        '13000000.mfgsys-gpu/')
    211 
    212         if self._cpu_type == 'exynos5':
    213             if os.path.isdir(exynos_node):
    214                 node = exynos_node
    215                 use_devfreq = False
    216                 enable_node = 'dvfs'
    217                 enable_value = 'on'
    218             else:
    219                 logging.error('Error: unknown exynos SoC.')
    220                 return self.handle_error('Unknown exynos SoC.')
    221         elif self._cpu_type.startswith('rockchip'):
    222             if os.path.isdir(rk3288_node):
    223                 node = rk3288_node
    224                 use_devfreq = False
    225                 enable_node = 'dvfs_enable'
    226                 enable_value = '1'
    227             elif os.path.isdir(rk3399_node):
    228                 node = rk3399_node
    229                 use_devfreq = True
    230             else:
    231                 logging.error('Error: unknown rockchip SoC.')
    232                 return self.handle_error('Unknown rockchip SoC.')
    233         elif self._cpu_type == 'mediatek':
    234             if os.path.isdir(mt8173_node):
    235                 node = mt8173_node
    236                 use_devfreq = True
    237             else:
    238                 logging.error('Error: unknown mediatek SoC.')
    239                 return self.handle_error('Unknown mediatek SoC.')
    240         else:
    241             return ''
    242 
    243         if use_devfreq:
    244             governor_path = utils.locate_file('governor', node)
    245             clock_path = utils.locate_file('cur_freq', node)
    246 
    247             governor = utils.read_one_line(governor_path)
    248             logging.info('DVFS governor = %s', governor)
    249             if not governor == 'simple_ondemand':
    250                 logging.error('Error: DVFS governor is not simple_ondemand.')
    251                 return self.handle_error('Governor is wrong.')
    252         else:
    253             clock_path = utils.locate_file('clock', node)
    254             enable_path = utils.locate_file(enable_node, node)
    255 
    256             enable = utils.read_one_line(enable_path)
    257             logging.info('DVFS enable = %s', enable)
    258             if not enable == enable_value:
    259                 return self.handle_error('DVFS is not enabled. ')
    260 
    261         freqs_path = utils.locate_file('available_frequencies', node)
    262 
    263         # available_frequencies are always sorted in ascending order
    264         # each line may contain one or multiple integers separated by spaces
    265         min_freq = int(utils.read_one_line(freqs_path).split()[0])
    266 
    267         # daisy_* (exynos5250) boards set idle frequency to 266000000
    268         # See: crbug.com/467401 and crosbug.com/p/19710
    269         if self._board.startswith('daisy'):
    270             min_freq = 266000000
    271 
    272         logging.info('Expecting idle DVFS clock = %u', min_freq)
    273         tries = 0
    274         found = False
    275         while not found and tries < 80:
    276             time.sleep(0.25)
    277             clock = int(utils.read_one_line(clock_path))
    278             if clock <= min_freq:
    279                 logging.info('Found idle DVFS clock = %u', clock)
    280                 found = True
    281                 break
    282 
    283             tries += 1
    284         if not found:
    285             logging.error('Error: DVFS clock (%u) > min (%u)', clock, min_freq)
    286             return self.handle_error('Did not see the min DVFS clock. ',
    287                                      clock_path)
    288         return ''
    289 
    290     def verify_graphics_fbc(self):
    291         """ On systems which support FBC, check that we can get into FBC;
    292         idle before doing so, and retry every second for 20 seconds."""
    293         logging.info('Running verify_graphics_fbc')
    294 
    295         # Link's FBC is disabled (crbug.com/338588).
    296         # TODO(marcheu): remove this when/if we fix this bug.
    297         board = utils.get_board()
    298         if board == 'link':
    299             return ''
    300 
    301         # Machines which don't have a monitor can't get FBC.
    302         if utils.has_no_monitor():
    303             return ''
    304 
    305         if (self._gpu_type == 'haswell' or self._gpu_type == 'ivybridge' or
    306                 self._gpu_type == 'sandybridge'):
    307             tries = 0
    308             found = False
    309             param_path = self.get_valid_path(FBC_PATHS)
    310             if not param_path:
    311                 return 'FBC_PATHS not found.'
    312             while not found and tries < 20:
    313                 time.sleep(1)
    314                 with open(param_path, 'r') as fbc_info_file:
    315                     for line in fbc_info_file:
    316                         if re.search('FBC enabled', line):
    317                             found = True
    318                             break
    319 
    320                 tries += 1
    321             if not found:
    322                 return self.handle_error('Did not see FBC enabled. ',
    323                                          param_path)
    324         return ''
    325 
    326     def verify_graphics_psr(self):
    327         """ On systems which support PSR, check that we can get into PSR;
    328         idle before doing so, and retry every second for 20 seconds."""
    329         logging.info('Running verify_graphics_psr')
    330 
    331         board = utils.get_board()
    332         if board != 'samus':
    333             return ''
    334         tries = 0
    335         found = False
    336         param_path = self.get_valid_path(PSR_PATHS)
    337         if not param_path:
    338             return 'PSR_PATHS not found.'
    339         while not found and tries < 20:
    340             time.sleep(1)
    341             with open(param_path, 'r') as fbc_info_file:
    342                 for line in fbc_info_file:
    343                     match = re.search(r'Performance_Counter: (.*)', line)
    344                     if match and int(match.group(1)) > 0:
    345                         found = True
    346                         break
    347 
    348             tries += 1
    349         if not found:
    350             return self.handle_error('Did not see PSR enabled. ', param_path)
    351         return ''
    352 
    353     def verify_graphics_gem_idle(self):
    354         """ On systems which have i915, check that we can get all gem objects
    355         to become idle (i.e. the i915_gem_active list or i915_gem_objects
    356         client/process gem object counts need to go to 0);
    357         idle before doing so, and retry every second for 20 seconds."""
    358         logging.info('Running verify_graphics_gem_idle')
    359         if utils.get_cpu_soc_family() == 'x86_64':
    360             tries = 0
    361             found = False
    362             per_process_check = False
    363 
    364             gem_path = self.get_valid_path(GEM_PATHS)
    365             if not gem_path:
    366                 gem_path = self.get_valid_path(GEM_OBJECTS_PATHS)
    367                 if gem_path:
    368                     per_process_check = True
    369                 else:
    370                     return 'GEM_PATHS not found.'
    371 
    372             # Checks 4.4 and later kernels
    373             if per_process_check:
    374                 while not found and tries < 240:
    375                     time.sleep(0.25)
    376                     gem_objects_idle = False
    377                     gem_active_search = False
    378                     with open(gem_path, 'r') as gem_file:
    379                         for line in gem_file:
    380                             if gem_active_search:
    381                                 if re.search('\(0 active,', line):
    382                                     gem_objects_idle = True
    383                                 else:
    384                                     gem_objects_idle = False
    385                                     break
    386                             elif line == '\n':
    387                                 gem_active_search = True
    388                         if gem_objects_idle:
    389                             found = True
    390                     tries += 1
    391 
    392             # Checks pre 4.4 kernels
    393             else:
    394                 while not found and tries < 240:
    395                     time.sleep(0.25)
    396                     with open(gem_path, 'r') as gem_file:
    397                         for line in gem_file:
    398                             if re.search('Total 0 objects', line):
    399                                 found = True
    400                                 break
    401                     tries += 1
    402             if not found:
    403                 return self.handle_error('Did not reach 0 gem actives. ',
    404                                          gem_path)
    405         return ''
    406