RubyRun_CE 0.9.0-powerpc-darwin
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +13 -0
- data/README +75 -0
- data/Rakefile +241 -0
- data/bin/confgure +2 -0
- data/docs/RubyRunCE_09.htm +6346 -0
- data/docs/RubyRunCE_09.pdf +0 -0
- data/docs/RubyRunCE_09_files/colorschememapping.xml +2 -0
- data/docs/RubyRunCE_09_files/filelist.xml +29 -0
- data/docs/RubyRunCE_09_files/header.htm +142 -0
- data/docs/RubyRunCE_09_files/image001.jpg +0 -0
- data/docs/RubyRunCE_09_files/image002.jpg +0 -0
- data/docs/RubyRunCE_09_files/image003.jpg +0 -0
- data/docs/RubyRunCE_09_files/image004.jpg +0 -0
- data/docs/RubyRunCE_09_files/image005.jpg +0 -0
- data/docs/RubyRunCE_09_files/image006.jpg +0 -0
- data/docs/RubyRunCE_09_files/image007.jpg +0 -0
- data/docs/RubyRunCE_09_files/image008.jpg +0 -0
- data/docs/RubyRunCE_09_files/image009.jpg +0 -0
- data/docs/RubyRunCE_09_files/image010.jpg +0 -0
- data/docs/RubyRunCE_09_files/image011.jpg +0 -0
- data/docs/RubyRunCE_09_files/image012.jpg +0 -0
- data/docs/RubyRunCE_09_files/image013.jpg +0 -0
- data/docs/RubyRunCE_09_files/image014.jpg +0 -0
- data/docs/RubyRunCE_09_files/image015.jpg +0 -0
- data/docs/RubyRunCE_09_files/image016.jpg +0 -0
- data/docs/RubyRunCE_09_files/image017.png +0 -0
- data/docs/RubyRunCE_09_files/image018.jpg +0 -0
- data/docs/RubyRunCE_09_files/image019.jpg +0 -0
- data/docs/RubyRunCE_09_files/image020.jpg +0 -0
- data/docs/RubyRunCE_09_files/image021.jpg +0 -0
- data/docs/RubyRunCE_09_files/image022.png +0 -0
- data/docs/RubyRunCE_09_files/themedata.thmx +0 -0
- data/etc/rubyrun_opts.yml +132 -0
- data/ext/extconf.rb +4 -0
- data/ext/rubyrunnative__.bundle +0 -0
- data/ext/rubyrunnative__.c +154 -0
- data/ext/rubyrunnative__.def +2 -0
- data/ext/rubyrunnative__.h +36 -0
- data/ext/rubyrunnative__.so +0 -0
- data/ext/rubyrunnative__linux.so +0 -0
- data/html/classes/Module.html +174 -0
- data/html/classes/Object.html +151 -0
- data/html/classes/RubyRunBufferMgr__.html +182 -0
- data/html/classes/RubyRunCommander__.html +578 -0
- data/html/classes/RubyRunDad__.html +144 -0
- data/html/classes/RubyRunGlobals.html +248 -0
- data/html/classes/RubyRunHTMLWriter.html +186 -0
- data/html/classes/RubyRunHTMLWriter/RubyRunHTMLDevice.html +157 -0
- data/html/classes/RubyRunHTML__.html +198 -0
- data/html/classes/RubyRunInitializer__.html +821 -0
- data/html/classes/RubyRunInstrumentor__.html +576 -0
- data/html/classes/RubyRunMonitor__.html +298 -0
- data/html/classes/RubyRunRSS.html +302 -0
- data/html/classes/RubyRunReport__.html +294 -0
- data/html/classes/RubyRunTracer__.html +253 -0
- data/html/classes/RubyRunUtils__.html +376 -0
- data/html/created.rid +1 -0
- data/html/files/LICENSE.html +119 -0
- data/html/files/README.html +196 -0
- data/html/files/lib/rubyrun/rubyrun_buffer_mgr___rb.html +101 -0
- data/html/files/lib/rubyrun/rubyrun_commander___rb.html +101 -0
- data/html/files/lib/rubyrun/rubyrun_dad___rb.html +101 -0
- data/html/files/lib/rubyrun/rubyrun_globals_rb.html +101 -0
- data/html/files/lib/rubyrun/rubyrun_html___rb.html +101 -0
- data/html/files/lib/rubyrun/rubyrun_html_writer___rb.html +108 -0
- data/html/files/lib/rubyrun/rubyrun_initializer___rb.html +112 -0
- data/html/files/lib/rubyrun/rubyrun_instrumentor___rb.html +116 -0
- data/html/files/lib/rubyrun/rubyrun_monitor___rb.html +116 -0
- data/html/files/lib/rubyrun/rubyrun_rb.html +121 -0
- data/html/files/lib/rubyrun/rubyrun_report___rb.html +101 -0
- data/html/files/lib/rubyrun/rubyrun_rss___rb.html +108 -0
- data/html/files/lib/rubyrun/rubyrun_tracer___rb.html +110 -0
- data/html/files/lib/rubyrun/rubyrun_utils___rb.html +108 -0
- data/html/files/lib/rubyrunm_rb.html +116 -0
- data/html/fr_class_index.html +42 -0
- data/html/fr_file_index.html +43 -0
- data/html/fr_method_index.html +96 -0
- data/html/index.html +24 -0
- data/html/rdoc-style.css +208 -0
- data/lib/rubyrun/rubyrun.rb +78 -0
- data/lib/rubyrun/rubyrun_buffer_mgr__.rb +49 -0
- data/lib/rubyrun/rubyrun_commander__.rb +196 -0
- data/lib/rubyrun/rubyrun_dad__.rb +35 -0
- data/lib/rubyrun/rubyrun_globals.rb +51 -0
- data/lib/rubyrun/rubyrun_html__.rb +136 -0
- data/lib/rubyrun/rubyrun_html_writer__.rb +64 -0
- data/lib/rubyrun/rubyrun_initializer__.rb +286 -0
- data/lib/rubyrun/rubyrun_instrumentor__.rb +226 -0
- data/lib/rubyrun/rubyrun_monitor__.rb +237 -0
- data/lib/rubyrun/rubyrun_report__.rb +109 -0
- data/lib/rubyrun/rubyrun_rss__.rb +97 -0
- data/lib/rubyrun/rubyrun_tracer__.rb +79 -0
- data/lib/rubyrun/rubyrun_utils__.rb +98 -0
- data/lib/rubyrun/rubyrunnative__.bundle +0 -0
- data/lib/rubyrunm.rb +10 -0
- metadata +149 -0
@@ -0,0 +1,226 @@
|
|
1
|
+
#---------------------------------------------------------------#
|
2
|
+
# #
|
3
|
+
# (C) Copyright Rubysophic Inc. 2007-2008 #
|
4
|
+
# All rights reserved. #
|
5
|
+
# #
|
6
|
+
# Use, duplication or disclosure of the code is not permitted #
|
7
|
+
# unless licensed. #
|
8
|
+
# #
|
9
|
+
# Last Updated: 7/09/08 #
|
10
|
+
#---------------------------------------------------------------#
|
11
|
+
# #
|
12
|
+
# RubyRunInstrumentor__ module is responsible for performing #
|
13
|
+
# instrumentation on ruby methods via metaprogramming. These #
|
14
|
+
# methods belong to the candidate classes/modules discovered #
|
15
|
+
# earlier in RubyRunInitializer__ plus others which are #
|
16
|
+
# explicitly requested in rubyrun_opts via the use of #
|
17
|
+
# INCLUDE_HASH. #
|
18
|
+
# #
|
19
|
+
#---------------------------------------------------------------#
|
20
|
+
module RubyRunInstrumentor__
|
21
|
+
|
22
|
+
require 'logger'
|
23
|
+
require 'yaml'
|
24
|
+
require 'rubyrun_globals'
|
25
|
+
require 'rubyrun_utils__'
|
26
|
+
require 'rubyrun_monitor__'
|
27
|
+
require 'rubyrun_tracer__'
|
28
|
+
require 'rubyrun_dad__'
|
29
|
+
require 'rubyrun_html__'
|
30
|
+
require 'rubyrun_html_writer__'
|
31
|
+
include RubyRunGlobals
|
32
|
+
include RubyRunUtils__
|
33
|
+
include RubyRunMonitor__
|
34
|
+
include RubyRunTracer__
|
35
|
+
include RubyRunDad__
|
36
|
+
include RubyRunHTML__
|
37
|
+
|
38
|
+
# Invoked by the traps set up in rubyrun.rb. This indicates that
|
39
|
+
# a file has been required/loaded such that the methods within
|
40
|
+
# are being added to the process. Each method being added will
|
41
|
+
# go through the following process to determine if it should be intrumented.
|
42
|
+
#
|
43
|
+
# 1. Through APP_PATHS a stream of class names whose methods are
|
44
|
+
# identified as candiates for instrumnetation in RubyRunIntializer__.
|
45
|
+
# This process forms the initial INCLUDE_HASH which states ALL methods
|
46
|
+
# belonging to these classes/methods should be instrumented
|
47
|
+
# 2. Through additional INCLUDE_HASH in rubyrun_opts a stream of
|
48
|
+
# class => methods hash entries provide further candidates for instrumentation.
|
49
|
+
# 3. Thru EXCLUDE_HASH the exclusion logic is then applied to reduce the scope
|
50
|
+
# of instrumentation.
|
51
|
+
# 4. Some classes and methods are never instrumented regarldess. These
|
52
|
+
# are identifed in constants FIREWALL_HASH.
|
53
|
+
def instrument_it?(type, klass, id)
|
54
|
+
get_dad(type, klass, id)
|
55
|
+
instrument_target(type, klass, id) \
|
56
|
+
if !(is_non_negotiably_excluded?(type, klass, id)) &&
|
57
|
+
!is_in?($rubyrun_exclude_hash, klass, id, 'strict') &&
|
58
|
+
is_in?($rubyrun_include_hash, klass, id, 'strict')
|
59
|
+
end
|
60
|
+
|
61
|
+
# Never instrument the following classes/methods to avoid recursion
|
62
|
+
# 1. Exclude classes and methods that the instrumentation code uses
|
63
|
+
# 2. Exclude method=
|
64
|
+
# 3. Exclude method aliased by rubyrun instrumentation code
|
65
|
+
# 4. Exclude method re-defined by rubyrun instrumentation code
|
66
|
+
# 5. Exclude inherited instances, private, protected, and singleton methods.
|
67
|
+
# The way this works is that if m is one of these non-inherited instance
|
68
|
+
# methods or singleton methods then it should NOT be excluded. Otherwise
|
69
|
+
# it is assumed it is an inherited one and hence excluded.
|
70
|
+
def is_non_negotiably_excluded?(type, klass, id)
|
71
|
+
return true if is_in?(RUBYRUN_FIREWALL_HASH, klass, id)
|
72
|
+
return true if id.id2name[-1,1] == '='
|
73
|
+
if id.id2name[0, RUBYRUN_PREFIX_LENGTH] == RUBYRUN_PREFIX
|
74
|
+
$rubyrun_prev_method = id.id2name
|
75
|
+
return true
|
76
|
+
end
|
77
|
+
if ($rubyrun_prev_method ||="").include?(id.id2name)
|
78
|
+
$rubyrun_prev_method = nil
|
79
|
+
return true
|
80
|
+
end
|
81
|
+
if type == 'i'
|
82
|
+
klass.instance_methods(false).each {|m|
|
83
|
+
return false if m == id.id2name
|
84
|
+
}
|
85
|
+
klass.private_instance_methods(false).each {|m|
|
86
|
+
return false if m == id.id2name
|
87
|
+
}
|
88
|
+
klass.protected_instance_methods(false).each {|m|
|
89
|
+
return false if m == id.id2name
|
90
|
+
}
|
91
|
+
else
|
92
|
+
klass.singleton_methods.each {|m|
|
93
|
+
return false if m == id.id2name
|
94
|
+
}
|
95
|
+
end
|
96
|
+
true
|
97
|
+
end
|
98
|
+
|
99
|
+
# First layer of code performing instrummentation on a class.method
|
100
|
+
# The injecting code is different depending on whether the
|
101
|
+
# method is an instance method, or singleton(static method of a class,
|
102
|
+
# specific method added to an object).
|
103
|
+
#
|
104
|
+
# If this class is a Rails active controller class, create a hash
|
105
|
+
# entry if it does not already exist. This hash is used to keep track
|
106
|
+
# of performance metrics by action by controller.
|
107
|
+
#
|
108
|
+
# If we fail to instrument for whatever reason, log the
|
109
|
+
# errors and leave the method alone.
|
110
|
+
#
|
111
|
+
# Also, create metrics hash for a RAILS controller class if it doesn't exist
|
112
|
+
def instrument_target(type, klass, id)
|
113
|
+
$rubyrun_logger.info "instrumenting #{klass.to_s}.#{id.id2name}."
|
114
|
+
create_metrics_hash(klass) if is_rails_controller?(klass, id)
|
115
|
+
begin
|
116
|
+
case type
|
117
|
+
when 'i'
|
118
|
+
insert_code_to_instance_method(klass, id)
|
119
|
+
when 's'
|
120
|
+
insert_code_to_singleton_method(klass, id)
|
121
|
+
else
|
122
|
+
raise "undefined instrumentation type"
|
123
|
+
end
|
124
|
+
$rubyrun_logger.info "#{klass.to_s}.#{id.id2name} instrumented."
|
125
|
+
rescue Exception => e
|
126
|
+
$rubyrun_logger.info "Class #{klass.to_s}.#{id.id2name} failed to instrument"
|
127
|
+
$rubyrun_logger.info e.to_s + "\n" + e.backtrace.join("\n")
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# To instrument an instance method of a class, a method proxy is used:
|
132
|
+
#
|
133
|
+
# 1. Alias the method to one with a prefix rubyrun_ (i.e., copy the method and
|
134
|
+
# create another one with a new name)
|
135
|
+
# 2. Create a new method with the original name. This is the method proxy.
|
136
|
+
# 3. Preserve the intended visibility of the original method in the proxy
|
137
|
+
# 4. This proxy method essentially adds pre and post wrapper code to
|
138
|
+
# the original code. This wrapper code is embodied in collect_method_data
|
139
|
+
# 5. All these must be done in the context of the class
|
140
|
+
def insert_code_to_instance_method(klass, mid)
|
141
|
+
klass.class_eval {
|
142
|
+
alias_method "#{RubyRunGlobals::RUBYRUN_PREFIX}_#{mid.id2name}", mid.id2name
|
143
|
+
eval <<-CODE1
|
144
|
+
def #{mid.id2name} (*args, &blk)
|
145
|
+
RubyRunInstrumentor__.collect_method_data(self, #{klass}, '#{mid}', *args) {self.send("#{RubyRunGlobals::RUBYRUN_PREFIX}_#{mid.id2name}", *args, &blk)}
|
146
|
+
end
|
147
|
+
CODE1
|
148
|
+
if klass.private_instance_methods(false).include?("#{RubyRunGlobals::RUBYRUN_PREFIX}_#{mid.id2name}")
|
149
|
+
private mid
|
150
|
+
elsif klass.protected_instance_methods(false).include?("#{RubyRunGlobals::RUBYRUN_PREFIX}_#{mid.id2name}")
|
151
|
+
protected mid
|
152
|
+
end
|
153
|
+
}
|
154
|
+
end
|
155
|
+
|
156
|
+
# Same as insert_code_to_instance_method
|
157
|
+
def insert_code_to_singleton_method(klass, mid)
|
158
|
+
(class << klass; self; end).class_eval {
|
159
|
+
alias_method "#{RubyRunGlobals::RUBYRUN_PREFIX}_#{mid.id2name}", mid.id2name
|
160
|
+
eval <<-EOF2
|
161
|
+
def #{mid.id2name} (*args, &blk)
|
162
|
+
RubyRunInstrumentor__.collect_method_data(self, #{klass}, '#{mid}', *args) {self.send("#{RubyRunGlobals::RUBYRUN_PREFIX}_#{mid.id2name}", *args, &blk)}
|
163
|
+
end
|
164
|
+
EOF2
|
165
|
+
if self.private_instance_methods(false).include?("#{RubyRunGlobals::RUBYRUN_PREFIX}_#{mid.id2name}")
|
166
|
+
private mid
|
167
|
+
elsif self.protected_instance_methods(false).include?("#{RubyRunGlobals::RUBYRUN_PREFIX}_#{mid.id2name}")
|
168
|
+
protected mid
|
169
|
+
end
|
170
|
+
}
|
171
|
+
end
|
172
|
+
|
173
|
+
# This is the piece of code that actually executed under the application
|
174
|
+
# thread during runtime and is not executed during instrumentation time
|
175
|
+
#
|
176
|
+
# 1. Create a equivalent thread local storage to store request performance
|
177
|
+
# metrics but only if this class is a Rails Active Controller class
|
178
|
+
# 2. Trace pre and post execution of the original method which has been aliased
|
179
|
+
# The original method is invoked via 'yield'
|
180
|
+
# 3. When a method ends, report the timings to the response time component but
|
181
|
+
# only if a thread local exists
|
182
|
+
def collect_method_data(obj, klass, mid, *args)
|
183
|
+
tid = get_thread_id
|
184
|
+
create_thread_local(tid, obj.request, klass, mid) if is_rails_controller?(klass, mid)
|
185
|
+
rubyrun_trace = is_in?($rubyrun_trace_hash, klass, mid)
|
186
|
+
if rubyrun_trace
|
187
|
+
invoker = get_caller_detail
|
188
|
+
enter_trace(tid, " Entry", obj, invoker, klass, mid, *args)
|
189
|
+
end
|
190
|
+
t1 = Time.new
|
191
|
+
result = yield
|
192
|
+
t2 = Time.new
|
193
|
+
if rubyrun_trace
|
194
|
+
(t2 - t1) >= RUBYRUN_HIGHLIGHT_THRESHOLD ? (type = "* #{sprintf("%6.2f", t2-t1)} Exit ") : (type = " #{sprintf("%6.2f", t2-t1)} Exit ")
|
195
|
+
enter_trace(tid, type, nil, nil, klass, mid, nil)
|
196
|
+
end
|
197
|
+
report_rails_timing(klass, mid, t2, t1, tid) if $rubyrun_thread_local[tid] && (t2 - t1) > 0
|
198
|
+
result
|
199
|
+
end
|
200
|
+
|
201
|
+
# Instrument Thread.new by wrapping target proc with a
|
202
|
+
# begin-rescue clause around the application block.
|
203
|
+
# When the thread monitor shoot the thread via thr.raise
|
204
|
+
# the rescue clause will catch the interrupt and collect the
|
205
|
+
# stack entries in $@ and store them in a global hash, later
|
206
|
+
# on printed in rubyrun log by thread id. If the thread dies
|
207
|
+
# naturally, print the stack trace on the rubyrun log
|
208
|
+
def instrument_thread_new
|
209
|
+
(class << Thread; self; end).class_eval {
|
210
|
+
alias_method "#{RubyRunGlobals::RUBYRUN_PREFIX}_new", "new"
|
211
|
+
def new(*rubyrun_args, &rubyrun_apps_block)
|
212
|
+
rubyrun_proc = lambda {
|
213
|
+
begin
|
214
|
+
rubyrun_apps_block.call(*rubyrun_args)
|
215
|
+
rescue Exception => e
|
216
|
+
e.message == RUBYRUN_KILL_3_STRING ?
|
217
|
+
$@.each {|line| ($rubyrun_thread_stack[Thread.current] ||= []) << line} :
|
218
|
+
$@.each {|line| $rubyrun_logger.info "#{line}"}
|
219
|
+
end
|
220
|
+
}
|
221
|
+
self.send("#{RubyRunGlobals::RUBYRUN_PREFIX}_new", *rubyrun_args, &rubyrun_proc)
|
222
|
+
end
|
223
|
+
}
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
#---------------------------------------------------------------#
|
2
|
+
# #
|
3
|
+
# (C) Copyright Rubysophic Inc. 2007-2008 #
|
4
|
+
# All rights reserved. #
|
5
|
+
# #
|
6
|
+
# Use, duplication or disclosure of the code is not permitted #
|
7
|
+
# unless licensed. #
|
8
|
+
# #
|
9
|
+
# Last Updated: 7/09/08 #
|
10
|
+
#---------------------------------------------------------------#
|
11
|
+
# #
|
12
|
+
# RubyRunMonitor__ is responsible for keeping track of #
|
13
|
+
# the response time of a Rails Request and its decomposition. #
|
14
|
+
# #
|
15
|
+
# For instance, a typical WEBrick/RAILS servlet dispatch #
|
16
|
+
# response time components generally break down look roughly #
|
17
|
+
# like this: #
|
18
|
+
# #
|
19
|
+
# SVS DI ACT DB DB DB VI VI #
|
20
|
+
# -+-----+----+----+--+--+--+--+--+----+--+----+-+--+---+----+ #
|
21
|
+
# DB' DB' DB' ACT' VI' VI' DI' SVS'#
|
22
|
+
# Legend: #
|
23
|
+
# SVS = DispatchServlet.service #
|
24
|
+
# DI = DispatchServlet.handle_dispatch #
|
25
|
+
# ACT = controller.action #
|
26
|
+
# DB = ActiveRecord::ConnectionAdapters::*Adapter.execute #
|
27
|
+
# VI = ActionView.Base:render_template #
|
28
|
+
# All apostrophe means "end of event" #
|
29
|
+
# #
|
30
|
+
# Notes: #
|
31
|
+
# 1. mutex held from SVS to SVS' #
|
32
|
+
# 2. Total dispatch time with mutex = SVS' - SVS #
|
33
|
+
# 3. Total DBIO time = Sum(DB' - DB) #
|
34
|
+
# 4. Action Time = ACT' - ACT #
|
35
|
+
# 5. View time = Sum(VI' - VI) #
|
36
|
+
# 6. View rendering includes template + layout #
|
37
|
+
# #
|
38
|
+
# Another key function is to act as a command agent, responding#
|
39
|
+
# to command such as displaying thread status, terminating #
|
40
|
+
# threads with stack tracke and showing object heap info. #
|
41
|
+
# #
|
42
|
+
#---------------------------------------------------------------#
|
43
|
+
module RubyRunMonitor__
|
44
|
+
|
45
|
+
require 'rubyrun_globals'
|
46
|
+
require 'rubyrun_utils__'
|
47
|
+
require 'rubyrun_tracer__'
|
48
|
+
require 'rubyrun_rss__'
|
49
|
+
require 'rubyrun_buffer_mgr__'
|
50
|
+
require 'rubyrun_html__'
|
51
|
+
require 'rubyrun_report__'
|
52
|
+
require 'rubyrun_commander__'
|
53
|
+
begin
|
54
|
+
require 'rubyrunnative__'
|
55
|
+
$rubyrun_native = true
|
56
|
+
rescue Exception
|
57
|
+
$rubyrun_native = false
|
58
|
+
end
|
59
|
+
include RubyRunGlobals
|
60
|
+
include RubyRunUtils__
|
61
|
+
include RubyRunTracer__
|
62
|
+
include RubyRunBufferMgr__
|
63
|
+
include RubyRunHTML__
|
64
|
+
include RubyRunReport__
|
65
|
+
include RubyRunCommander__
|
66
|
+
|
67
|
+
# In response to the presence of a 'cmd_status', 'cmd_soft_kill', 'cmd_hard_kill'
|
68
|
+
# or 'cmd_object_map' in the work directory, the monitor thread will either
|
69
|
+
# display thread status, interrupt the threads in different manner, or show object instances
|
70
|
+
# in memory
|
71
|
+
def start_thread_monitor
|
72
|
+
$rubyrun_logger.info "----- RubyRun Thread Monitor started -----"
|
73
|
+
monitor_thr = Thread.new {
|
74
|
+
cycle = $rubyrun_report_timer / RUBYRUN_MONITOR_TIMER
|
75
|
+
sleep_count = 0
|
76
|
+
loop do
|
77
|
+
sleep RUBYRUN_MONITOR_TIMER
|
78
|
+
monitor_thr.exit if exit_monitor?
|
79
|
+
Thread.new {
|
80
|
+
begin
|
81
|
+
sleep_count += 1
|
82
|
+
sleep_count == cycle ? (dump_reports(true); sleep_count = 0) : dump_reports
|
83
|
+
dump_thread_status if thread_status?
|
84
|
+
dump_object_map if object_map?
|
85
|
+
kill_threads(monitor_thr) if soft_kill? || hard_kill?
|
86
|
+
rescue Exception => e
|
87
|
+
$stderr.print e.to_s + "\n" + e.backtrace.join("\n")
|
88
|
+
exit(-1)
|
89
|
+
end
|
90
|
+
}
|
91
|
+
end
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
# Simulate a thread local storage by using a private hash keyed on thread id.
|
96
|
+
# Key elements in the hash are, for instance:
|
97
|
+
# {#{tid} => {:req=>request.object_id, :controller=>name, :action=> name,
|
98
|
+
# :action_t=>t, :dbio=>t, :dispatch_t=>t, :view_t=>t, :uncaptured_t=>t,
|
99
|
+
# :dispatch_wait_t=>t}}
|
100
|
+
# Thread local is used to store performance metrics of a RAILS request.
|
101
|
+
# When the same thread serves a different request, the current thread local
|
102
|
+
# data needs to be rolled up and re-initialized.
|
103
|
+
def create_thread_local(tid, request, klass, mid)
|
104
|
+
$rubyrun_thread_local[tid] ||= {}
|
105
|
+
init_thread_local(tid, request, klass, mid) unless $rubyrun_thread_local[tid][:req]
|
106
|
+
if $rubyrun_thread_local[tid][:req] != request.object_id
|
107
|
+
roll_up_metrics(tid)
|
108
|
+
init_thread_local(tid, request, klass, mid)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Create a place holding global metrics hash for each RAILS application
|
113
|
+
# controller class to accumulate performance metrics by action.
|
114
|
+
# Key elements in the hash are, for instance:
|
115
|
+
# {#{controller} => {#{action} => [dispatch_t, action_t, dbio_t, view_t, uncap_t, dispatch_wait_t]}}
|
116
|
+
def create_metrics_hash(klass)
|
117
|
+
$rubyrun_metrics_hash[klass.to_s.downcase[0..-11]] ||= {}
|
118
|
+
end
|
119
|
+
|
120
|
+
# Report timings to thread local after decomposing it into the right component.
|
121
|
+
# For each action, there are 5 components of Response Time (RT)
|
122
|
+
# :dispatch_t, :#{action}_t, :dbio_t, :view_t, :uncaptured_t
|
123
|
+
def report_rails_timing(klass, mid, t2, t1, tid)
|
124
|
+
t = t2 - t1
|
125
|
+
if is_in_hash?($rubyrun_adapter_hash, klass, mid)
|
126
|
+
$rubyrun_thread_local[tid][:dbio_t] += t
|
127
|
+
elsif is_in_hash?(RUBYRUN_VIEW_HASH, klass, mid)
|
128
|
+
$rubyrun_thread_local[tid][:view_t] << t2 << t1
|
129
|
+
elsif is_in_hash?($rubyrun_outer_dispatch_hash, klass, mid)
|
130
|
+
$rubyrun_thread_local[tid][:outer_dispatch_t] << t
|
131
|
+
elsif is_in_hash?($rubyrun_inner_dispatch_hash, klass, mid)
|
132
|
+
$rubyrun_thread_local[tid][:inner_dispatch_t] << t
|
133
|
+
elsif is_rails_controller?(klass, mid)
|
134
|
+
$rubyrun_thread_local[tid][:action_t] = t
|
135
|
+
$rubyrun_thread_local[tid][:scafold_style] = $rubyrun_thread_local[tid][:view_t].empty? ? true : false
|
136
|
+
elsif is_in?(RUBYRUN_THREAD_END_HASH, klass, mid, 'strict')
|
137
|
+
roll_up_metrics(tid, true)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
# Roll up action metrics to controller_metrics_hash
|
144
|
+
# First, roll up the pieces in thread_local
|
145
|
+
# Then roll up the thread local data to the metrics global hash
|
146
|
+
def roll_up_metrics(tid, thread_end=false)
|
147
|
+
($rubyrun_thread_local[tid].clear; return) if thread_local_incomplete?(tid)
|
148
|
+
dbio_t = $rubyrun_thread_local[tid][:dbio_t]
|
149
|
+
outer_dispatch_t = $rubyrun_thread_local[tid][:outer_dispatch_t].max
|
150
|
+
inner_dispatch_t = $rubyrun_thread_local[tid][:inner_dispatch_t].max
|
151
|
+
dispatch_wait_t = outer_dispatch_t - inner_dispatch_t
|
152
|
+
view_t = $rubyrun_thread_local[tid][:view_t].empty? ? 0 : $rubyrun_thread_local[tid][:view_t].max - $rubyrun_thread_local[tid][:view_t].min
|
153
|
+
action_t = ($rubyrun_thread_local[tid][:action_t])
|
154
|
+
uncap_t = $rubyrun_thread_local[tid][:scafold_style] ? outer_dispatch_t - view_t - action_t - dispatch_wait_t : outer_dispatch_t - action_t - dispatch_wait_t
|
155
|
+
$rubyrun_thread_local[tid][:uncaptured_t] = uncap_t
|
156
|
+
$rubyrun_thread_local[tid][:dispatch_wait_t] = dispatch_wait_t
|
157
|
+
push_current_buffer([tid, Time.now,
|
158
|
+
$rubyrun_thread_local[tid][:url],
|
159
|
+
$rubyrun_thread_local[tid][:controller],
|
160
|
+
$rubyrun_thread_local[tid][:action],
|
161
|
+
outer_dispatch_t, action_t, dbio_t, view_t, uncap_t, dispatch_wait_t])
|
162
|
+
!thread_end ? $rubyrun_thread_local[tid].clear : $rubyrun_thread_local.delete(tid)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Initialize the thread_local hash
|
166
|
+
def init_thread_local(tid, request, klass, mid)
|
167
|
+
$rubyrun_thread_local[tid][:req] = request.object_id
|
168
|
+
$rubyrun_thread_local[tid][:controller] = klass.to_s.split('Controller')[0].downcase
|
169
|
+
$rubyrun_thread_local[tid][:action] = return_method_name(mid)
|
170
|
+
$rubyrun_thread_local[tid][:url] = request.protocol + request.host_with_port + request.request_uri
|
171
|
+
$rubyrun_thread_local[tid][:dbio_t] = 0
|
172
|
+
$rubyrun_thread_local[tid][:outer_dispatch_t] = []
|
173
|
+
$rubyrun_thread_local[tid][:inner_dispatch_t] = []
|
174
|
+
$rubyrun_thread_local[tid][:action_t] = 0
|
175
|
+
$rubyrun_thread_local[tid][:uncaptured_t] = 0
|
176
|
+
$rubyrun_thread_local[tid][:view_t] = []
|
177
|
+
$rubyrun_thread_local[tid][:dispatch_wait_t] = 0
|
178
|
+
$rubyrun_thread_local[tid][:scafold_style] = false
|
179
|
+
$rubyrun_host_with_port = request.host_with_port
|
180
|
+
end
|
181
|
+
|
182
|
+
# Use the data hash returned by the native function to show
|
183
|
+
# the top frame inside the thread struct (node and orig_func)
|
184
|
+
def get_top_stack(th_data_hash, thread_id)
|
185
|
+
th_data_hash.each {|th, top_stack|
|
186
|
+
if th.to_s.include?(thread_id)
|
187
|
+
return "#{top_stack[0].gsub('rubyrun_', '')}"
|
188
|
+
break
|
189
|
+
end
|
190
|
+
}
|
191
|
+
end
|
192
|
+
|
193
|
+
# If request aborted, thread_local can be corrupted (half filled)
|
194
|
+
# Return true if corrupted else false
|
195
|
+
def thread_local_incomplete? (tid)
|
196
|
+
$rubyrun_thread_local[tid][:controller].nil? ||
|
197
|
+
$rubyrun_thread_local[tid][:action].nil? ||
|
198
|
+
$rubyrun_thread_local[tid][:outer_dispatch_t].empty? ||
|
199
|
+
$rubyrun_thread_local[tid][:inner_dispatch_t].empty?
|
200
|
+
end
|
201
|
+
|
202
|
+
# Sort $rubyrun_metrics_hash by response time in descending order
|
203
|
+
# An array of the following data structure is returned:
|
204
|
+
# metrics[0] controller/action name
|
205
|
+
# metrics[1] Array of performance data
|
206
|
+
# metrics[1][0] resposne time metrics[1][1] action time
|
207
|
+
# metrics[1][2] database IO time metrics[1][3] view time
|
208
|
+
# metrics[1][4] uncaptured time metrics[1][5] wait time
|
209
|
+
# metrics[1][6] request count
|
210
|
+
def sort_performance_metrics
|
211
|
+
results = Hash.new
|
212
|
+
$rubyrun_metrics_hash.each {|controller, action_metrics|
|
213
|
+
next if action_metrics.empty?
|
214
|
+
action_metrics.each {|action, metrics|
|
215
|
+
results["#{controller}/#{action}"] = metrics
|
216
|
+
}
|
217
|
+
}
|
218
|
+
results.sort {|a, b| -1*(a[1]<=>b[1])}
|
219
|
+
end
|
220
|
+
|
221
|
+
# An optimized runtime version of the original is_in? in RubyRunInstrumentor__
|
222
|
+
# This is used during runtime and not instrumentation, hence something of better
|
223
|
+
# performance but less general is required.
|
224
|
+
def is_in_hash?(hash, klass, mid)
|
225
|
+
return false if hash.empty?
|
226
|
+
name = klass.to_s
|
227
|
+
if hash.has_key?(name)
|
228
|
+
return true if hash[name].empty?
|
229
|
+
method_name = return_method_name(mid)
|
230
|
+
hash[name].each {|meth_name|
|
231
|
+
return true if method_name.downcase == meth_name.downcase
|
232
|
+
}
|
233
|
+
end
|
234
|
+
false
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|