def run(self): # Register our exiter at the beginning of the run() method so that any code in this process from # this point onwards will use that exiter in the case of a fatal error. ExceptionSink.reset_exiter(self._exiter) options_bootstrapper = OptionsBootstrapper(env=self._env, args=self._args) bootstrap_options = options_bootstrapper.get_bootstrap_options() global_bootstrap_options = bootstrap_options.for_global_scope() ExceptionSink.reset_should_print_backtrace_to_terminal( global_bootstrap_options.print_exception_stacktrace) ExceptionSink.reset_log_location( global_bootstrap_options.pants_workdir) if global_bootstrap_options.enable_pantsd: try: return RemotePantsRunner(self._exiter, self._args, self._env, bootstrap_options).run() except RemotePantsRunner.Fallback as e: logger.warn( 'caught client exception: {!r}, falling back to non-daemon mode' .format(e)) # N.B. Inlining this import speeds up the python thin client run by about 100ms. from pants.bin.local_pants_runner import LocalPantsRunner runner = LocalPantsRunner.create( self._exiter, self._args, self._env, options_bootstrapper=options_bootstrapper) runner.set_start_time(self._start_time) return runner.run()
def run_sync(self): """Synchronously run pantsd.""" # Switch log output to the daemon's log stream from here forward. self._close_stdio() with self._pantsd_logging() as log_stream: # Register an exiter using os._exit to ensure we only close stdio streams once. ExceptionSink.reset_exiter(Exiter(exiter=os._exit)) # We don't have any stdio streams to log to anymore, but we can get tracebacks of the pantsd # process by tailing the pantsd log and sending it SIGUSR2. ExceptionSink.reset_interactive_output_stream(log_stream) # Reset the log location and the backtrace preference from the global bootstrap options. global_bootstrap_options = self._bootstrap_options.for_global_scope() ExceptionSink.reset_should_print_backtrace_to_terminal( global_bootstrap_options.print_exception_stacktrace) ExceptionSink.reset_log_location(global_bootstrap_options.pants_workdir) self._logger.info('pantsd starting, log level is {}'.format(self._log_level)) self._native.set_panic_handler() # Set the process name in ps output to 'pantsd' vs './pants compile src/etc:: -ldebug'. set_process_title('pantsd [{}]'.format(self._build_root)) # Write service socket information to .pids. self._write_named_sockets(self._services.port_map) # Enter the main service runner loop. self._setup_services(self._services) self._run_services(self._services)
def post_fork_child(self): """Post-fork child process callback executed via ProcessManager.daemonize().""" # Set the Exiter exception hook post-fork so as not to affect the pantsd processes exception # hook with socket-specific behavior. Note that this intentionally points the faulthandler # trace stream to sys.stderr, which at this point is still a _LoggerStream object writing to # the `pantsd.log`. This ensures that in the event of e.g. a hung but detached pantsd-runner # process that the stacktrace output lands deterministically in a known place vs to a stray # terminal window. # TODO: test the above! ExceptionSink.reset_exiter(self._exiter) ExceptionSink.reset_interactive_output_stream( sys.stderr.buffer if PY3 else sys.stderr) # Ensure anything referencing sys.argv inherits the Pailgun'd args. sys.argv = self._args # Set context in the process title. set_process_title('pantsd-runner [{}]'.format(' '.join(self._args))) # Broadcast our process group ID (in PID form - i.e. negated) to the remote client so # they can send signals (e.g. SIGINT) to all processes in the runners process group. NailgunProtocol.send_pid(self._socket, os.getpid()) NailgunProtocol.send_pgrp(self._socket, os.getpgrp() * -1) # Stop the services that were paused pre-fork. for service in self._services.services: service.terminate() # Invoke a Pants run with stdio redirected and a proxied environment. with self.nailgunned_stdio(self._socket, self._env) as finalizer,\ hermetic_environment_as(**self._env): try: # Setup the Exiter's finalizer. self._exiter.set_finalizer(finalizer) # Clean global state. clean_global_runtime_state(reset_subsystem=True) # Re-raise any deferred exceptions, if present. self._raise_deferred_exc() # Otherwise, conduct a normal run. runner = LocalPantsRunner.create(self._exiter, self._args, self._env, self._target_roots, self._graph_helper, self._options_bootstrapper) runner.set_start_time( self._maybe_get_client_start_time_from_env(self._env)) runner.run() except KeyboardInterrupt: self._exiter.exit_and_fail('Interrupted by user.\n') except Exception: ExceptionSink._log_unhandled_exception_and_exit() else: self._exiter.exit(0)
def main(): start_time = time.time() exiter = Exiter() ExceptionSink.reset_exiter(exiter) with maybe_profiled(os.environ.get('PANTSC_PROFILE')): try: PantsRunner(exiter, start_time=start_time).run() except KeyboardInterrupt as e: exiter.exit_and_fail('Interrupted by user:\n{}'.format(e))
def execute(self): exit_msg = self._lifecycle_stubs.add_exiter_message if exit_msg: new_exiter = MessagingExiter(exit_msg) ExceptionSink.reset_exiter(new_exiter) output_file = self._lifecycle_stubs.new_interactive_stream_output_file if output_file: file_stream = open(output_file, 'wb') ExceptionSink.reset_interactive_output_stream(file_stream) raise Exception('erroneous!')
def set_start_time(self, start_time): # Launch RunTracker as early as possible (before .run() is called). self._run_tracker = RunTracker.global_instance() self._reporting = Reporting.global_instance() self._run_start_time = start_time self._reporting.initialize(self._run_tracker, self._options, start_time=self._run_start_time) # Capture a repro of the 'before' state for this build, if needed. self._repro = Reproducer.global_instance().create_repro() if self._repro: self._repro.capture(self._run_tracker.run_info.get_as_dict()) # The __call__ method of the Exiter allows for the prototype pattern. self._exiter = LocalExiter(self._run_tracker, self._repro, exiter=self._exiter) ExceptionSink.reset_exiter(self._exiter)
def run(self): # Register our exiter at the beginning of the run() method so that any code in this process from # this point onwards will use that exiter in the case of a fatal error. ExceptionSink.reset_exiter(self._exiter) options_bootstrapper = OptionsBootstrapper.create(env=self._env, args=self._args) bootstrap_options = options_bootstrapper.bootstrap_options global_bootstrap_options = bootstrap_options.for_global_scope() # We enable Rust logging here, # and everything before it will be routed through regular Python logging. self._enable_rust_logging(global_bootstrap_options) ExceptionSink.reset_should_print_backtrace_to_terminal( global_bootstrap_options.print_exception_stacktrace) ExceptionSink.reset_log_location( global_bootstrap_options.pants_workdir) for message_regexp in global_bootstrap_options.ignore_pants_warnings: warnings.filterwarnings(action='ignore', message=message_regexp) # TODO https://github.com/pantsbuild/pants/issues/7205 if self._should_run_with_pantsd(global_bootstrap_options): try: return RemotePantsRunner(self._exiter, self._args, self._env, options_bootstrapper).run() except RemotePantsRunner.Fallback as e: logger.warn( 'caught client exception: {!r}, falling back to non-daemon mode' .format(e)) # N.B. Inlining this import speeds up the python thin client run by about 100ms. from pants.bin.local_pants_runner import LocalPantsRunner if self.will_terminate_pantsd(): logger.debug("Pantsd terminating goal detected: {}".format( self._args)) runner = LocalPantsRunner.create( self._exiter, self._args, self._env, options_bootstrapper=options_bootstrapper) runner.set_start_time(self._start_time) return runner.run()
def run_sync(self): """Synchronously run pantsd.""" # Switch log output to the daemon's log stream from here forward. self._close_stdio() with self._pantsd_logging() as (log_stream, log_filename): # Register an exiter using os._exit to ensure we only close stdio streams once. ExceptionSink.reset_exiter(Exiter(exiter=os._exit)) # We don't have any stdio streams to log to anymore, so we log to a file. # We don't override the faulthandler destination because the stream we get will proxy things # via the rust logging code, and faulthandler needs to be writing directly to a real file # descriptor. When pantsd logging was originally initialised, we already set up faulthandler # to log to the correct file descriptor, so don't override it. # # We can get tracebacks of the pantsd process by tailing the pantsd log and sending it # SIGUSR2. ExceptionSink.reset_interactive_output_stream( log_stream, override_faulthandler_destination=False, ) # Reset the log location and the backtrace preference from the global bootstrap options. global_bootstrap_options = self._bootstrap_options.for_global_scope( ) ExceptionSink.reset_should_print_backtrace_to_terminal( global_bootstrap_options.print_exception_stacktrace) ExceptionSink.reset_log_location( global_bootstrap_options.pants_workdir) self._native.set_panic_handler() # Set the process name in ps output to 'pantsd' vs './pants compile src/etc:: -ldebug'. set_process_title('pantsd [{}]'.format(self._build_root)) # Write service socket information to .pids. self._write_named_sockets(self._services.port_map) # Enter the main service runner loop. self._setup_services(self._services) self._run_services(self._services)