Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(315)

Side by Side Diff: tools/perf/profile_creators/fast_navigation_profile_extender.py

Issue 907503002: telemetry: Create a helper class to quickly extend profiles. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tools/perf/profile_creators/fast_navigation_profile_extender_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2015 The Chromium 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 import time
5
6 from telemetry.core import browser_finder
7 from telemetry.core import browser_finder_exceptions
8 from telemetry.core import exceptions
9 from telemetry.core import util
10
11
12 class FastNavigationProfileExtender(object):
13 """
14 This class creates or extends an existing profile by performing a set of tab
15 navigations in large batches. This is accomplished by opening a large number
16 of tabs, simultaneously navigating all the tabs, and then waiting for all the
17 tabs to load. This provides two benefits:
18 - Takes advantage of the high number of logical cores on modern CPUs.
19 - The total time spent waiting for navigations to time out scales linearly
20 with the number of batches, but does not scale with the size of the
21 batch.
22 """
23 def __init__(self):
24 super(FastNavigationProfileExtender, self).__init__()
25
26 # A reference to the browser that will be performing all of the tab
27 # navigations.
28 self._browser = None
29
30 # A static copy of the urls that this class is going to navigate to.
31 self._navigation_urls = None
32
33 # The number of tabs to use.
34 self._NUM_TABS = 15
35
36 # The number of pages to load in parallel.
37 self._NUM_PARALLEL_PAGES = 15
38
39 # It doesn't make sense for the batch size to be larger than the number of
40 # available tabs.
41 assert(self._NUM_PARALLEL_PAGES <= self._NUM_TABS)
42
43 # The amount of time to wait for pages to finish loading.
44 self._PAGE_LOAD_TIMEOUT_IN_SECONDS = 10
45
46 # The amount of time to wait for the retrieval of the URL of a tab.
47 self._TAB_URL_RETRIEVAL_TIMEOUT_IN_SECONDS = 1
48
49 # The amount of time to wait for a navigation to be committed.
50 self._NAVIGATION_COMMIT_WAIT_IN_SECONDS = 0.1
51
52 # A list of tuples (tab, initial_url). A navigation command has been sent
53 # to |tab|. |tab| had a URL of |initial_url| before the command was sent.
54 self._queued_tabs = []
nednguyen 2015/02/10 18:48:43 I don't think you need this either.
erikchen 2015/02/10 21:30:11 Done.
55
56 def Run(self, finder_options):
57 """
58 |finder_options| contains the directory of the input profile, the directory
59 to place the output profile, and sufficient information to choose a specific
60 browser binary.
61 """
62 try:
63 self._navigation_urls = self.GetUrlsToNavigate()
64 self._SetUpBrowser(finder_options)
65 self._PerformNavigations()
66 finally:
67 self._TearDownBrowser()
68
69 def GetUrlsToNavigate(self):
70 """
71 Intended for subclass override. Returns a list of urls to be navigated to.
72 """
73 raise NotImplementedError()
74
75
76 def _GetPossibleBrowser(self, finder_options):
77 """Return a possible_browser with the given options."""
78 possible_browser = browser_finder.FindBrowser(finder_options)
79 if not possible_browser:
80 raise browser_finder_exceptions.BrowserFinderException(
81 'No browser found.\n\nAvailable browsers:\n%s\n' %
82 '\n'.join(browser_finder.GetAllAvailableBrowserTypes(finder_options)))
83 finder_options.browser_options.browser_type = (
84 possible_browser.browser_type)
85
86 return possible_browser
87
88 def _RetrieveTabUrl(self, tab):
89 """Retrives the URL of the tab."""
90 try:
91 return tab.EvaluateJavaScript('document.URL',
92 self._TAB_URL_RETRIEVAL_TIMEOUT_IN_SECONDS)
93 except exceptions.DevtoolsTargetCrashException:
94 return None
95
96 def _BatchNavigateTabs(self, batch):
97 """
98 Performs a batch of tab navigations with minimal delay.
99
100 |batch| is a list of tuples (tab, url).
101 """
102 timeout_in_seconds = 0
103
104 for tab, url in batch:
105 initial_url = self._RetrieveTabUrl(tab)
nednguyen 2015/02/10 18:48:43 Not sure that you need to do this kind of checking
erikchen 2015/02/10 21:30:11 This is necessary since I navigate the tab with 0
nednguyen 2015/02/12 02:10:53 If the behavior of timeout_in_seconds=0 is not wel
106
107 try:
108 tab.Navigate(url, None, timeout_in_seconds)
109 except exceptions.DevtoolsTargetCrashException:
110 # We expect a time out, and don't mind if the webpage crashes. Ignore
111 # both exceptions.
112 pass
113
114 self._queued_tabs.append((tab, initial_url))
115
116 def _WaitForQueuedTabsToLoad(self):
117 """Waits for all the batch navigated tabs to finish loading."""
118 end_time = time.time() + self._PAGE_LOAD_TIMEOUT_IN_SECONDS
119 for tab, initial_url in self._queued_tabs:
120 seconds_to_wait = end_time - time.time()
121 seconds_to_wait = max(0, seconds_to_wait)
122
123 if seconds_to_wait == 0:
124 break
125
126 # Since we don't wait any time for the tab url navigation to commit, it's
127 # possible that the tab hasn't started navigating yet.
128 current_url = self._RetrieveTabUrl(tab)
129
130 if current_url == initial_url:
131 # If the navigation hasn't been committed yet, wait a small amount of
132 # time. Don't bother rechecking the condition, since it's also possible
133 # that the web page isn't processing javascript.
134 time.sleep(self._NAVIGATION_COMMIT_WAIT_IN_SECONDS)
135
136 try:
137 tab.WaitForDocumentReadyStateToBeComplete(seconds_to_wait)
138 except (util.TimeoutException, exceptions.DevtoolsTargetCrashException):
139 # Ignore time outs and web page crashes.
140 pass
141 self._queued_tabs = []
142
143 def _SetUpBrowser(self, finder_options):
144 """
145 Finds the browser, starts the browser, and opens the requisite number of
146 tabs.
147 """
148 possible_browser = self._GetPossibleBrowser(finder_options)
149 self._browser = possible_browser.Create(finder_options)
150
151 for _ in range(self._NUM_TABS):
152 self._browser.tabs.New()
153
154 def _PerformNavigations(self):
155 """
156 Performs the navigations specified by |_navigation_urls| in large batches.
157 """
158 # The index of the first url that has not yet been navigated to.
159 navigation_url_index = 0
160 while True:
161 # Generate the next batch of navigations.
162 batch = []
163 max_index = min(navigation_url_index + self._NUM_PARALLEL_PAGES,
164 len(self._navigation_urls))
165 for i in range(navigation_url_index, max_index):
166 url = self._navigation_urls[i]
167 tab = self._browser.tabs[i % self._NUM_TABS]
168 batch.append((tab, url))
169 navigation_url_index = max_index
170
171 self._BatchNavigateTabs(batch)
172 self._WaitForQueuedTabsToLoad()
173
174 if navigation_url_index == len(self._navigation_urls):
175 break
176
177 def _TearDownBrowser(self):
178 """
179 Teardown that is guaranteed to be executed before the instance is destroyed.
180 """
181 if self._browser:
182 self._browser.Close()
183 self._browser = None
OLDNEW
« no previous file with comments | « no previous file | tools/perf/profile_creators/fast_navigation_profile_extender_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698