Class: RBTracer
- Inherits:
-
Object
- Object
- RBTracer
- Defined in:
- lib/rbtrace/version.rb,
lib/rbtrace/rbtracer.rb
Constant Summary collapse
- VERSION =
'0.4.4'
Instance Attribute Summary collapse
-
#out ⇒ Object
Public: The IO where tracing output is written (default: STDOUT).
-
#pid ⇒ Object
readonly
Public: The Fixnum pid of the traced process.
-
#prefix ⇒ Object
The String prefix used on nested method calls (default: ‘ ’).
-
#show_duration ⇒ Object
The Boolean flag for showing how long method calls take (default: true).
-
#show_time ⇒ Object
The Boolean flag for showing the timestamp when method calls start (default: false).
-
#timeout ⇒ Object
Public: The timeout before giving up on attaching/detaching to a process.
Instance Method Summary collapse
-
#add(methods, slow = false) ⇒ Object
Add tracers for the given list of methods.
-
#add_slow(methods) ⇒ Object
Restrict slow tracing to a specific list of methods.
-
#attach ⇒ Object
Attach to the process.
- #clean_socket_path ⇒ Object
-
#detach ⇒ Object
Detach from the traced process.
-
#devmode ⇒ Object
Turn on dev mode.
-
#eval(code) ⇒ Object
Evaluate some ruby code.
-
#firehose ⇒ Object
Turn on the firehose (show all method calls).
-
#fork ⇒ Object
Fork the process and return the copy’s pid.
-
#gc ⇒ Object
Turn on GC tracing.
-
#initialize(pid) ⇒ RBTracer
constructor
Create a new tracer.
- #puts(arg = nil) ⇒ Object
-
#recv_lines ⇒ Object
Process events from the traced process, without blocking if there is nothing to do.
-
#recv_loop ⇒ Object
Process events from the traced process.
- #socket_path ⇒ Object
-
#watch(msec, cpu_only = false) ⇒ Object
Watch for method calls slower than a threshold.
Constructor Details
#initialize(pid) ⇒ RBTracer
Create a new tracer
pid - The String of Fixnum process id
Returns a tracer.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/rbtrace/rbtracer.rb', line 32 def initialize(pid) begin raise ArgumentError unless pid @pid = pid.to_i raise ArgumentError unless @pid > 0 Process.kill(0, @pid) rescue TypeError, ArgumentError raise ArgumentError, 'pid required' rescue Errno::ESRCH raise ArgumentError, 'invalid pid' rescue Errno::EPERM raise ArgumentError, 'could not signal process, are you running as root?' end @sock = Socket.new Socket::AF_UNIX, Socket::SOCK_DGRAM, 0 @sockaddr = Socket.pack_sockaddr_un(socket_path) @sock.bind(@sockaddr) FileUtils.chmod 0666, socket_path at_exit { clean_socket_path } 5.times do signal sleep 0.15 # wait for process to create msgqs @qo = MsgQ.msgget(-@pid, 0666) break if @qo > -1 end if @qo == -1 raise ArgumentError, 'pid is not listening for messages, did you `require "rbtrace"`' end @klasses = {} @methods = {} @tracers = Hash.new{ |h,k| h[k] = { :query => nil, :times => [], :names => [], :exprs => {}, :last => false, :arglist => false } } @max_nesting = @last_nesting = @nesting = 0 @last_tracer = nil @timeout = 5 @out = STDOUT @out.sync = true @prefix = ' ' @printed_newline = true @show_time = false @show_duration = true @watch_slow = false attach end |
Instance Attribute Details
#out ⇒ Object
Public: The IO where tracing output is written (default: STDOUT).
13 14 15 |
# File 'lib/rbtrace/rbtracer.rb', line 13 def out @out end |
#pid ⇒ Object (readonly)
Public: The Fixnum pid of the traced process.
10 11 12 |
# File 'lib/rbtrace/rbtracer.rb', line 10 def pid @pid end |
#prefix ⇒ Object
The String prefix used on nested method calls (default: ‘ ’).
19 20 21 |
# File 'lib/rbtrace/rbtracer.rb', line 19 def prefix @prefix end |
#show_duration ⇒ Object
The Boolean flag for showing how long method calls take (default: true).
22 23 24 |
# File 'lib/rbtrace/rbtracer.rb', line 22 def show_duration @show_duration end |
#show_time ⇒ Object
The Boolean flag for showing the timestamp when method calls start (default: false).
25 26 27 |
# File 'lib/rbtrace/rbtracer.rb', line 25 def show_time @show_time end |
#timeout ⇒ Object
Public: The timeout before giving up on attaching/detaching to a process.
16 17 18 |
# File 'lib/rbtrace/rbtracer.rb', line 16 def timeout @timeout end |
Instance Method Details
#add(methods, slow = false) ⇒ Object
Add tracers for the given list of methods.
methods - The String or Array of method selectors to trace.
Returns nothing.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/rbtrace/rbtracer.rb', line 178 def add(methods, slow=false) Array(methods).each do |func| func = func.strip next if func.empty? if func =~ /^(.+?)\((.+)\)$/ name, args = $1, $2 args = args.split(',').map{ |a| a.strip } end send_cmd(:add, name || func, slow) if args and args.any? args.each do |arg| if (err = valid_syntax?(arg)) != true raise ArgumentError, "#{err.class} for expression #{arg.inspect} in method #{func.inspect}" end if arg =~ /^@/ and arg !~ /^@[_a-z][_a-z0-9]+$/i # arg[0]=='@' means ivar, but if this is an expr # we can hack a space in front so it gets eval'd instead arg = " #{arg}" end send_cmd(:addexpr, arg) end end end end |
#add_slow(methods) ⇒ Object
Restrict slow tracing to a specific list of methods.
methods - The String or Array of method selectors.
Returns nothing.
169 170 171 |
# File 'lib/rbtrace/rbtracer.rb', line 169 def add_slow(methods) add(methods, true) end |
#attach ⇒ Object
Attach to the process.
Returns nothing.
209 210 211 212 213 214 215 216 |
# File 'lib/rbtrace/rbtracer.rb', line 209 def attach send_cmd(:attach, Process.pid) if wait('to attach'){ @attached == true } STDERR.puts "*** attached to process #{pid}" else raise ArgumentError, 'process already being traced?' end end |
#clean_socket_path ⇒ Object
98 99 100 |
# File 'lib/rbtrace/rbtracer.rb', line 98 def clean_socket_path FileUtils.rm(socket_path) if File.exists?(socket_path) end |
#detach ⇒ Object
Detach from the traced process.
Returns nothing.
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/rbtrace/rbtracer.rb', line 221 def detach begin send_cmd(:detach) rescue Errno::ESRCH end newline if wait('to detach cleanly'){ @attached == false } newline STDERR.puts "*** detached from process #{pid}" else newline STDERR.puts "*** could not detach cleanly from process #{pid}" end rescue Errno::EINVAL, Errno::EIDRM newline STDERR.puts "*** process #{pid} is gone" # STDERR.puts "*** #{$!.inspect}" # STDERR.puts $!.backtrace.join("\n ") rescue Interrupt, SignalException retry ensure clean_socket_path end |
#devmode ⇒ Object
Turn on dev mode.
Returns nothing.
122 123 124 |
# File 'lib/rbtrace/rbtracer.rb', line 122 def devmode send_cmd(:devmode) end |
#eval(code) ⇒ Object
Evaluate some ruby code.
Returns the String result.
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/rbtrace/rbtracer.rb', line 141 def eval(code) if (err = valid_syntax?(code)) != true raise ArgumentError, "#{err.class} for expression #{code.inspect}" end send_cmd(:eval, code) if wait('for eval response', 15){ !!@eval_result } res = @eval_result @eval_result = nil res else STDERR.puts '*** timed out waiting for eval response' end end |
#firehose ⇒ Object
Turn on the firehose (show all method calls).
Returns nothing.
115 116 117 |
# File 'lib/rbtrace/rbtracer.rb', line 115 def firehose send_cmd(:firehose) end |
#fork ⇒ Object
Fork the process and return the copy’s pid.
Returns a Fixnum pid.
129 130 131 132 133 134 135 136 |
# File 'lib/rbtrace/rbtracer.rb', line 129 def fork send_cmd(:fork) if wait('for fork', 30){ !!@forked_pid } @forked_pid else STDERR.puts '*** timed out waiting for fork' end end |
#gc ⇒ Object
Turn on GC tracing.
Returns nothing.
160 161 162 |
# File 'lib/rbtrace/rbtracer.rb', line 160 def gc send_cmd(:gc) end |
#puts(arg = nil) ⇒ Object
274 275 276 277 |
# File 'lib/rbtrace/rbtracer.rb', line 274 def puts(arg=nil) @printed_newline = true arg ? @out.puts(arg) : @out.puts end |
#recv_lines ⇒ Object
Process events from the traced process, without blocking if there is nothing to do. This is a useful way to drain the buffer so messages do not accumulate in kernel land.
Returns nothing.
267 268 269 270 271 272 |
# File 'lib/rbtrace/rbtracer.rb', line 267 def recv_lines 50.times do break unless line = recv_cmd(false) process_line(line) end end |
#recv_loop ⇒ Object
Process events from the traced process.
Returns nothing.
250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/rbtrace/rbtracer.rb', line 250 def recv_loop while true # block until a message arrives process_line(recv_cmd) # process any remaining messages recv_lines end rescue Errno::EINVAL, Errno::EIDRM # process went away end |
#socket_path ⇒ Object
94 95 96 |
# File 'lib/rbtrace/rbtracer.rb', line 94 def socket_path "/tmp/rbtrace-#{@pid}.sock" end |
#watch(msec, cpu_only = false) ⇒ Object
Watch for method calls slower than a threshold.
msec - The Fixnum threshold in milliseconds
Returns nothing.
107 108 109 110 |
# File 'lib/rbtrace/rbtracer.rb', line 107 def watch(msec, cpu_only=false) @watch_slow = true send_cmd(cpu_only ? :watchcpu : :watch, msec) end |