rubyrun 0.9.5-x86-linux

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.
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