diff options
author | Brice Figureau <brice-puppet@daysofwonder.com> | 2010-12-18 12:31:52 +0100 |
---|---|---|
committer | Brice Figureau <brice-puppet@daysofwonder.com> | 2010-12-18 15:18:57 +0100 |
commit | aed4b5fd674107d6c1e3a2e2e5c2f8c42f7a5c35 (patch) | |
tree | c67f92adfbb34de8b5ebff626256aaa26a38f4f7 /lib/puppet/util | |
parent | 2b8e834fcbc548a221b4cd02ee7200fa4f6c2c78 (diff) | |
download | puppet-aed4b5fd674107d6c1e3a2e2e5c2f8c42f7a5c35.tar.gz puppet-aed4b5fd674107d6c1e3a2e2e5c2f8c42f7a5c35.tar.xz puppet-aed4b5fd674107d6c1e3a2e2e5c2f8c42f7a5c35.zip |
Process name instrumentation infrastructure
This is special feature that changes the process name of the running puppet
entity to display its current activity.
It is disabled by default, and can be enabled by sending the QUIT signal
to the process in question (or calling enable through the code).
This system can work only if some "probes" are integrated in the core puppet
codebase. Since tools to visualize process names have a large refresh time
(ie more than 1s) it only makes sense to track long activities (like compilation,
transaction or file serving).
Those probes are the subject of a subsequent patch.
This system tracks every thread activity and form a strings which will
be used as the process name. Due to the way it is implemented it is
possible that it doesn't work on all platforms (I tested successfully
on osx and linux). On some systems the space available is dependent on
the original size of the full command. That's why if this string is longer
than a 50 characters, the string is scrolled (like stock market tickers).
Note: This is not intended to be a generic instrumentation system. Also, being
block based means that it can reduce performance if the instrumentation
probes are used in tight inner loops.
Signed-off-by: Brice Figureau <brice-puppet@daysofwonder.com>
Diffstat (limited to 'lib/puppet/util')
-rw-r--r-- | lib/puppet/util/instrumentation.rb | 12 | ||||
-rw-r--r-- | lib/puppet/util/instrumentation/process_name.rb | 129 |
2 files changed, 141 insertions, 0 deletions
diff --git a/lib/puppet/util/instrumentation.rb b/lib/puppet/util/instrumentation.rb new file mode 100644 index 000000000..5981bea59 --- /dev/null +++ b/lib/puppet/util/instrumentation.rb @@ -0,0 +1,12 @@ +require 'puppet/util/instrumentation/process_name' + +module Puppet::Util::Instrumentation + + def instrument(title) + Puppet::Util::Instrumentation::ProcessName.instrument(title) do + yield + end + end + module_function :instrument + +end
\ No newline at end of file diff --git a/lib/puppet/util/instrumentation/process_name.rb b/lib/puppet/util/instrumentation/process_name.rb new file mode 100644 index 000000000..370d29e2e --- /dev/null +++ b/lib/puppet/util/instrumentation/process_name.rb @@ -0,0 +1,129 @@ +require 'puppet' +require 'puppet/util/instrumentation' + +module Puppet::Util::Instrumentation + class ProcessName + + # start scrolling when process name is longer than + SCROLL_LENGTH = 50 + + @active = false + class << self + attr_accessor :active, :reason + end + + trap(:QUIT) do + active? ? disable : enable + end + + def self.active? + !! @active + end + + def self.enable + mutex.synchronize do + Puppet.info("Process Name instrumentation is enabled") + @active = true + @x = 0 + setproctitle + end + end + + def self.disable + mutex.synchronize do + Puppet.info("Process Name instrumentation is disabled") + @active = false + $0 = @oldname + end + end + + def self.instrument(activity) + # inconditionnally start the scroller thread here + # because it doesn't seem possible to start a new thrad + # from the USR2 signal handler + @scroller ||= Thread.new do + loop do + scroll if active? + sleep 1 + end + end + + push_activity(Thread.current, activity) + yield + ensure + pop_activity(Thread.current) + end + + def self.setproctitle + @oldname ||= $0 + $0 = "#{base}: " + rotate(process_name,@x) if active? + end + + def self.push_activity(thread, activity) + mutex.synchronize do + @reason ||= {} + @reason[thread] ||= [] + @reason[thread].push(activity) + setproctitle + end + end + + def self.pop_activity(thread) + mutex.synchronize do + @reason[thread].pop + if @reason[thread].empty? + @reason.delete(thread) + end + setproctitle + end + end + + def self.process_name + out = (@reason || {}).inject([]) do |out, reason| + out << "#{thread_id(reason[0])} #{reason[1].join(',')}" + end + out.join(' | ') + end + + # certainly non-portable + def self.thread_id(thread) + thread.inspect.gsub(/^#<.*:0x([a-f0-9]+) .*>$/, '\1') + end + + def self.rotate(string, steps) + steps ||= 0 + if string.length > 0 && steps > 0 + steps = steps % string.length + return string[steps..string.length].concat " -- #{string[0..(steps-1)]}" + end + string + end + + def self.base + basename = case Puppet.run_mode.name + when :master + "master" + when :agent + "agent" + else + "puppet" + end + end + + def self.mutex + #Thread.exclusive { + @mutex ||= Sync.new + #} + @mutex + end + + def self.scroll + return if process_name.length < SCROLL_LENGTH + mutex.synchronize do + setproctitle + @x += 1 + end + end + + end +end
\ No newline at end of file |