# -*- ruby -*- #encoding: utf-8 require 'arborist/cli' unless defined?( Arborist::CLI ) # Command to start a Arborist daemon module Arborist::CLI::Start extend Arborist::CLI::Subcommand VALID_DAEMONS = %w[ manager monitors observers ] desc 'Start an Arborist daemon' long_desc <<-EOF Start the Arborist manager, observers, or monitors. The SOURCE is passed to the loader to tell it where to load things from. EOF arg :DAEMON arg :SOURCE command :start do |cmd| cmd.flag :loader, desc: "Specify a loader type to use.", default_value: 'file' cmd.desc "Run under the profiler in the given MODE (one of wall, cpu, or object; defaults to wall)." cmd.arg_name :MODE cmd.flag [:p, 'profiler'], must_match: ['wall', 'cpu', 'object'] cmd.action do |globals, options, args| appname = args.shift source = args.shift unless VALID_DAEMONS.include?( appname ) raise "Unknown daemon component. Should be one of: %s" % [ VALID_DAEMONS.join(', ') ] end loader = Arborist::Loader.create( options[:loader], source ) runner = case appname when 'manager' Arborist.manager_for( loader ) when 'monitors' Arborist.monitor_runner_for( loader ) when 'observers' Arborist.observer_runner_for( loader ) else raise "Don't know how to start %p" % [ appname ] end unless_dryrun( "starting #{appname} using #{runner.inspect}" ) do start( runner, options[:p] ) end end end ############### module_function ############### ### Start the specified +runner+ instance after setting up the environment for ### it. def start( runner, profile_mode=nil ) Process.setproctitle( runner.class.name ) if profile_mode self.with_profiling_enabled( profile_mode, runner ) do runner.run end else runner.run end end ### Wrap the profiler around the specified +callable+. def self::with_profiling_enabled( profile_arg, runner, &block ) require 'stackprof' mode, outfile = self.parse_profile_args( profile_arg, runner ) self.log.info "Profiling in %s mode, outputting to %s" % [ mode, outfile ] StackProf.run( mode: mode.to_sym, out: outfile, &block ) rescue LoadError => err self.log.debug "%p while loading the StackProf profiler: %s" exit_now!( "Couldn't load the profiler; you probably need to `gem install stackprof`", 254 ) end ### Set up the StackProf profiler to run in the given +mode+. def self::parse_profile_args( arg, runner ) profile_mode, profile_filename = arg.split( ':', 2 ) profile_filename ||= self.default_profile_filename( profile_mode, runner ) return profile_mode, profile_filename end ### Return a filename for a StackProf profile run over the given +runner+. def self::default_profile_filename( mode, runner ) basename = runner.class.name.gsub( /.*::/, '' ) return "%s-%s-%s.%d.dump" % [ basename, mode, Time.now.strftime('%Y%m%d%H%M%S'), Process.pid, ] end end # module Arborist::CLI::Start