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