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