• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 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 collections
6import logging
7import re
8import six
9
10from autotest_lib.client.bin import utils
11from autotest_lib.client.common_lib import error
12
13
14def get_histogram_text(tab, histogram_name):
15    """
16     This returns contents of the given histogram.
17
18     @param tab: object, Chrome tab instance
19     @param histogram_name: string, name of the histogram
20     @returns string: contents of the histogram
21     """
22    docEle = 'document.documentElement'
23    tab.Navigate('chrome://histograms/%s' % histogram_name)
24    tab.WaitForDocumentReadyStateToBeComplete()
25    raw_text = tab.EvaluateJavaScript('{0} && {0}.innerText'.format(docEle))
26    # extract the contents of the histogram
27    histogram = raw_text[raw_text.find('Histogram:'):].strip()
28    if histogram:
29        logging.debug('chrome://histograms/%s:\n%s', histogram_name, histogram)
30    else:
31        logging.debug('No histogram is shown in chrome://histograms/%s',
32                      histogram_name)
33    return histogram
34
35
36def loaded(tab, histogram_name, pattern):
37    """
38     Checks if the histogram page has been fully loaded.
39
40     @param tab: object, Chrome tab instance
41     @param histogram_name: string, name of the histogram
42     @param pattern: string, required text to look for
43     @returns re.MatchObject if the given pattern is found in the text
44              None otherwise
45
46     """
47    return re.search(pattern, get_histogram_text(tab, histogram_name))
48
49
50def  verify(cr, histogram_name, histogram_bucket_value):
51    """
52     Verifies histogram string and success rate in a parsed histogram bucket.
53     The histogram buckets are outputted in debug log regardless of the
54     verification result.
55
56     Full histogram URL is used to load histogram. Example Histogram URL is :
57     chrome://histograms/Media.GpuVideoDecoderInitializeStatus
58
59     @param cr: object, the Chrome instance
60     @param histogram_name: string, name of the histogram
61     @param histogram_bucket_value: int, required bucket number to look for
62     @raises error.TestError if histogram is not successful
63
64     """
65    bucket_pattern = '\n' + str(histogram_bucket_value) + '.*100\.0%.*'
66    error_msg_format = ('{} not loaded or histogram bucket not found '
67                        'or histogram bucket found at < 100%')
68    tab = cr.browser.tabs.New()
69    msg = error_msg_format.format(histogram_name)
70    utils.poll_for_condition(lambda: loaded(tab, histogram_name, bucket_pattern
71                                            ),
72                             exception=error.TestError(msg),
73                             sleep_interval=1)
74
75
76def is_bucket_present(cr,histogram_name, histogram_bucket_value):
77    """
78     This returns histogram succes or fail to called function
79
80     @param cr: object, the Chrome instance
81     @param histogram_name: string, name of the histogram
82     @param histogram_bucket_value: int, required bucket number to look for
83     @returns True if histogram page was loaded and the bucket was found.
84              False otherwise
85
86     """
87    try:
88        verify(cr, histogram_name, histogram_bucket_value)
89    except error.TestError:
90        return False
91    else:
92        return True
93
94
95def is_histogram_present(cr, histogram_name):
96    """
97     This checks if the given histogram is present and non-zero.
98
99     @param cr: object, the Chrome instance
100     @param histogram_name: string, name of the histogram
101     @returns True if histogram page was loaded and the histogram is present
102              False otherwise
103
104     """
105    histogram_pattern = 'Histogram: '+ histogram_name + ' recorded ' + \
106                        r'[1-9][0-9]*' + ' samples'
107    tab = cr.browser.tabs.New()
108    try:
109        utils.poll_for_condition(lambda: loaded(tab, histogram_name,
110                                                histogram_pattern),
111                                 timeout=2,
112                                 sleep_interval=0.1)
113        return True
114    except utils.TimeoutError:
115        # the histogram is not present, and then returns false
116        return False
117
118
119def get_histogram(cr, histogram_name):
120    """
121     This returns contents of the given histogram.
122
123     @param cr: object, the Chrome instance
124     @param histogram_name: string, name of the histogram
125     @returns string: contents of the histogram
126
127     """
128    tab = cr.browser.tabs.New()
129    return get_histogram_text(tab, histogram_name)
130
131
132def parse_histogram(histogram_text):
133    """
134     Parses histogram text into bucket structure.
135
136     @param histogram_text: histogram raw text.
137     @returns dict(bucket_value, bucket_count)
138     """
139    # Match separator line, e.g. "1   ..."
140    RE_SEPEARTOR = re.compile(r'\d+\s+\.\.\.')
141    # Match bucket line, e.g. "2  --O  (46 = 1.5%) {46.1%}"
142    RE_BUCKET = re.compile(r'(\d+)\s+\-*O\s+\((\d+) = (\d+\.\d+)%\).*')
143    result = {}
144    for line in histogram_text.splitlines():
145        if RE_SEPEARTOR.match(line):
146            continue
147        m = RE_BUCKET.match(line)
148        if m:
149            result[int(m.group(1))] = int(m.group(2))
150    return result
151
152
153def subtract_histogram(minuend, subtrahend):
154    """
155     Subtracts histogram: minuend - subtrahend
156
157     @param minuend: histogram bucket dict from which another is to be
158                     subtracted.
159     @param subtrahend: histogram bucket dict to be subtracted from another.
160     @result difference of the two histograms in bucket dict. Note that
161             zero-counted buckets are removed.
162     """
163    result = collections.defaultdict(int, minuend)
164    for k, v in six.iteritems(subtrahend):
165        result[k] -= v
166
167    # Remove zero counted buckets.
168    return {k: v for k, v in six.iteritems(result) if v}
169
170
171def expect_sole_bucket(histogram_differ, bucket, bucket_name, timeout=10,
172                       sleep_interval=1):
173    """
174     Returns true if the given bucket solely exists in histogram differ.
175
176     @param histogram_differ: a HistogramDiffer instance used to get histogram
177            name and histogram diff multiple times.
178     @param bucket: bucket value.
179     @param bucket_name: bucket name to be shown on error message.
180     @param timeout: timeout in seconds.
181     @param sleep_interval: interval in seconds between getting diff.
182     @returns True if the given bucket solely exists in histogram.
183     @raises TestError if bucket doesn't exist or other buckets exist.
184     """
185    timer = utils.Timer(timeout)
186    histogram = {}
187    histogram_name = histogram_differ.histogram_name
188    while timer.sleep(sleep_interval):
189        histogram = histogram_differ.end()
190        if histogram:
191            break
192
193    if bucket not in histogram:
194        raise error.TestError('Expect %s has %s. Histogram: %r' %
195                              (histogram_name, bucket_name, histogram))
196    if len(histogram) > 1:
197        raise error.TestError('%s has bucket other than %s. Histogram: %r' %
198                              (histogram_name, bucket_name, histogram))
199    return True
200
201
202def poll_histogram_grow(histogram_differ, timeout=2, sleep_interval=0.1):
203    """
204     Polls histogram to see if it grows within |timeout| seconds.
205
206     @param histogram_differ: a HistogramDiffer instance used to get histogram
207            name and histogram diff multiple times.
208     @param timeout: observation timeout in seconds.
209     @param sleep_interval: interval in seconds between getting diff.
210     @returns (True, histogram_diff) if the histogram grows.
211              (False, {}) if it does not grow in |timeout| seconds.
212     """
213    timer = utils.Timer(timeout)
214    while timer.sleep(sleep_interval):
215        histogram_diff = histogram_differ.end()
216        if histogram_diff:
217            return (True, histogram_diff)
218    return (False, {})
219
220
221class HistogramDiffer(object):
222    """
223     Calculates a histogram's progress between begin() and end().
224
225     Usage:
226       differ = HistogramDiffer(cr, 'Media.GpuVideoDecoderError')
227       ....
228       diff_gvd_error = differ.end()
229     """
230
231    def __init__(self, cr, histogram_name, begin=True):
232        """
233          Constructor.
234
235          @param: cr: object, the Chrome instance
236          @param: histogram_name: string, name of the histogram
237          @param: begin: if set, calls begin().
238          """
239        self.cr = cr
240        self.histogram_name = histogram_name
241        self.begin_histogram_text = ''
242        self.end_histogram_text = ''
243        self.begin_histogram = {}
244        self.end_histogram = {}
245        if begin:
246            self.begin()
247
248    def _get_histogram(self):
249        """
250          Gets current histogram bucket.
251
252          @returns (dict(bucket_value, bucket_count), histogram_text)
253          """
254        tab = self.cr.browser.tabs.New()
255        text = get_histogram_text(tab, self.histogram_name)
256        tab.Close()
257        return (parse_histogram(text), text)
258
259    def begin(self):
260        """
261          Takes a histogram snapshot as begin_histogram.
262          """
263        (self.begin_histogram,
264         self.begin_histogram_text) = self._get_histogram()
265        logging.debug('begin histograms/%s: %r\nraw_text: %s',
266                      self.histogram_name, self.begin_histogram,
267                      self.begin_histogram_text)
268
269    def end(self):
270        """
271          Takes a histogram snapshot as end_histogram.
272
273          @returns self.diff()
274          """
275        self.end_histogram, self.end_histogram_text = self._get_histogram()
276        logging.debug('end histograms/%s: %r\nraw_text: %s',
277                      self.histogram_name, self.end_histogram,
278                      self.end_histogram_text)
279        diff = subtract_histogram(self.end_histogram, self.begin_histogram)
280        logging.debug('histogram diff: %r', diff)
281        return diff
282