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