def Open(self): """Overridden connection creation.""" # Kill all existing Chrome instances. killed_count = LocalChromeController.KillChromeProcesses() if killed_count > 0: logging.warning('Killed existing Chrome instance.') chrome_cmd = [OPTIONS.LocalBinary('chrome')] chrome_cmd.extend(self._GetChromeArguments()) # Force use of simple cache. chrome_cmd.append('--use-simple-cache-backend=on') chrome_cmd.append('--user-data-dir=%s' % self._profile_dir) # Navigates to about:blank for couples of reasons: # - To find the correct target descriptor at devtool connection; # - To avoid cache and WPR pollution by the NTP. chrome_cmd.append('about:blank') tmp_log = \ tempfile.NamedTemporaryFile(prefix="chrome_controller_", suffix='.log') chrome_process = None try: chrome_env_override = self._chrome_env_override.copy() if self._wpr_attributes: chrome_env_override.update( self._wpr_attributes.chrome_env_override) chrome_env = os.environ.copy() chrome_env.update(chrome_env_override) # Launch Chrome. logging.info( common_util.GetCommandLineForLogging(chrome_cmd, chrome_env_override)) chrome_process = subprocess.Popen(chrome_cmd, stdout=tmp_log.file, stderr=tmp_log.file, env=chrome_env) # Attempt to connect to Chrome's devtools for attempt_id in xrange(self.DEVTOOLS_CONNECTION_ATTEMPTS): logging.info('Devtools connection attempt %d' % attempt_id) process_result = chrome_process.poll() if process_result is not None: raise ChromeControllerInternalError( 'Unexpected Chrome exit: {}'.format(process_result)) try: connection = devtools_monitor.DevToolsConnection( OPTIONS.devtools_hostname, OPTIONS.devtools_port) break except socket.error as e: if e.errno != errno.ECONNREFUSED: raise time.sleep( self.DEVTOOLS_CONNECTION_ATTEMPT_INTERVAL_SECONDS) else: raise ChromeControllerInternalError( 'Failed to connect to Chrome devtools after {} ' 'attempts.'.format(self.DEVTOOLS_CONNECTION_ATTEMPTS)) # Start and yield the devtool connection. self._StartConnection(connection) yield connection if self._slow_death: connection.Close() chrome_process.wait() chrome_process = None except ChromeControllerError._PASSTHROUGH_WHITE_LIST: raise except Exception: raise ChromeControllerError(log=open(tmp_log.name).read()) finally: if OPTIONS.local_noisy: sys.stderr.write(open(tmp_log.name).read()) del tmp_log if chrome_process: try: chrome_process.kill() except OSError: pass # Chrome is already dead.
def ExecuteWithCommandLine(args, default_final_tasks): """Helper to execute tasks using command line arguments. Args: args: Command line argument parsed with CommandLineParser(). default_final_tasks: Default final tasks if there is no -r command line arguments. Returns: 0 if success or 1 otherwise """ # Builds the scenario. final_tasks, frozen_tasks = _SelectTasksFromCommandLineRegexes( args, default_final_tasks) scenario = GenerateScenario(final_tasks, frozen_tasks) if len(scenario) == 0: logging.error('No tasks to build.') return 1 if not os.path.isdir(args.output): os.makedirs(args.output) # Print the task dependency graph visualization. if args.output_graphviz: graphviz_path = os.path.join(args.output, _TASK_GRAPH_DOTFILE_NAME) png_graph_path = os.path.join(args.output, _TASK_GRAPH_PNG_NAME) with open(graphviz_path, 'w') as output: OutputGraphViz(scenario, final_tasks, output) subprocess.check_call( ['dot', '-Tpng', graphviz_path, '-o', png_graph_path]) # Use the build scenario. if args.dry_run: for task in scenario: print task.name return 0 # Run the Scenario while saving intermediate state to be able to resume later. failed_tasks = [] tasks_to_skip = set() dependents_per_task = GenerateDependentSetPerTask(scenario) def MarkTaskNotToExecute(task): if task not in tasks_to_skip: logging.warning('can not execute task: %s', task.name) tasks_to_skip.add(task) for dependent in dependents_per_task[task]: MarkTaskNotToExecute(dependent) log_filename = datetime.datetime.now().strftime( _TASK_EXECUTION_LOG_NAME_FORMAT) log_path = os.path.join(args.output, _TASK_LOGS_DIR_NAME, log_filename) if not os.path.isdir(os.path.dirname(log_path)): os.makedirs(os.path.dirname(log_path)) formatter = logging.Formatter('[%(asctime)s] %(levelname)s: %(message)s') handler = logging.FileHandler(log_path, mode='a') handler.setFormatter(formatter) logging.getLogger().addHandler(handler) logging.info('%s %s', '-' * 60, common_util.GetCommandLineForLogging(sys.argv)) try: with _ResumingFileBuilder(args) as resume_file_builder: for task_execute_id, task in enumerate(scenario): if task in tasks_to_skip: continue logging.info('%s %s', '-' * 60, task.name) try: task.Execute() except (MemoryError, SyntaxError): raise except BaseException: # The resuming file being incrementally generated by # resume_file_builder.OnTaskSuccess() is automatically fsynced(). # But resume_file_builder.OnScenarioFinish() completely rewrite # this file with the mininal subset of task to freeze, and in case # of an ENOSPC, we don't want to touch the resuming file at all so # that it remains uncorrupted. if (sys.exc_info()[0] == IOError and sys.exc_info()[1].errno == errno.ENOSPC): raise logging.exception('%s %s failed', '-' * 60, task.name) failed_tasks.append(task) if args.keep_going and sys.exc_info( )[0] != KeyboardInterrupt: MarkTaskNotToExecute(task) else: tasks_to_skip.update(set(scenario[task_execute_id:])) break else: resume_file_builder.OnTaskSuccess(task) if tasks_to_skip: assert failed_tasks resume_file_builder.OnScenarioFinish(scenario, final_tasks, failed_tasks, tasks_to_skip) if sys.exc_info()[0] == KeyboardInterrupt: raise return 1 finally: logging.getLogger().removeHandler(handler) assert not failed_tasks return 0