diff options
author | Ryan Cragun <me@ryan.ec> | 2015-05-27 18:47:27 -0700 |
---|---|---|
committer | Ryan Cragun <me@ryan.ec> | 2015-05-28 12:23:39 -0700 |
commit | 28b0a7f9d2617ad783299d20fb6c936b05ff77e6 (patch) | |
tree | 34fd0d0c21a376779441599ba72b949c3de55d89 | |
parent | 678d696b169d6fbcf5966124f41847bfc3282c13 (diff) | |
download | chef-ryan/profiling.tar.gz |
Add Ruby profiling support for Chef Applicationsryan/profiling
-rw-r--r-- | chef-config/lib/chef-config/config.rb | 30 | ||||
-rw-r--r-- | chef.gemspec | 1 | ||||
-rw-r--r-- | lib/chef/application.rb | 3 | ||||
-rw-r--r-- | lib/chef/application/client.rb | 5 | ||||
-rw-r--r-- | lib/chef/ruby_profiler.rb | 47 |
5 files changed, 85 insertions, 1 deletions
diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb index 63de8a451f..acf70dab1a 100644 --- a/chef-config/lib/chef-config/config.rb +++ b/chef-config/lib/chef-config/config.rb @@ -720,6 +720,36 @@ module ChefConfig # break Chef community cookbooks and is very highly discouraged. default :ruby_encoding, Encoding::UTF_8 + # Whether or not to enable Ruby profiling + default :ruby_profile, false + + # Which part of the Ruby VM to profile + # https://github.com/ruby-prof/ruby-prof/blob/master/README.rdoc#measurements + # + # We support using the standard RUBY_PROF_MEASURE_MODE environment variables: + # wall, process, cpu, allocations, memory, gc_time, gc_runs + # + # We also support configuring this in the client.rb/knife.rb: + # wall_time, process_time, cpu_time, allocations, memory, gc_time, gc_runs + default(:ruby_profile_measure_mode) do + mode = env['RUBY_PROF_MEASURE_MODE'] || 'memory' + case mode + when 'wall', 'process', 'cpu' + mode = "#{mode}_time" + end + mode + end + + # Path to a file to print profiling results + default :ruby_profile_outfile, nil + + # Which profile printer to use + # https://github.com/ruby-prof/ruby-prof/blob/master/README.rdoc#printers + # Available printers: + # flat_printer, flat_printer_with_line_numbers, graph_printer, graph_html_printer, + # dot_printer, call_tree_printer, call_stack_printer + default :ruby_profile_printer, 'flat_printer' + # If installed via an omnibus installer, this gives the path to the # "embedded" directory which contains all of the software packaged with # omnibus. This is used to locate the cacert.pem file on windows. diff --git a/chef.gemspec b/chef.gemspec index f4f8a31207..7793c96193 100644 --- a/chef.gemspec +++ b/chef.gemspec @@ -33,6 +33,7 @@ Gem::Specification.new do |s| s.add_dependency "chef-zero", "~> 4.2", ">= 4.2.2" s.add_dependency "pry", "~> 0.9" + s.add_dependency "ruby-prof", "~> 0.15" s.add_dependency 'plist', '~> 3.1.0' diff --git a/lib/chef/application.rb b/lib/chef/application.rb index 297e46ef3c..69cb4678f4 100644 --- a/lib/chef/application.rb +++ b/lib/chef/application.rb @@ -28,6 +28,7 @@ require 'chef/platform' require 'mixlib/cli' require 'tmpdir' require 'rbconfig' +require 'chef/ruby_profiler' class Chef class Application @@ -57,7 +58,7 @@ class Chef setup_signal_handlers reconfigure setup_application - run_application + Chef::RubyProfiler.profile(config) { run_application } end def setup_signal_handlers diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb index 551e26e303..50323e981b 100644 --- a/lib/chef/application/client.rb +++ b/lib/chef/application/client.rb @@ -263,6 +263,11 @@ class Chef::Application::Client < Chef::Application :description => "Whether a local mode (-z) server binds to a port", :boolean => true + option :ruby_profile, + :long => "--ruby-profile", + :description => "Whether or not to enable Ruby profiling during the client application run", + :boolean => false + IMMEDIATE_RUN_SIGNAL = "1".freeze attr_reader :chef_client_json diff --git a/lib/chef/ruby_profiler.rb b/lib/chef/ruby_profiler.rb new file mode 100644 index 0000000000..b1554fa0a3 --- /dev/null +++ b/lib/chef/ruby_profiler.rb @@ -0,0 +1,47 @@ +# +# Author:: Ryan Cragun (<ryan@chef.io>) +# Copyright:: Copyright (c) 2015 Chef, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'ruby-prof' + +class Chef + module RubyProfiler + def self.profile(config = {}, &block) + if config[:ruby_profile] || Chef::Config[:ruby_profile] + measure_mode = config[:ruby_profile_measure_mode] || Chef::Config[:ruby_profile_measure_mode] + measure_mode = RubyProf.const_get("RubyProf::#{measure_mode.upcase}") + + profile_printer = config[:ruby_profile_printer] || Chef::Config[:ruby_profile_printer] + profile_printer = profile_printer.split('_').map { |c| c.capitalize }.join + profile_printer = RubyProf.const_get("RubyProf::#{profile_printer}") + + if file = config[:ruby_profile_outfile] || Chef::Config[:ruby_profile_outfile] + profile_out = File.open(file, 'w+') + end + profile_out ||= $stdout + + RubyProf.measure_mode = measure_mode + + res = RubyProf.profile { block.call } + printer = profile_printer.new(res) + printer.print(profile_out) + else + block.call + end + end + end +end |