rubyrun 0.9.5-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/LICENSE +13 -0
  2. data/README +77 -0
  3. data/bin/Rakefile +11 -0
  4. data/docs/rubyrun-0.9.5.htm +5937 -0
  5. data/docs/rubyrun-0.9.5.pdf +0 -0
  6. data/docs/rubyrun-0.9.5_files/colorschememapping.xml +2 -0
  7. data/docs/rubyrun-0.9.5_files/filelist.xml +29 -0
  8. data/docs/rubyrun-0.9.5_files/header.htm +138 -0
  9. data/docs/rubyrun-0.9.5_files/image001.jpg +0 -0
  10. data/docs/rubyrun-0.9.5_files/image002.jpg +0 -0
  11. data/docs/rubyrun-0.9.5_files/image003.jpg +0 -0
  12. data/docs/rubyrun-0.9.5_files/image004.jpg +0 -0
  13. data/docs/rubyrun-0.9.5_files/image005.jpg +0 -0
  14. data/docs/rubyrun-0.9.5_files/image006.jpg +0 -0
  15. data/docs/rubyrun-0.9.5_files/image007.jpg +0 -0
  16. data/docs/rubyrun-0.9.5_files/image008.jpg +0 -0
  17. data/docs/rubyrun-0.9.5_files/image009.jpg +0 -0
  18. data/docs/rubyrun-0.9.5_files/image010.jpg +0 -0
  19. data/docs/rubyrun-0.9.5_files/image011.jpg +0 -0
  20. data/docs/rubyrun-0.9.5_files/image012.jpg +0 -0
  21. data/docs/rubyrun-0.9.5_files/image013.jpg +0 -0
  22. data/docs/rubyrun-0.9.5_files/image014.jpg +0 -0
  23. data/docs/rubyrun-0.9.5_files/image015.jpg +0 -0
  24. data/docs/rubyrun-0.9.5_files/image016.jpg +0 -0
  25. data/docs/rubyrun-0.9.5_files/image017.png +0 -0
  26. data/docs/rubyrun-0.9.5_files/image018.jpg +0 -0
  27. data/docs/rubyrun-0.9.5_files/image019.jpg +0 -0
  28. data/docs/rubyrun-0.9.5_files/image020.jpg +0 -0
  29. data/docs/rubyrun-0.9.5_files/image021.jpg +0 -0
  30. data/docs/rubyrun-0.9.5_files/image022.png +0 -0
  31. data/docs/rubyrun-0.9.5_files/themedata.thmx +0 -0
  32. data/etc/rubyrun_opts.yml +132 -0
  33. data/ext/extconf.rb +5 -0
  34. data/ext/rubyrunnative__.c +154 -0
  35. data/ext/rubyrunnative__.def +2 -0
  36. data/ext/rubyrunnative__.h +36 -0
  37. data/ext/rubyrunnative__ppc-darwin.bundle +0 -0
  38. data/ext/rubyrunnative__x86-darwin.bundle +0 -0
  39. data/ext/rubyrunnative__x86-linux.so +0 -0
  40. data/ext/rubyrunnative__x86-mswin32.so +0 -0
  41. data/lib/rubyrun/rubyrun.rb +2 -0
  42. data/lib/rubyrun/rubyrun_boot__.rb +79 -0
  43. data/lib/rubyrun/rubyrun_buffer_mgr__.rb +49 -0
  44. data/lib/rubyrun/rubyrun_commander__.rb +196 -0
  45. data/lib/rubyrun/rubyrun_dad__.rb +35 -0
  46. data/lib/rubyrun/rubyrun_globals.rb +52 -0
  47. data/lib/rubyrun/rubyrun_html__.rb +136 -0
  48. data/lib/rubyrun/rubyrun_html_writer__.rb +64 -0
  49. data/lib/rubyrun/rubyrun_initializer__.rb +313 -0
  50. data/lib/rubyrun/rubyrun_instrumentor__.rb +226 -0
  51. data/lib/rubyrun/rubyrun_monitor__.rb +238 -0
  52. data/lib/rubyrun/rubyrun_report__.rb +109 -0
  53. data/lib/rubyrun/rubyrun_rss__.rb +97 -0
  54. data/lib/rubyrun/rubyrun_tracer__.rb +79 -0
  55. data/lib/rubyrun/rubyrun_utils__.rb +101 -0
  56. data/lib/rubyrun/rubyrunnative__.so +0 -0
  57. metadata +115 -0
@@ -0,0 +1,238 @@
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
+ return unless ($rubyrun_thread_local[tid] || is_action?(klass, mid))
105
+ $rubyrun_thread_local[tid] ||= {}
106
+ init_thread_local(tid, request, klass, mid) unless $rubyrun_thread_local[tid][:req]
107
+ if $rubyrun_thread_local[tid][:req] != request.object_id
108
+ roll_up_metrics(tid)
109
+ init_thread_local(tid, request, klass, mid)
110
+ end
111
+ end
112
+
113
+ # Create a place holding global metrics hash for each RAILS application
114
+ # controller class to accumulate performance metrics by action.
115
+ # Key elements in the hash are, for instance:
116
+ # {#{controller} => {#{action} => [dispatch_t, action_t, dbio_t, view_t, uncap_t, dispatch_wait_t]}}
117
+ def create_metrics_hash(klass)
118
+ $rubyrun_metrics_hash[klass.to_s.downcase[0..-11]] ||= {}
119
+ end
120
+
121
+ # Report timings to thread local after decomposing it into the right component.
122
+ # For each action, there are 5 components of Response Time (RT)
123
+ # :dispatch_t, :#{action}_t, :dbio_t, :view_t, :uncaptured_t
124
+ def report_rails_timing(klass, mid, t2, t1, tid)
125
+ t = t2 - t1
126
+ if is_in_hash?($rubyrun_adapter_hash, klass, mid)
127
+ $rubyrun_thread_local[tid][:dbio_t] += t
128
+ elsif is_in_hash?(RUBYRUN_VIEW_HASH, klass, mid)
129
+ $rubyrun_thread_local[tid][:view_t] << t2 << t1
130
+ elsif is_in_hash?($rubyrun_outer_dispatch_hash, klass, mid)
131
+ $rubyrun_thread_local[tid][:outer_dispatch_t] << t
132
+ elsif is_in_hash?($rubyrun_inner_dispatch_hash, klass, mid)
133
+ $rubyrun_thread_local[tid][:inner_dispatch_t] << t
134
+ elsif is_rails_controller?(klass)
135
+ $rubyrun_thread_local[tid][:action_t] = t
136
+ $rubyrun_thread_local[tid][:scafold_style] = $rubyrun_thread_local[tid][:view_t].empty? ? true : false
137
+ elsif is_in?(RUBYRUN_THREAD_END_HASH, klass, mid, 'strict')
138
+ roll_up_metrics(tid, true)
139
+ end
140
+ end
141
+
142
+ private
143
+
144
+ # Roll up action metrics to controller_metrics_hash
145
+ # First, roll up the pieces in thread_local
146
+ # Then roll up the thread local data to the metrics global hash
147
+ def roll_up_metrics(tid, thread_end=false)
148
+ ($rubyrun_thread_local[tid].clear; return) if thread_local_incomplete?(tid)
149
+ dbio_t = $rubyrun_thread_local[tid][:dbio_t]
150
+ outer_dispatch_t = $rubyrun_thread_local[tid][:outer_dispatch_t].max
151
+ inner_dispatch_t = $rubyrun_thread_local[tid][:inner_dispatch_t].max
152
+ dispatch_wait_t = outer_dispatch_t - inner_dispatch_t
153
+ view_t = $rubyrun_thread_local[tid][:view_t].empty? ? 0 : $rubyrun_thread_local[tid][:view_t].max - $rubyrun_thread_local[tid][:view_t].min
154
+ action_t = ($rubyrun_thread_local[tid][:action_t])
155
+ 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
156
+ $rubyrun_thread_local[tid][:uncaptured_t] = uncap_t
157
+ $rubyrun_thread_local[tid][:dispatch_wait_t] = dispatch_wait_t
158
+ push_current_buffer([tid, Time.now,
159
+ $rubyrun_thread_local[tid][:url],
160
+ $rubyrun_thread_local[tid][:controller],
161
+ $rubyrun_thread_local[tid][:action],
162
+ outer_dispatch_t, action_t, dbio_t, view_t, uncap_t, dispatch_wait_t])
163
+ !thread_end ? $rubyrun_thread_local[tid].clear : $rubyrun_thread_local.delete(tid)
164
+ end
165
+
166
+ # Initialize the thread_local hash
167
+ def init_thread_local(tid, request, klass, mid)
168
+ $rubyrun_thread_local[tid][:req] = request.object_id
169
+ $rubyrun_thread_local[tid][:controller] = klass.to_s.split('Controller')[0].downcase
170
+ $rubyrun_thread_local[tid][:action] = return_method_name(mid)
171
+ $rubyrun_thread_local[tid][:url] = request.protocol + request.host_with_port + request.request_uri
172
+ $rubyrun_thread_local[tid][:dbio_t] = 0
173
+ $rubyrun_thread_local[tid][:outer_dispatch_t] = []
174
+ $rubyrun_thread_local[tid][:inner_dispatch_t] = []
175
+ $rubyrun_thread_local[tid][:action_t] = 0
176
+ $rubyrun_thread_local[tid][:uncaptured_t] = 0
177
+ $rubyrun_thread_local[tid][:view_t] = []
178
+ $rubyrun_thread_local[tid][:dispatch_wait_t] = 0
179
+ $rubyrun_thread_local[tid][:scafold_style] = false
180
+ $rubyrun_host_with_port = request.host_with_port
181
+ end
182
+
183
+ # Use the data hash returned by the native function to show
184
+ # the top frame inside the thread struct (node and orig_func)
185
+ def get_top_stack(th_data_hash, thread_id)
186
+ th_data_hash.each {|th, top_stack|
187
+ if th.to_s.include?(thread_id)
188
+ return "#{top_stack[0].gsub('rubyrun_', '')}"
189
+ break
190
+ end
191
+ }
192
+ end
193
+
194
+ # If request aborted, thread_local can be corrupted (half filled)
195
+ # Return true if corrupted else false
196
+ def thread_local_incomplete? (tid)
197
+ $rubyrun_thread_local[tid][:controller].nil? ||
198
+ $rubyrun_thread_local[tid][:action].nil? ||
199
+ $rubyrun_thread_local[tid][:outer_dispatch_t].empty? ||
200
+ $rubyrun_thread_local[tid][:inner_dispatch_t].empty?
201
+ end
202
+
203
+ # Sort $rubyrun_metrics_hash by response time in descending order
204
+ # An array of the following data structure is returned:
205
+ # metrics[0] controller/action name
206
+ # metrics[1] Array of performance data
207
+ # metrics[1][0] resposne time metrics[1][1] action time
208
+ # metrics[1][2] database IO time metrics[1][3] view time
209
+ # metrics[1][4] uncaptured time metrics[1][5] wait time
210
+ # metrics[1][6] request count
211
+ def sort_performance_metrics
212
+ results = Hash.new
213
+ $rubyrun_metrics_hash.each {|controller, action_metrics|
214
+ next if action_metrics.empty?
215
+ action_metrics.each {|action, metrics|
216
+ results["#{controller}/#{action}"] = metrics
217
+ }
218
+ }
219
+ results.sort {|a, b| -1*(a[1]<=>b[1])}
220
+ end
221
+
222
+ # An optimized runtime version of the original is_in? in RubyRunInstrumentor__
223
+ # This is used during runtime and not instrumentation, hence something of better
224
+ # performance but less general is required.
225
+ def is_in_hash?(hash, klass, mid)
226
+ return false if hash.empty?
227
+ name = klass.to_s
228
+ if hash.has_key?(name)
229
+ return true if hash[name].empty?
230
+ method_name = return_method_name(mid)
231
+ hash[name].each {|meth_name|
232
+ return true if method_name.downcase == meth_name.downcase
233
+ }
234
+ end
235
+ false
236
+ end
237
+
238
+ end
@@ -0,0 +1,109 @@
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/7/08 #
10
+ #---------------------------------------------------------------#
11
+ # #
12
+ # RubyRunReport__ is responsible for generating report data in #
13
+ # RSS or CSV format depending on the type of report. #
14
+ # #
15
+ #---------------------------------------------------------------#
16
+ module RubyRunReport__
17
+
18
+ # Add entries to transaction log CSV file
19
+ # metrics structure
20
+ # metrics[0] Thread ID metrics[1] Timestamp of the request
21
+ # metrics[2] URL metrics[3] Controller name
22
+ # metrics[4] Action name metrics[5] Response time
23
+ # metrics[6] Action time metrics[7] Database IO time
24
+ # metrics[8] View time metrics[9] Uncaptured time
25
+ # metrics[10] Dispatch wait time
26
+ # CSV format is URL, thread ID, timestamp, response time, action time,
27
+ # database io time, view time, dispatch wait time, uncaptured time
28
+ def add_txn_log_csv_item(buffer)
29
+ return if buffer.length == 0
30
+ $rubyrun_txn_log_reporter.info "\n----- Transaction Log at #{Time.now.ctime} -----"
31
+ buffer.each { |metrics|
32
+ $rubyrun_txn_log_reporter.info "#{metrics[2]},#{metrics[0]},#{metrics[1].strftime("%H:%M:%S")}.#{("%.3f" % metrics[1].to_f).split('.')[1]} #{metrics[1].strftime("%m/%d/%y")},#{sprintf("%0.3f", metrics[5])},#{sprintf("%0.3f", metrics[6])},#{sprintf("%0.3f", metrics[7])},#{sprintf("%0.3f", metrics[8])},#{sprintf("%0.3f", metrics[10])},#{sprintf("%0.3f", metrics[9])}"
33
+ }
34
+ end
35
+
36
+ # Add an item to performance summary RSS channel
37
+ def add_perf_summary_rss_item(req_count)
38
+ return unless $rubyrun_host_with_port # Server started but no request yet
39
+ # First, Throughput Summary
40
+ $rubyrun_throughput.shift if $rubyrun_throughput.length == $rubyrun_report_shift_age
41
+ index = $rubyrun_throughput.length
42
+ $rubyrun_throughput[index] = [Time.now, req_count.to_f/$rubyrun_report_timer*60]
43
+ bar_content = ''
44
+ label_content = ''
45
+ max = ($rubyrun_throughput.max {|a,b| a[1] <=> b[1]})[1]
46
+ max = 1 if max == 0 # Avoid divided by zero below
47
+ $rubyrun_throughput.reverse!
48
+ $rubyrun_report_shift_age.times { |i|
49
+ if $rubyrun_throughput[i]
50
+ bar_content += sprintf(THROUGHPUT_BAR_TABLE, sprintf('%0.0f',$rubyrun_throughput[i][1]), ($rubyrun_throughput[i][1]*250/max).to_i)
51
+ label_content += sprintf(THROUGHPUT_LABEL_TABLE, $rubyrun_throughput[i][0].strftime("%H:%M %b&nbsp;%d"))
52
+ else
53
+ bar_content += sprintf(THROUGHPUT_BAR_TABLE, '', 0)
54
+ label_content += sprintf(THROUGHPUT_LABEL_TABLE, '')
55
+ end
56
+ }
57
+ $rubyrun_throughput.reverse!
58
+ html_content = THROUGHPUT_HTML.sub(/%THROUGHPUT_BAR_TABLE%/, bar_content)
59
+ html_content.sub!(/%THROUGHPUT_LABEL_TABLE%/, label_content)
60
+ html_content.sub!(/%APPS_NAME%/,Rails::Configuration.new.root_path.split('/').last)
61
+ html_content.sub!(/%TIMESTAMP%/,Time.now.strftime("%H:%M:%S %m/%d/%Y"))
62
+ # Second, Top Slowest Requests
63
+ results = sort_performance_metrics
64
+ table_content = ''
65
+ 10.times { |i|
66
+ break unless results[i]
67
+ table_content += sprintf(TOP_SLOWEST_REQUESTS_TABLE, results[i][0],
68
+ (150*results[i][1][0]/results[0][1][0]).round, results[i][1][0])
69
+ }
70
+ (html_content << TOP_SLOWEST_REQUESTS_HTML).sub!(/%TOP_SLOWEST_REQUESTS_TABLE%/,table_content)
71
+ # Third, Request Performance Breakdown
72
+ odd = true
73
+ table_content = ''
74
+ results.each { |metrics|
75
+ table_content += sprintf("#{odd ? REQ_PERF_BREAKDOWN_TABLE_ODD : REQ_PERF_BREAKDOWN_TABLE_EVEN}", metrics[0], metrics[1][6], metrics[1][0], metrics[1][1], (metrics[1][1]/metrics[1][0]*100).round, metrics[1][2], (metrics[1][2]/metrics[1][0]*100).round, metrics[1][3], (metrics[1][3]/metrics[1][0]*100).round, metrics[1][5], (metrics[1][5]/metrics[1][0]*100).round, metrics[1][4])
76
+ odd = odd ? false : true
77
+ }
78
+ (html_content << REQ_PERF_BREAKDOWN_HTML).sub!(/%REQ_PERF_BREAKDOWN_TABLE%/,table_content);
79
+ $rubyrun_perf_summary_rss.add_item(RubyRunRSS::RUBYRUN_RSS_PERF_SUMMARY_ITEM_TITLE,
80
+ RubyRunRSS::RUBYRUN_RSS_PERF_SUMMARY_ITEM_DESCRIPTION, html_content)
81
+ end
82
+
83
+ # Create the folder for keeping RSS XML file and HTML files
84
+ # Create the RSS channel
85
+ def create_rss_channels
86
+ rss_folder = $rubyrun_config['RSS_PATH'] ? $rubyrun_config['RSS_PATH'] \
87
+ : "#{Rails::Configuration.new.root_path}/public/#{RubyRunRSS::RUBYRUN_RSS_FOLDER}"
88
+ begin
89
+ Dir.mkdir(rss_folder) unless File.exist?(rss_folder)
90
+ rescue Exception
91
+ rss_folder = @rubyrun_report_folder
92
+ end
93
+ $rubyrun_perf_summary_rss = RubyRunRSS.new \
94
+ RubyRunRSS::RUBYRUN_RSS_PERF_SUMMARY_CHANNEL_TITLE,
95
+ RubyRunRSS::RUBYRUN_RSS_PERF_SUMMARY_CHANNEL_DESCRIPTION,
96
+ rss_folder,
97
+ RubyRunRSS::RUBYRUN_RSS_PERF_SUMMARY_CHANNEL_FILENAME,
98
+ RubyRunRSS::RUBYRUN_RSS_PERF_SUMMARY_CHANNEL_ITEM_FILENAME \
99
+ unless $rubyrun_perf_summary_rss
100
+ $rubyrun_throughput = Array.new
101
+ end
102
+
103
+ # Create the CSV files
104
+ def create_csv_files
105
+ $rubyrun_txn_log_reporter = Logger.new("#{@rubyrun_report_folder}/#{File.basename($0, ".*")}_#{$$.to_s}_txn_log.csv", shift_age = 10, shift_size = 4096000)
106
+ $rubyrun_txn_log_reporter.info "#\n# Format: [URL],[thread ID],[timestamp],[response time],[action time],[database IO time],[view time],[dispatch delay time],[uncaptured time]\n#"
107
+ end
108
+
109
+ end
@@ -0,0 +1,97 @@
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
+ # Dynamic Application Discovery #
9
+ # #
10
+ # Last Updated: 6/03/08 #
11
+ #---------------------------------------------------------------#
12
+ # #
13
+ # RubyRunRSS provides the interfaces for creating RSS channel #
14
+ # and adding items to it. #
15
+ # #
16
+ #---------------------------------------------------------------#
17
+
18
+ require 'rss/maker'
19
+
20
+ class RubyRunRSS
21
+ RUBYRUN_RSS_VERSION = '2.0'
22
+ RUBYRUN_RSS_CHANNEL_URL = 'http://www.rubysophic.com'
23
+ RUBYRUN_RSS_IMAGE_TITLE = 'Learn more about RubyRun from Rubysophic'
24
+ RUBYRUN_RSS_IMAGE_URL = 'http://www.rubysophic.com/images/logo.jpg'
25
+ RUBYRUN_RSS_FOLDER = 'rubyrun_rss'
26
+ RUBYRUN_RSS_PERF_SUMMARY_CHANNEL_TITLE = 'RubyRun: %s Performance Summary'
27
+ RUBYRUN_RSS_PERF_SUMMARY_CHANNEL_ITEM_FILENAME = 'perf_summary_item'
28
+ RUBYRUN_RSS_PERF_SUMMARY_CHANNEL_DESCRIPTION = 'RubyRun delivers up-to-the-minute performance summary of your application.'
29
+ RUBYRUN_RSS_PERF_SUMMARY_CHANNEL_FILENAME = 'perf_summary.xml'
30
+ RUBYRUN_RSS_PERF_SUMMARY_ITEM_TITLE = 'Performance summary at %s'
31
+ RUBYRUN_RSS_PERF_SUMMARY_ITEM_DESCRIPTION = 'Performance summary of %s at %s'
32
+
33
+ # Constructor of the class.
34
+ # RSS XML file and HTML files will be kept in the directory as specified
35
+ def initialize (title, description, directory, rss_filename, html_filename)
36
+ @title = title
37
+ @description = description
38
+ @directory = directory
39
+ @rss_filename = rss_filename
40
+ @html_filename = html_filename
41
+ @apps_name = Rails::Configuration.new.root_path.split('/').last
42
+ @rss_xml_destination = "#{@directory}/#{@rss_filename}"
43
+ create_channel_content unless File::exists?(@rss_xml_destination)
44
+ @rss = load_rss_content
45
+ end
46
+
47
+ # Create the RSS channel
48
+ def create_channel_content
49
+ content = RSS::Maker.make(RUBYRUN_RSS_VERSION) do |m|
50
+ m.channel.title = sprintf(@title, @apps_name)
51
+ m.channel.link = RUBYRUN_RSS_CHANNEL_URL
52
+ m.channel.description = @description
53
+ m.items.do_sort = true # sort items by date
54
+ m.image.title = RUBYRUN_RSS_IMAGE_TITLE
55
+ m.image.width = 140
56
+ m.image.height = 25
57
+ m.image.url = RUBYRUN_RSS_IMAGE_URL
58
+ end
59
+ File.open(@rss_xml_destination,"w") do |f|
60
+ f.write(content)
61
+ end
62
+ content.to_s
63
+ end
64
+
65
+ # Add an item to the RSS channel
66
+ def add_item (title, description, html_content)
67
+ filename = "#{@html_filename}_#{Time.now.to_i}#{rand(1000000)}.html"
68
+ File.open("#{@directory}/#{filename}", 'w') { |file| file.puts html_content }
69
+ item = RSS::Rss::Channel::Item.new
70
+ item.title = sprintf(title, Time.now.strftime("%H:%M:%S"))
71
+ item.description = sprintf(description, @apps_name, Time.now.strftime("%H:%M:%S"))
72
+ item.link = "http://#{$rubyrun_host_with_port}/#{RUBYRUN_RSS_FOLDER}/#{filename}"
73
+ remove_old_item(@rss) if @rss.items.length == $rubyrun_report_shift_age
74
+ @rss.items << item
75
+ File.open(@rss_xml_destination,"w") do |f|
76
+ f.write(@rss)
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ # Load the existing RSS XML file. Create a new channel if the channel is blank
83
+ def load_rss_content
84
+ content = "" # raw content of rss feed will be loaded here
85
+ open(@rss_xml_destination) do |s| content = s.read end
86
+ content = create_channel_content if content == ''
87
+ RSS::Parser.parse(content, false)
88
+ end
89
+
90
+ # Remove the last item in the RSS channel
91
+ def remove_old_item(rss)
92
+ item = rss.items.shift
93
+ filename = "#{@directory}/#{item.link.split('/').last}"
94
+ File.delete(filename) if File.exists?(filename)
95
+ end
96
+
97
+ end
@@ -0,0 +1,79 @@
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
+ # RubyRunTracer__ is the module for tracing a line of data #
13
+ # in the output destination. #
14
+ # #
15
+ # The source of the trace can be data from forward-tracing #
16
+ # or data collected from a stack trace. #
17
+ # #
18
+ #---------------------------------------------------------------#
19
+ module RubyRunTracer__
20
+
21
+ require 'rubyrun_globals'
22
+ require 'rubyrun_utils__'
23
+ require 'rubyrun_html__'
24
+ include RubyRunGlobals
25
+ include RubyRunUtils__
26
+ include RubyRunHTML__
27
+
28
+ # 1. If arguments are required to trace, try using kernel inspect to print it
29
+ # 2. If obejct is required to trace, try using kernel inspect to print it.
30
+ # Otherwise print class name, The inspect can cause recursion and blow up
31
+ # ruby. Rescue only delays the issue hence not used here.
32
+ # 3. Show the last caller and line
33
+ def enter_trace(tid, type, obj, invoker, klass, mid, *args)
34
+ @rubyrun_trace_odd_row = true if @rubyrun_trace_odd_row == nil
35
+ cur_time = Time.now
36
+ html_content = sprintf("#{@rubyrun_trace_odd_row ? METHOD_TRACE_ODD_ROW : METHOD_TRACE_EVEN_ROW}",
37
+ "#{cur_time.strftime("%H:%M:%S")}.#{("%.3f" % cur_time.to_f).split('.')[1]} #{cur_time.strftime("%m/%d/%y")}",
38
+ get_thread_id,
39
+ "#{type.split(' ').reverse.first}",
40
+ "#{type.split(' ').length == 3 ? '#3B9C9C' : (@rubyrun_trace_odd_row ? '#AFDCEC' : 'white')}",
41
+ "#{type.split(' ').length == 1 ? '' : (type.split(' ').length == 3 ? '*'+type.split(' ').reverse[1]+'s' : type.split(' ').reverse[1]+'s')}",
42
+ klass.to_s,
43
+ return_method_name(mid),
44
+ "#{args.each {|arg| arg.inspect} if $rubyrun_debug_args || is_in_hash?($rubyrun_adapter_hash, klass, mid)}",
45
+ "#{$rubyrun_debug_obj && obj ? obj.inspect : obj.class if obj}",
46
+ "#{invoker if invoker}")
47
+ write_trace(html_content)
48
+ @rubyrun_trace_odd_row = !@rubyrun_trace_odd_row
49
+ end
50
+
51
+ # Write a trace entry to the trace destination
52
+ def write_trace(html_content)
53
+ begin
54
+ $rubyrun_tracer.info(html_content)
55
+ rescue Exception => e
56
+ $rubyrun_logger.warn(e.to_s)
57
+ end
58
+ end
59
+
60
+ # The stack trace global hash is printed out in the rubyrun log in
61
+ # thread id order, also showing the top stack of all these
62
+ # threads before they were interrupted.
63
+ def back_trace_all(th_data_hash)
64
+ $rubyrun_thread_dump_reporter.info "----- RubyRun Thread Dump STARTS-----"
65
+ $rubyrun_thread_stack.each {|th, stack|
66
+ $rubyrun_thread_dump_reporter.info
67
+ thread_id = get_thread_id(th)
68
+ $rubyrun_thread_dump_reporter.info "Thread ID = #{thread_id}"
69
+ $rubyrun_thread_dump_reporter.info " Last line before interrupt: #{get_top_stack(th_data_hash, thread_id)}"
70
+ $rubyrun_thread_dump_reporter.info " Stack trace at interrupt"
71
+ stack.each {|line|
72
+ $rubyrun_thread_dump_reporter.info "\t#{line}"
73
+ }
74
+ }
75
+ $rubyrun_thread_dump_reporter.info
76
+ $rubyrun_thread_dump_reporter.info "----- RubyRun Thread Dump ENDS -----"
77
+ $rubyrun_thread_stack.clear
78
+ end
79
+ end
@@ -0,0 +1,101 @@
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
+ # RubyRunUtils__ is a module that owns common methods shared #
13
+ # by all the other RubyRun modules. #
14
+ # #
15
+ #---------------------------------------------------------------#
16
+ module RubyRunUtils__
17
+
18
+ require 'rubyrun_globals'
19
+ include RubyRunGlobals
20
+
21
+ # Return a readable thread ID for the current thread of execution
22
+ def get_thread_id(th=Thread.current)
23
+ th.inspect.split(/Thread:0x/)[1].split(/ .*?>/)[0]
24
+ end
25
+
26
+ # Retrieve caller details (filename, line number, method name)
27
+ def get_caller_detail(n=3)
28
+ caller(0)[n].gsub("#{RUBYRUN_PREFIX}_", '').gsub('//', '/')
29
+ end
30
+
31
+ # Environment variable not defined or defined with nil value is deemed to
32
+ # be non-existent
33
+ def env_var_exists?(var)
34
+ ENV[var].nil? || ENV[var] == '' ? false : true
35
+ end
36
+
37
+ # Error exit
38
+ def fatal_exit(e)
39
+ $stderr.print e.to_s + "\n" + e.backtrace.join("\n")
40
+ exit(-1)
41
+ end
42
+
43
+ # Return true if a Rails Action Controller class
44
+ # A module or object has no superclass hence the rescue clause
45
+ def is_rails_controller?(klass)
46
+ return true if $rubyrun_controller_classes.include?(klass)
47
+ $rubyrun_rails_env ||= ENV['RAILS_ENV']
48
+ begin
49
+ ($rubyrun_controller_classes << klass; return true) if $rubyrun_rails_env &&
50
+ klass.to_s[-10, 10] == 'Controller' && is_application_controller(klass)
51
+ rescue
52
+ end
53
+ false
54
+ end
55
+
56
+ # Return true if this is an action method in a controller class
57
+ def is_action?(klass, mid)
58
+ !klass.private_instance_methods(false).include?(return_method_name(mid))
59
+ end
60
+
61
+ # Given a class, it's deemed to be a Rails Action Controller if one of its
62
+ # ancestors is ApplicationController
63
+ def is_application_controller(klass)
64
+ return false unless klass.superclass
65
+ if klass.superclass == ApplicationController
66
+ return true
67
+ else
68
+ is_application_controller(klass.superclass)
69
+ end
70
+ end
71
+
72
+ # Return false if the passed in hash is empty
73
+ # Return false if the hash doenst even have the class name as a key
74
+ # Return true if the hash has the key but the method array is empty
75
+ # Return true if the method array has a case-insensitive matching name,
76
+ # matching can be exact or 'include'.
77
+ # Otherwise return false
78
+ def is_in?(hash, klass, mid, mode='loose')
79
+ return false if hash.empty?
80
+ [klass.to_s, klass.class.to_s, '*'].each {|name|
81
+ if hash.has_key?(name)
82
+ return true if hash[name].empty?
83
+ method_name = return_method_name(mid)
84
+ hash[name].each {|meth_name|
85
+ case mode
86
+ when 'strict'
87
+ return true if method_name.downcase == meth_name.downcase
88
+ when 'loose'
89
+ return true if method_name.downcase.include?(meth_name.downcase)
90
+ end
91
+ }
92
+ end
93
+ }
94
+ false
95
+ end
96
+
97
+ # Return method name since mid can be an method object ID or a string
98
+ def return_method_name(mid)
99
+ mid.kind_of?(String) ? mid : mid.id2name
100
+ end
101
+ end
Binary file