Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 import multiprocessing | |
| 4 import time | 5 import time |
| 5 | 6 |
| 6 from telemetry.core import browser_finder | 7 from telemetry.core import browser_finder |
| 7 from telemetry.core import browser_finder_exceptions | 8 from telemetry.core import browser_finder_exceptions |
| 8 from telemetry.core import exceptions | 9 from telemetry.core import exceptions |
| 9 from telemetry.core import util | 10 from telemetry.core import util |
| 10 | 11 |
| 11 | 12 |
| 12 class FastNavigationProfileExtender(object): | 13 class FastNavigationProfileExtender(object): |
| 13 """Extends a Chrome profile. | 14 """Extends a Chrome profile. |
| 14 | 15 |
| 15 This class creates or extends an existing profile by performing a set of tab | 16 This class creates or extends an existing profile by performing a set of tab |
| 16 navigations in large batches. This is accomplished by opening a large number | 17 navigations in large batches. This is accomplished by opening a large number |
| 17 of tabs, simultaneously navigating all the tabs, and then waiting for all the | 18 of tabs, simultaneously navigating all the tabs, and then waiting for all the |
| 18 tabs to load. This provides two benefits: | 19 tabs to load. This provides two benefits: |
| 19 - Takes advantage of the high number of logical cores on modern CPUs. | 20 - Takes advantage of the high number of logical cores on modern CPUs. |
| 20 - The total time spent waiting for navigations to time out scales linearly | 21 - The total time spent waiting for navigations to time out scales linearly |
| 21 with the number of batches, but does not scale with the size of the | 22 with the number of batches, but does not scale with the size of the |
| 22 batch. | 23 batch. |
| 23 """ | 24 """ |
| 24 def __init__(self): | 25 def __init__(self): |
| 25 super(FastNavigationProfileExtender, self).__init__() | 26 super(FastNavigationProfileExtender, self).__init__() |
| 26 | 27 |
| 28 # The path of the profile that the browser will use while its running. | |
|
sullivan
2015/02/18 21:18:05
nit: it's
erikchen
2015/02/18 22:04:01
Done.
| |
| 29 # This member is initialized during SetUp(). | |
| 30 self.profile_path = None | |
| 31 | |
| 27 # A reference to the browser that will be performing all of the tab | 32 # A reference to the browser that will be performing all of the tab |
| 28 # navigations. | 33 # navigations. |
| 34 # This member is initialized during SetUp(). | |
| 29 self._browser = None | 35 self._browser = None |
| 30 | 36 |
| 31 # A static copy of the urls that this class is going to navigate to. | |
| 32 self._navigation_urls = None | |
| 33 | |
| 34 # The number of tabs to use. | 37 # The number of tabs to use. |
| 35 self._NUM_TABS = 15 | 38 # This member is initialized during SetUp(). |
| 36 | 39 self._NUM_TABS = None |
|
nednguyen
2015/02/18 22:43:14
Why not make this a parameter in the constructor?
erikchen
2015/02/18 23:32:30
Done.
| |
| 37 # The number of pages to load in parallel. | |
| 38 self._NUM_PARALLEL_PAGES = 15 | |
| 39 | |
| 40 assert self._NUM_PARALLEL_PAGES <= self._NUM_TABS, (' the batch size can\'t' | |
| 41 ' be larger than the number of available tabs') | |
| 42 | 40 |
| 43 # The amount of time to wait for a batch of pages to finish loading. | 41 # The amount of time to wait for a batch of pages to finish loading. |
| 44 self._BATCH_PAGE_LOAD_TIMEOUT_IN_SECONDS = 10 | 42 self._BATCH_PAGE_LOAD_TIMEOUT_IN_SECONDS = 10 |
| 45 | 43 |
| 46 # The default amount of time to wait for the retrieval of the URL of a tab. | 44 # The default amount of time to wait for the retrieval of the URL of a tab. |
| 47 self._TAB_URL_RETRIEVAL_TIMEOUT_IN_SECONDS = 1 | 45 self._TAB_URL_RETRIEVAL_TIMEOUT_IN_SECONDS = 1 |
| 48 | 46 |
| 49 def Run(self, finder_options): | 47 def Run(self, finder_options): |
| 50 """Extends the profile. | 48 """Extends the profile. |
| 51 | 49 |
| 52 Args: | 50 Args: |
| 53 finder_options: An instance of BrowserFinderOptions that contains the | 51 finder_options: An instance of BrowserFinderOptions that contains the |
| 54 directory of the input profile, the directory to place the output | 52 directory of the input profile, the directory to place the output |
| 55 profile, and sufficient information to choose a specific browser binary. | 53 profile, and sufficient information to choose a specific browser binary. |
| 56 """ | 54 """ |
| 57 try: | 55 try: |
| 58 self._navigation_urls = self.GetUrlsToNavigate() | 56 self.SetUp(finder_options) |
| 59 self._SetUpBrowser(finder_options) | |
| 60 self._PerformNavigations() | 57 self._PerformNavigations() |
| 61 finally: | 58 finally: |
| 62 self._TearDownBrowser() | 59 self.TearDown() |
| 63 | 60 |
| 64 def GetUrlsToNavigate(self): | 61 def GetUrlsToNavigate(self, maximum_batch_size): |
| 65 """Returns a list of urls to be navigated to. | 62 """Gets URLs for the browser to navigate to. |
| 63 | |
| 64 Intended for subclass override. | |
| 65 | |
| 66 Args: | |
| 67 maximum_batch_size: A positive integer indicating the maximum allowed | |
| 68 length of the returned list. | |
| 69 Returns: | |
| 70 A list of urls to be navigated to. | |
| 71 """ | |
| 72 raise NotImplementedError() | |
| 73 | |
| 74 def ShouldExit(self): | |
| 75 """Returns a boolean indicating whether profile extension is finished. | |
| 66 | 76 |
| 67 Intended for subclass override. | 77 Intended for subclass override. |
| 68 """ | 78 """ |
| 69 raise NotImplementedError() | 79 raise NotImplementedError() |
| 70 | 80 |
| 81 def NumTabs(self): | |
| 82 """Returns the number of tabs to simultaneously load pages in. | |
| 83 | |
| 84 Can be overridden by subclasses. | |
| 85 """ | |
| 86 return multiprocessing.cpu_count() | |
|
nednguyen
2015/02/18 22:43:14
Why do you think hat number of tabs should equal n
erikchen
2015/02/18 23:32:30
I moved this logic into the specific subclasses, a
| |
| 87 | |
| 88 def SetUp(self, finder_options): | |
| 89 """Finds the browser, starts the browser, and opens the requisite number of | |
| 90 tabs. | |
| 91 | |
| 92 Can be overridden by subclasses. Subclasses must call the super class | |
| 93 implementation. | |
| 94 """ | |
| 95 self.profile_path = finder_options.output_profile_path | |
| 96 self._NUM_TABS = self.NumTabs() | |
| 97 possible_browser = self._GetPossibleBrowser(finder_options) | |
| 98 self._browser = possible_browser.Create(finder_options) | |
|
sullivan
2015/02/18 21:18:05
I think there are some browser types this won't wo
erikchen
2015/02/18 22:04:01
I added asserts for "supports_tab_control" and os
| |
| 99 | |
| 100 while(len(self._browser.tabs) < self._NUM_TABS): | |
| 101 self._browser.tabs.New() | |
| 102 | |
| 103 def TearDown(self): | |
| 104 """Teardown that is guaranteed to be executed before the instance is | |
| 105 destroyed. | |
| 106 | |
| 107 Can be overridden by subclasses. Subclasses must call the super class | |
| 108 implementation. | |
| 109 """ | |
| 110 if self._browser: | |
| 111 self._browser.Close() | |
| 112 self._browser = None | |
| 113 | |
| 114 def FinishedNavigationOfBatch(self): | |
|
nednguyen
2015/02/18 22:43:14
CleanUpAfterBatchNavigation
erikchen
2015/02/18 23:32:30
Done.
| |
| 115 """A hook for subclasses to perform cleanup after each batch of | |
| 116 navigations. | |
| 117 | |
| 118 Can be overridden by subclasses. | |
| 119 """ | |
| 120 pass | |
| 71 | 121 |
| 72 def _GetPossibleBrowser(self, finder_options): | 122 def _GetPossibleBrowser(self, finder_options): |
| 73 """Return a possible_browser with the given options.""" | 123 """Return a possible_browser with the given options.""" |
| 74 possible_browser = browser_finder.FindBrowser(finder_options) | 124 possible_browser = browser_finder.FindBrowser(finder_options) |
| 75 if not possible_browser: | 125 if not possible_browser: |
| 76 raise browser_finder_exceptions.BrowserFinderException( | 126 raise browser_finder_exceptions.BrowserFinderException( |
| 77 'No browser found.\n\nAvailable browsers:\n%s\n' % | 127 'No browser found.\n\nAvailable browsers:\n%s\n' % |
| 78 '\n'.join(browser_finder.GetAllAvailableBrowserTypes(finder_options))) | 128 '\n'.join(browser_finder.GetAllAvailableBrowserTypes(finder_options))) |
| 79 finder_options.browser_options.browser_type = ( | 129 finder_options.browser_options.browser_type = ( |
| 80 possible_browser.browser_type) | 130 possible_browser.browser_type) |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 155 | 205 |
| 156 seconds_to_wait = end_time - time.time() | 206 seconds_to_wait = end_time - time.time() |
| 157 seconds_to_wait = max(0, seconds_to_wait) | 207 seconds_to_wait = max(0, seconds_to_wait) |
| 158 | 208 |
| 159 try: | 209 try: |
| 160 tab.WaitForDocumentReadyStateToBeComplete(seconds_to_wait) | 210 tab.WaitForDocumentReadyStateToBeComplete(seconds_to_wait) |
| 161 except (util.TimeoutException, exceptions.DevtoolsTargetCrashException): | 211 except (util.TimeoutException, exceptions.DevtoolsTargetCrashException): |
| 162 # Ignore time outs and web page crashes. | 212 # Ignore time outs and web page crashes. |
| 163 pass | 213 pass |
| 164 | 214 |
| 165 def _SetUpBrowser(self, finder_options): | |
| 166 """Finds the browser, starts the browser, and opens the requisite number of | |
| 167 tabs.""" | |
| 168 possible_browser = self._GetPossibleBrowser(finder_options) | |
| 169 self._browser = possible_browser.Create(finder_options) | |
| 170 | |
| 171 for _ in range(self._NUM_TABS): | |
| 172 self._browser.tabs.New() | |
| 173 | |
| 174 def _PerformNavigations(self): | 215 def _PerformNavigations(self): |
| 175 """Performs the navigations specified by |_navigation_urls| in large | 216 """Repeatedly fetches a batch of urls, and navigates to those urls. This |
| 176 batches.""" | 217 will run until an empty batch is returned, or ShouldExit() returns True. |
| 177 # The index of the first url that has not yet been navigated to. | 218 """ |
| 178 navigation_url_index = 0 | |
| 179 while True: | 219 while True: |
| 180 # Generate the next batch of navigations. | 220 # Generate the next batch of navigations. |
| 221 urls = self.GetUrlsToNavigate(self._NUM_TABS) | |
| 222 if len(urls) == 0: | |
| 223 break | |
| 224 | |
| 181 batch = [] | 225 batch = [] |
| 182 max_index = min(navigation_url_index + self._NUM_PARALLEL_PAGES, | 226 for i in range(len(urls)): |
| 183 len(self._navigation_urls)) | 227 url = urls[i] |
| 184 for i in range(navigation_url_index, max_index): | 228 tab = self._browser.tabs[i] |
| 185 url = self._navigation_urls[i] | |
| 186 tab = self._browser.tabs[i % self._NUM_TABS] | |
| 187 batch.append((tab, url)) | 229 batch.append((tab, url)) |
| 188 navigation_url_index = max_index | |
| 189 | 230 |
| 190 queued_tabs = self._BatchNavigateTabs(batch) | 231 queued_tabs = self._BatchNavigateTabs(batch) |
| 191 self._WaitForQueuedTabsToLoad(queued_tabs) | 232 self._WaitForQueuedTabsToLoad(queued_tabs) |
| 192 | 233 |
| 193 if navigation_url_index == len(self._navigation_urls): | 234 self.FinishedNavigationOfBatch() |
| 235 | |
| 236 if self.ShouldExit(): | |
|
nednguyen
2015/02/18 22:43:14
Just looking at this method name, it's unclear to
erikchen
2015/02/18 23:32:30
Done.
| |
| 194 break | 237 break |
| 195 | |
| 196 def _TearDownBrowser(self): | |
| 197 """Teardown that is guaranteed to be executed before the instance is | |
| 198 destroyed.""" | |
| 199 if self._browser: | |
| 200 self._browser.Close() | |
| 201 self._browser = None | |
| OLD | NEW |