summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Cragun <me@ryan.ec>2015-05-27 18:47:27 -0700
committerRyan Cragun <me@ryan.ec>2015-05-28 12:23:39 -0700
commit28b0a7f9d2617ad783299d20fb6c936b05ff77e6 (patch)
tree34fd0d0c21a376779441599ba72b949c3de55d89
parent678d696b169d6fbcf5966124f41847bfc3282c13 (diff)
downloadchef-ryan/profiling.tar.gz
Add Ruby profiling support for Chef Applicationsryan/profiling
-rw-r--r--chef-config/lib/chef-config/config.rb30
-rw-r--r--chef.gemspec1
-rw-r--r--lib/chef/application.rb3
-rw-r--r--lib/chef/application/client.rb5
-rw-r--r--lib/chef/ruby_profiler.rb47
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