# Lint as: python2, python3 # Copyright 2014 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import errno, glob, logging, os, re, struct, sys, time from autotest_lib.client.bin import test from autotest_lib.client.bin import utils from autotest_lib.client.common_lib import error from autotest_lib.client.common_lib import utils as common_utils from autotest_lib.client.common_lib.cros import chrome from autotest_lib.client.cros import cros_logging from autotest_lib.client.cros.graphics import graphics_utils # Kernel 3.8 to 3.14 has cur_delay_info, 3.18+ has frequency_info. CLOCK_PATHS = [ '/sys/kernel/debug/dri/0/i915_frequency_info', '/sys/kernel/debug/dri/0/i915_cur_delayinfo' ] # Kernel 3.8 has i915_fbc, kernel > 3.8 i915_fbc_status. FBC_PATHS = [ '/sys/kernel/debug/dri/0/i915_fbc', '/sys/kernel/debug/dri/0/i915_fbc_status' ] GEM_OBJECTS_PATHS = ['/sys/kernel/debug/dri/0/i915_gem_objects'] GEM_PATHS = ['/sys/kernel/debug/dri/0/i915_gem_active'] PSR_PATHS = ['/sys/kernel/debug/dri/0/i915_edp_psr_status'] # Kernel 5.7+ has DRPC info in gt/ subdirectory RC6_PATHS = [ '/sys/kernel/debug/dri/0/i915_drpc_info', '/sys/kernel/debug/dri/0/gt/drpc' ] class graphics_Idle(graphics_utils.GraphicsTest): """Class for graphics_Idle. See 'control' for details.""" version = 1 _gpu_type = None _cpu_type = None _board = None def run_once(self, arc_mode=None): # If we are in arc_mode, do not report failures to perf dashboard. if arc_mode: self._test_failure_report_enable = False self.add_failures('graphics_Idle') with chrome.Chrome( logged_in=True, arc_mode=arc_mode) as cr: # The New Tab Page contains the Google doodle which can cause # arbitrary side effects. Hide it by going to a neutral page. if not cr.browser.tabs: cr.browser.tabs.New() tab = cr.browser.tabs[0] tab.Navigate('chrome://version') # Try to protect against runaway previous tests. if not utils.wait_for_idle_cpu(60.0, 0.1): logging.warning('Could not get idle CPU before running tests.') self._gpu_type = utils.get_gpu_family() self._cpu_type = utils.get_cpu_soc_family() self._board = utils.get_board() errors = '' errors += self.verify_graphics_dvfs() errors += self.verify_graphics_fbc() errors += self.verify_graphics_psr() errors += self.verify_graphics_gem_idle() errors += self.verify_graphics_i915_min_clock() errors += self.verify_graphics_rc6() errors += self.verify_lvds_downclock() errors += self.verify_short_blanking() if errors: raise error.TestFail('Failed: %s' % errors) self.remove_failures('graphics_Idle') def get_valid_path(self, paths): for path in paths: if os.path.exists(path): return path logging.error('Error: %s not found.', ' '.join(paths)) return None def handle_error(self, message, path=None): logging.error('Error: %s', message) # For debugging show the content of the file. if path is not None: with open(path, 'r') as text_file: logging.info('Content of %s\n%s', path, text_file.read()) # Dump the output of 'top'. utils.log_process_activity() return message def verify_lvds_downclock(self): """On systems which support LVDS downclock, checks the kernel log for a message that an LVDS downclock mode has been added.""" logging.info('Running verify_lvds_downclock') board = utils.get_board() if not (board == 'alex' or board == 'lumpy' or board == 'stout'): return '' # Get the downclock message from the logs. reader = cros_logging.LogReader() reader.set_start_by_reboot(-1) if not reader.can_find('Adding LVDS downclock mode'): return self.handle_error('LVDS downclock quirk not applied. ') return '' def verify_short_blanking(self): """On baytrail systems with a known panel, checks the kernel log for a message that a short blanking mode has been added.""" logging.info('Running verify_short_blanking') if self._gpu_type != 'baytrail' or utils.has_no_monitor(): return '' # Open the EDID to find the panel model. param_path = '/sys/class/drm/card0-eDP-1/edid' if not os.path.exists(param_path): logging.error('Error: %s not found.', param_path) return self.handle_error( 'Short blanking not added (no EDID found). ') with open(param_path, 'r') as edp_edid_file: edp_edid_file.seek(8) data = edp_edid_file.read(2) manufacturer = int(struct.unpack(' min (%u)', clock, min_freq) return self.handle_error('Did not see the min DVFS clock. ', clock_path) return '' def verify_graphics_fbc(self): """ On systems which support FBC, check that we can get into FBC; idle before doing so, and retry every second for 20 seconds.""" logging.info('Running verify_graphics_fbc') # Link's FBC is disabled (crbug.com/338588). # TODO(marcheu): remove this when/if we fix this bug. board = utils.get_board() if board == 'link': return '' # Machines which don't have a monitor can't get FBC. if utils.has_no_monitor(): return '' if (self._gpu_type == 'haswell' or self._gpu_type == 'ivybridge' or self._gpu_type == 'sandybridge'): tries = 0 found = False param_path = self.get_valid_path(FBC_PATHS) if not param_path: return 'FBC_PATHS not found.' while not found and tries < 20: time.sleep(1) with open(param_path, 'r') as fbc_info_file: for line in fbc_info_file: if re.search('FBC enabled', line): found = True break tries += 1 if not found: return self.handle_error('Did not see FBC enabled. ', param_path) return '' def verify_graphics_psr(self): """ On systems which support PSR, check that we can get into PSR; idle before doing so, and retry every second for 20 seconds.""" logging.info('Running verify_graphics_psr') if utils.get_cpu_soc_family() != 'x86_64': return '' tries = 0 found = False param_path = self.get_valid_path(PSR_PATHS) if not param_path: logging.warning("PSR_PATHS not found.") return '' kernel_version = utils.get_kernel_version()[0:4].rstrip(".") logging.info('Kernel version: %s', kernel_version) # First check if PSR is enabled on the device so # we can watch for the active values with open(param_path, 'r') as psr_info_file: match = None try: for line in psr_info_file: match = re.search(r'Enabled: yes', line) if match: logging.info('PSR enabled') break if not match: logging.warning('PSR not enabled') return '' except IOError as e: num, strerror = e.args # Newer kernels might report ENODEV when PSR not available. if num == errno.ENODEV: logging.warning('PSR not enabled') return '' else: logging.error('While accessing %s', param_path) logging.error(e) return self.handle_error('Unexpected PSR read failure. ') while not found and tries < 20: time.sleep(1) with open(param_path, 'r') as psr_info_file: for line in psr_info_file: # Kernels 4.4 and up if common_utils.compare_versions(kernel_version, '4.4') != -1: match = re.search(r'PSR status: .* \[SRDENT', line) if match: found = True logging.info('Found active with kernel >= 4.4') break # 3.18 kernel elif kernel_version == '3.18': match = re.search(r'Performance_Counter: 0', line) if match: found = True logging.info('Found active with 3.18 kernel') break # Older kernels (up to 3.14) else: match = re.search(r'Performance_Counter: ([\d])+', line) if match and int(match.group(1)) > 0: found = True logging.info('Found active with kernel <= 3.14') break tries += 1 if not found: return self.handle_error('Did not see PSR activity. ', param_path) return '' def verify_graphics_gem_idle(self): """ On systems which have i915, check that we can get all gem objects to become idle (i.e. the i915_gem_active list or i915_gem_objects client/process gem object counts need to go to 0); idle before doing so, and retry every second for 20 seconds.""" kernel_version = utils.get_kernel_version()[0:4].rstrip(".") # Skip test on kernel 5.10 and above. if common_utils.compare_versions(kernel_version, '5.10') != -1: # The data needed for this test was removed in the 5.10 kernel. # See b/179453336 for details. logging.info('Skipping gem idle check on 5.10 and above') return '' logging.info('Running verify_graphics_gem_idle') if utils.get_cpu_soc_family() == 'x86_64': tries = 0 found = False per_process_check = False gem_path = self.get_valid_path(GEM_PATHS) if not gem_path: gem_path = self.get_valid_path(GEM_OBJECTS_PATHS) if gem_path: per_process_check = True else: return 'GEM_PATHS not found.' # Checks 4.4 and later kernels if per_process_check: while not found and tries < 240: time.sleep(0.25) gem_objects_idle = False gem_active_search = False with open(gem_path, 'r') as gem_file: for line in gem_file: if gem_active_search: if re.search('\(0 active,', line): gem_objects_idle = True else: gem_objects_idle = False break elif line == '\n': gem_active_search = True if gem_objects_idle: found = True tries += 1 # Checks pre 4.4 kernels else: while not found and tries < 240: time.sleep(0.25) with open(gem_path, 'r') as gem_file: for line in gem_file: if re.search('Total 0 objects', line): found = True break tries += 1 if not found: return self.handle_error('Did not reach 0 gem actives. ', gem_path) return ''