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