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,49 @@
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
+ # RubyRunBufferMgr__ module is responsible for passing the data #
13
+ # from the inline execution thread to the monitor thread #
14
+ # #
15
+ # Two buffers (primary and secondary) are used and the buffers are #
16
+ # swapped between the inline execution thread and RubyRunMonitor__ #
17
+ # thread periodically as defined by REPORT_TIMER. The inline #
18
+ # execution thread pushes data into the primary buffer, when the #
19
+ # REPORT_TIMER expires, RubyRunMonitor__ thread then takes it over #
20
+ # and gives the inline execution thread the secondary buffer via #
21
+ # a quick swap. The same process keeps repeating as the monitor #
22
+ # timer pops and the primary and secondary keep switched between #
23
+ # the two threads. #
24
+ # #
25
+ #--------------------------------------------------------------------#
26
+ module RubyRunBufferMgr__
27
+
28
+ # Push data into the current buffer
29
+ # Primary or secondary buffer may be used but it is transparent to the caller
30
+ def push_current_buffer(metrics)
31
+ $rubyrun_lock.synchronize {
32
+ ($rubyrun_current_buffer == 1 ? $rubyrun_prime_buffer : $rubyrun_alt_buffer) << metrics
33
+ }
34
+ end
35
+
36
+ # Return the current buffer and swap it with the other one
37
+ # This method is invoked by the consumer of the data in the buffer
38
+ def return_and_switch_buffer()
39
+ $rubyrun_lock.synchronize {
40
+ if ($rubyrun_current_buffer == 1)
41
+ $rubyrun_current_buffer == 2
42
+ $rubyrun_prime_buffer
43
+ else
44
+ $rubyrun_current_buffer == 1
45
+ $rubyrun_alt_buffer
46
+ end
47
+ }
48
+ end
49
+ end
@@ -0,0 +1,196 @@
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/18/08 #
10
+ #-----------------------------------------------------------------#
11
+ # #
12
+ # RubyRunCommander__ is a module which handles the commands #
13
+ # cmd_status, cmd_object_map, cmd_soft_kill and cmd_hard_kill #
14
+ # #
15
+ #-----------------------------------------------------------------#
16
+ module RubyRunCommander__
17
+
18
+ # Use Thread.list to list show thread status, and native code to
19
+ # display the last line # and function of the threads
20
+ def dump_thread_status
21
+ (unsupport_function; return) unless $rubyrun_native
22
+ $rubyrun_thread_status_reporter = RubyRunHTMLWriter.new(@rubyrun_report_folder + '/' + File.basename($0, ".*") + '_' + $$.to_s + '_thread_status.html', nil, shift_age = 10, shift_size = 4096000) unless $rubyrun_thread_status_reporter
23
+ start_time = Time.now
24
+ th_data_hash = RubyRunNative__.get_all_top_stacks
25
+ odd_row ||= true
26
+ table_content = ''
27
+ Thread.list.each {|th|
28
+ thread_id = get_thread_id(th)
29
+ table_content += sprintf("#{odd_row ? THREAD_STATUS_ODD_ROW : THREAD_STATUS_EVEN_ROW}",
30
+ thread_id, th.status, get_top_stack(th_data_hash, thread_id))
31
+ odd_row = !odd_row
32
+ }
33
+ html_content = THREAD_STATUS_HTML.sub(/%START_TIMESTAMP%/,start_time.strftime("%H:%M:%S %m/%d/%Y"))
34
+ html_content.sub!(/%THREAD_STATUS_ROW%/,table_content)
35
+ $rubyrun_thread_status_reporter.info(html_content)
36
+ end
37
+
38
+ # Dump Controller/Actions response time metrics
39
+ # metrics structure
40
+ # metrics[0] Thread ID metrics[1] Timestamp of the request
41
+ # metrics[2] URL metrics[3] Controller name
42
+ # metrics[4] Action name metrics[5] Response time
43
+ # metrics[6] Action time metrics[7] Database IO time
44
+ # metrics[8] View time metrics[9] Uncaptured time
45
+ # metrics[10] Dispatch wait time
46
+ def dump_reports(dump_all_reports = false)
47
+ buffer = return_and_switch_buffer
48
+ buffer.each { |metrics|
49
+ # Last element is 1, representing a request count of 1, used for calculating average response time for this controller/action
50
+ update_perf_metrics(metrics[3], {metrics[4] => [metrics[5],metrics[6],metrics[7],metrics[8],metrics[9],metrics[10],1]})
51
+ }
52
+ @rubyrun_req_count ||= 0
53
+ @rubyrun_req_count += buffer.length
54
+ if $rubyrun_config['OUTPUT'].include?(RUBYRUN_OUTPUT_PERF_SUMMARY) && dump_all_reports
55
+ create_rss_channels if (!$rubyrun_perf_summary_rss && $rubyrun_rails_env)
56
+ add_perf_summary_rss_item(@rubyrun_req_count)
57
+ @rubyrun_req_count = 0
58
+ end
59
+ if $rubyrun_config['OUTPUT'].include?(RUBYRUN_OUTPUT_TXN_LOG)
60
+ create_csv_files unless $rubyrun_txn_log_reporter
61
+ add_txn_log_csv_item(buffer)
62
+ end
63
+ buffer.clear # Clear the buffer so that the main thread will push the data into a blank bucket
64
+ end
65
+
66
+ # The way to do soft/hard kill is to performa a thr.raise on the thread
67
+ # from the thread monitor. Using the begin/rescue created around the block
68
+ # in Thread.new by RubyRunInstrumentor__, the raise will be rescued
69
+ # and $@ is then extracted to a global hash.
70
+ # Softkill only kills non-main threads. Hardkill kills the main thread also
71
+ # but as the last step.
72
+ def kill_threads(monitor_thr)
73
+ (unsupport_function; return) unless $rubyrun_native
74
+ if !$rubyrun_thread_dump_reporter
75
+ $rubyrun_thread_dump_reporter = Logger.new(@rubyrun_report_folder + '/' + File.basename($0, ".*") + '_' + $$.to_s + '_thread_dump.txt', shift_age = 10, shift_size = 4096000)
76
+ $rubyrun_thread_dump_reporter.level = Logger::INFO
77
+ class << $rubyrun_thread_dump_reporter
78
+ include RubyRunUtils__
79
+ def format_message (severity, timestamp, progname, msg)
80
+ "[#{timestamp.strftime("%Y-%m-%d %H:%M:%S")}.#{("%.3f" % timestamp.to_f).split('.')[1]}] #{get_thread_id} #{msg}\n"
81
+ end
82
+ end
83
+ end
84
+ th_data_hash = RubyRunNative__.get_all_top_stacks
85
+ j_th_id = return_joined_thread(th_data_hash)
86
+ Thread.list.each {|th|
87
+ th_id = get_thread_id(th)
88
+ if th.status == 'sleep' && th_id != get_thread_id && th_id != get_thread_id(monitor_thr) &&
89
+ th_id != get_thread_id(Thread.main) && th_id != j_th_id
90
+ $rubyrun_thread_dump_reporter.info "*** Raising exception #{RUBYRUN_KILL_3_STRING} to #{get_thread_id(th)} ***"
91
+ th.raise ThreadError, RUBYRUN_KILL_3_STRING
92
+ end
93
+ }
94
+ sleep 3
95
+ back_trace_all(th_data_hash)
96
+ hard_kill = hard_kill?
97
+ remove_cmd_folder
98
+ Thread.main.raise ThreadError, RUBYRUN_KILL_3_STRING if hard_kill
99
+ end
100
+
101
+ # Show the top 20 Ruby classes which have the largest no. of instances in memory
102
+ # The snapshot is taken after a gc call is made
103
+ def dump_object_map
104
+ start_time = Time.now
105
+ $rubyrun_obj_map_reporter = RubyRunHTMLWriter.new(@rubyrun_report_folder + '/' + File.basename($0, ".*") + '_' + $$.to_s + '_object_map.html', nil, shift_age = 10, shift_size = 4096000) unless $rubyrun_obj_map_reporter
106
+ object_map = Hash.new
107
+ ttl_object = 0
108
+ ObjectSpace.garbage_collect
109
+ ObjectSpace.each_object { |obj|
110
+ ttl_object += 1
111
+ object_map.has_key?(obj.class) ? object_map[obj.class] += 1 : object_map[obj.class] = 1
112
+ }
113
+ results = object_map.sort{|a,b| a[1]<=>b[1]}.reverse!
114
+ table_content = ''
115
+ odd_row ||=true
116
+ 20.times {|i|
117
+ table_content += sprintf("#{odd_row ? OBJ_MAP_ODD_ROW : OBJ_MAP_EVEN_ROW}",
118
+ results[i][0], results[i][1].to_s)
119
+ odd_row = !odd_row
120
+ }
121
+ html_content = OBJ_MAP_HTML.sub(/%START_TIMESTAMP%/,start_time.strftime("%H:%M:%S %m/%d/%Y"))
122
+ html_content.sub!(/%OBJ_MAP_ROW%/,table_content)
123
+ $rubyrun_obj_map_reporter.info(html_content)
124
+ end
125
+
126
+ # metrics hash is a global collection point for all metrics (averaged)
127
+ # for all actions by controller
128
+ # Use serialization before updating this global hash
129
+ # Structure of $rubyrun_metrics_hash:
130
+ # controller_name => {action_name => [response_time, action_time,
131
+ # db_io_time, view_time,
132
+ # uncaptured_time, dispatch_wait_time,
133
+ # request_count]}
134
+ def update_perf_metrics(controller, action_metrics_hash)
135
+ $rubyrun_metrics_hash[controller].merge!(action_metrics_hash) {|action, o_metrics, new_metrics|
136
+ o_metrics.each_index { |x|
137
+ (o_metrics[x] += new_metrics[x]; break) if x == (o_metrics.length-1) # Calculate the total request count for this controller/action
138
+ o_metrics[x] = (o_metrics[x] * o_metrics.last + new_metrics[x])/(o_metrics.last + new_metrics.last).to_f
139
+ }
140
+ o_metrics
141
+ }
142
+ end
143
+
144
+ # If a thread is joined this method returns the joining thread ID
145
+ def return_joined_thread(th_data_hash)
146
+ th_data_hash.each {|th, top_stack|
147
+ if th.to_s.include?(get_thread_id(Thread.main))
148
+ top_stack[0] =~ /\*\*(.+?)\*\*/
149
+ return $1
150
+ end
151
+ }
152
+ end
153
+
154
+ # Remove the cmd_kill-3 folder or file if any
155
+ def remove_cmd_folder
156
+ [RUBYRUN_CMD_SOFT_KILL, RUBYRUN_CMD_HARD_KILL].each { |cmd|
157
+ path = $rubyrun_working_dir + cmd + '_' + Process.pid.to_s
158
+ next unless File.exist?(path)
159
+ File.directory?(path) ? Dir.delete(path) : File.delete(path)
160
+ }
161
+ end
162
+
163
+ # If exists, indicate to the monitor thread that a thread
164
+ # status report is requested
165
+ def thread_status?
166
+ File.exists?($rubyrun_working_dir + RUBYRUN_CMD_STATUS)
167
+ end
168
+
169
+ # If exists, indicate to the monitor thread that a soft kill
170
+ # (kill all threads except the main thread) command is sent
171
+ def soft_kill?
172
+ File.exists?($rubyrun_working_dir + RUBYRUN_CMD_SOFT_KILL + '_' + Process.pid.to_s)
173
+ end
174
+
175
+ # If exists, indicate to the monitor thread that a hard kill
176
+ # (kill all threads including the main thread) command is sent
177
+ def hard_kill?
178
+ File.exists?($rubyrun_working_dir + RUBYRUN_CMD_HARD_KILL + '_' + Process.pid.to_s)
179
+ end
180
+
181
+ # If exists, indicate to the montior thread that a object map is requested
182
+ def object_map?
183
+ File.exists?($rubyrun_working_dir + RUBYRUN_CMD_OBJECT_MAP)
184
+ end
185
+
186
+ # If exists, indicate to the monitor thread to exit
187
+ def exit_monitor?
188
+ File.exists?($rubyrun_working_dir + RUBYRUN_CMD_EXIT)
189
+ end
190
+
191
+ # Log if native library can't be loaded or not found
192
+ def unsupport_function
193
+ $rubyrun_logger.info "Native library not available. Function not supported."
194
+ end
195
+
196
+ end
@@ -0,0 +1,35 @@
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: 7/09/08 #
11
+ #---------------------------------------------------------------#
12
+ # #
13
+ # Dynamic Application Discovery lists out the name of a method #
14
+ # as it is being added to the ruby process. #
15
+ # #
16
+ #---------------------------------------------------------------#
17
+ module RubyRunDad__
18
+
19
+ # perform dynamic application discovery if requested by printing
20
+ # out the class and method name of the method being added
21
+ def get_dad(type, klass, id)
22
+ print_method_added(type, klass, id) if $rubyrun_dad
23
+ end
24
+
25
+ private
26
+
27
+ # print the dynamic method added message
28
+ def print_method_added(type, obj, id)
29
+ if type == 'i'
30
+ $rubyrun_logger.info "#{obj.to_s}.#{id.id2name} added as an instance method"
31
+ else
32
+ $rubyrun_logger.info "#{obj.to_s}.#{id.id2name} added as an singleton method"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,52 @@
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
+ # Provide CONSTANTS for RubyRun modules #
13
+ # #
14
+ #---------------------------------------------------------------#
15
+ module RubyRunGlobals
16
+
17
+ RUBYRUN_PREFIX = 'rubyrunX'
18
+ RUBYRUN_PREFIX_LENGTH = RUBYRUN_PREFIX.length
19
+ RUBYRUN_HIGHLIGHT_THRESHOLD = 1
20
+ RUBYRUN_FIREWALL_HASH = {"Gem" => [], "gem" => [], "FileUtils::Verbose" => [], "FileUtils" => [],
21
+ "Module" => [], "Object" => [], "Time" => [], "Logger" => [], "Thread" => [],
22
+ "*" => ['method_added', 'initialize', 'singleton_method_added', 'send', 'caller']}
23
+ RUBYRUN_WORKING_DIR_NAME = 'rubyrun'
24
+ RUBYRUN_ETC_DIR = 'etc'
25
+ RUBYRUN_LOG = '/log'
26
+ RUBYRUN_REPORT = '/report'
27
+ RUBYRUN_SIGNATURE = '/signatures'
28
+ RUBYRUN_INCLUDE_HASH_FILE = '/include_hash_file'
29
+ RUBYRUN_EXCLUDE_HASH_FILE = '/exclude_hash_file'
30
+ RUBYRUN_DIR_HASH_FILE = '/dir_hash_file'
31
+ RUBYRUN_CMD_SOFT_KILL = '/cmd_soft_kill'
32
+ RUBYRUN_CMD_HARD_KILL = '/cmd_hard_kill'
33
+ RUBYRUN_CMD_STATUS = '/cmd_status'
34
+ RUBYRUN_CMD_EXIT = '/cmd_exit'
35
+ RUBYRUN_CMD_OBJECT_MAP = '/cmd_object_map'
36
+ RUBYRUN_MONITOR_TIMER = 10
37
+ RUBYRUN_OUTPUT_PERF_SUMMARY = 'perf_summary'
38
+ RUBYRUN_OUTPUT_TXN_LOG = 'txn_log'
39
+ RUBYRUN_KILL_3_STRING = 'RUBYRUN kill -3'
40
+ RUBYRUN_OPTS_FILE = '/rubyrun_opts.yml'
41
+ RUBYRUN_PROP_DEFAULTS = {"APP_PATHS"=>[], "EXCLUDE_HASH"=>{}, "INCLUDE_HASH"=>{},
42
+ "DEBUG_ARGS"=>false, "DEBUG_OBJ"=>false, "DAD"=>false,
43
+ "REPORT_TIMER"=>60, "REPORT_SHIFT_AGE"=>60,
44
+ "OUTPUT" => ["#{RUBYRUN_OUTPUT_PERF_SUMMARY}","#{RUBYRUN_OUTPUT_TXN_LOG}","#{RUBYRUN_OUTPUT_PERF_SUMMARY}"],
45
+ "RSS_PATH" => nil, "TRACE_HASH"=>{}, "DB_ADAPTER_HASH"=>{}}
46
+ RUBYRUN_ACTIVERECORD = 'ActiveRecord::Base'
47
+ RUBYRUN_VIEW_HASH = {'ActionView::Base' => ['pick_template_extension','render_file']}
48
+ RUBYRUN_THREAD_END_HASH = {'WEBrick::HTTPServer' => ['run'], 'Mongrel::HttpServer' => ['process_client']}
49
+ RUBYRUN_OUTER_DISPATCH_HASH = {'Mongrel::Rails::RailsHandler' => ['process'], 'DispatchServlet' => ['service']}
50
+ RUBYRUN_INNER_DISPATCH_HASH = { 'Dispatcher' => ['dispatch'], 'ActionController::Dispatcher' => ['dispatch']}
51
+
52
+ end
@@ -0,0 +1,136 @@
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/08/08 #
10
+ #-----------------------------------------------------------------#
11
+ # #
12
+ # RubyRunHTML__ defines HTML templates and assigns them to #
13
+ # various constants to be used by other reporter modules. #
14
+ # The templates include thread status, method trace and RSS #
15
+ # content. #
16
+ # #
17
+ #-----------------------------------------------------------------#
18
+ module RubyRunHTML__
19
+ OBJ_MAP_HTML = "<table cellspacing='2' width='55%'>
20
+ <tr align='middle' bgcolor='#43bfc7'>
21
+ <th colspan='2'><font face='Helvetica' size='2' color='white'>Top 20 Ruby classes with the largest number of objects (%START_TIMESTAMP%)</font></th></tr>
22
+ <tr align='middle' bgcolor='#43bfc7'>
23
+ <th><font face='Helvetica' size='2' color='white'>Class name</font></th>
24
+ <th><font face='Helvetica' size='2' color='white'>Number of objects</font></th>
25
+ %OBJ_MAP_ROW%
26
+ </table><br></br>"
27
+ OBJ_MAP_ODD_ROW = "<tr align='middle'>
28
+ <td align='left' bgcolor='#afdcec'><font face='Tahoma' size='2' color='black'>%s</font></td>
29
+ <td align='left' bgcolor='#afdcec'><font face='Tahoma' size='2' color='black'>%s</font></td>"
30
+ OBJ_MAP_EVEN_ROW = "<tr align='middle'>
31
+ <td align='left'><font face='Tahoma' size='2' color='black'>%s</font></td>
32
+ <td align='left'><font face='Tahoma' size='2' color='black'>%s</font></td>"
33
+ THREAD_STATUS_HTML = "<table cellspacing='2' width='100%'>
34
+ <tr align='middle' bgcolor='#43bfc7'>
35
+ <th colspan='3'><font face='Helvetica' size='2' color='white'>RubyRun Thread Status Starts (%START_TIMESTAMP%)</font></th>
36
+ <tr align='middle' bgcolor='#43bfc7'>
37
+ <th><font face='Helvetica' size='2' color='white'>Thread ID</font></th>
38
+ <th><font face='Helvetica' size='2' color='white'>Status</font></th>
39
+ <th><font face='Helvetica' size='2' color='white'>Top of the stack</font></th>
40
+ %THREAD_STATUS_ROW%
41
+ </table><br></br>"
42
+ THREAD_STATUS_ODD_ROW = "<tr align='middle'>
43
+ <td bgcolor='#afdcec'><font face='Tahoma' size='2' color='black'>%s</font></td>
44
+ <td bgcolor='#afdcec'><font face='Tahoma' size='2' color='black'>%s</font></td>
45
+ <td align='left' bgcolor='#afdcec'><font face='Tahoma' size='2' color='black'>%s</font></td>"
46
+ THREAD_STATUS_EVEN_ROW = "<tr align='middle'>
47
+ <td><font face='Tahoma' size='2' color='black'>%s</font></td>
48
+ <td><font face='Tahoma' size='2' color='black'>%s</font></td>
49
+ <td align='left'><font face='Tahoma' size='2' color='black'>%s</font></td></tr>"
50
+
51
+ METHOD_TRACE_HEADER = "<table cellspacing=2 width=100%>
52
+ <tr align=center bgcolor=#43BFC7>
53
+ <th><font face=Helvetica size=2 color=white>Time</font></th>
54
+ <th><font face=Helvetica size=2 color=white>Thread ID</font></th>
55
+ <th><font face=Helvetica size=2 color=white>Method Entry/Exit</font></th>
56
+ <th><font face=Helvetica size=2 color=white>Time Taken</font></th>
57
+ <th><font face=Helvetica size=2 color=white>Class</font></th>
58
+ <th><font face=Helvetica size=2 color=white>Method</font></th>
59
+ <th><font face=Helvetica size=2 color=white>Parameter Value(s)</font></th>
60
+ <th><font face=Helvetica size=2 color=white>Caller Object Class</font></th>
61
+ <th><font face=Helvetica size=2 color=white>Caller Method</font></th></tr>"
62
+ METHOD_TRACE_ODD_ROW = "<tr align=center>
63
+ <td bgcolor=#AFDCEC><font face='Tahoma' size=2 color=black>%s</font></td>
64
+ <td bgcolor=#AFDCEC><font face='Tahoma' size=2 color=black>%s</font></td>
65
+ <td bgcolor=#AFDCEC><font face='Tahoma' size=2 color=black>%s</font></td>
66
+ <td bgcolor=%s><font face='Tahoma' size=2 color=black>%s</font></td>
67
+ <td align=left bgcolor=#AFDCEC><font face='Tahoma' size=2 color=black>%s</font></td>
68
+ <td align=left bgcolor=#AFDCEC><font face='Tahoma' size=2 color=black>%s</font></td>
69
+ <td align=left bgcolor=#AFDCEC><font face='Tahoma' size=2 color=black>%s</font></td>
70
+ <td align=left bgcolor=#AFDCEC><font face='Tahoma' size=2 color=black>%s</font></td>
71
+ <td align=left bgcolor=#AFDCEC><font face='Tahoma' size=2 color=black>%s</font></td></tr>"
72
+ METHOD_TRACE_EVEN_ROW = "<tr align=center>
73
+ <td><font face='Tahoma' size=2 color=black>%s</font></td>
74
+ <td><font face='Tahoma' size=2 color=black>%s</font></td>
75
+ <td><font face='Tahoma' size=2 color=black>%s</font></td>
76
+ <td bgcolor=%s><font face='Tahoma' size=2 color=black>%s</font></td>
77
+ <td align=left><font face='Tahoma' size=2 color=black>%s</font></td>
78
+ <td align=left><font face='Tahoma' size=2 color=black>%s</font></td>
79
+ <td align=left><font face='Tahoma' size=2 color=black>%s</font></td>
80
+ <td align=left><font face='Tahoma' size=2 color=black>%s</font></td>
81
+ <td align=left><font face='Tahoma' size=2 color=black>%s</font></td></tr>"
82
+
83
+ THROUGHPUT_HTML = "<p align=center><font size='3' face='Verdana'>Performance summary of %APPS_NAME% as of %TIMESTAMP%</font></p>
84
+ <table id=tblgraph align=center width=80 cellpadding=2 cellspacing=0 border=0>
85
+ <tr><td bgcolor=WHITE align=center valign=middle width=22 style='writing-mode:tb-rl'><font face=arial size='-1'>Average throughput per min</font></td>%THROUGHPUT_BAR_TABLE%</tr>
86
+ <tr bgcolor=#505050>
87
+ <td align=center bordor=1 bgcolor=#FFFFFF>&nbsp;</td>%THROUGHPUT_LABEL_TABLE%
88
+ <td bgcolor=white align=center><font face=arial size='-2' color=black>Time</font></td>
89
+ </tr></table>"
90
+ THROUGHPUT_BAR_TABLE = "<td align=center valign=bottom width=22>
91
+ <font face=arial size='-2'>%s</font><br>
92
+ <div style='writing-mode:tb-rl; background-color:firebrick; width:30; height:%d;' />
93
+ </td>"
94
+ THROUGHPUT_LABEL_TABLE = "<td align=center><font face=arial size='-2' color=white>%s</font></td>"
95
+ TOP_SLOWEST_REQUESTS_HTML ="<p><font size='2' face='Verdana'>Top 10 Slowest Requests</font></p>
96
+ <table cellspacing=0 width=50%%><tr bgcolor=FF6633>
97
+ <th><font face=Helvetica size=2 color=#FFFFFF>Controller/Action<font></th>
98
+ <th><font face=Helvetica size=2 color=#FFFFFF>Response Time<font></th></tr>
99
+ %TOP_SLOWEST_REQUESTS_TABLE%
100
+ </table>"
101
+ TOP_SLOWEST_REQUESTS_TABLE="<tr><td align=left bgcolor=585858><font face=Helvetica size=2 color=white>%s</font></td>
102
+ <td><table><tr><td bgcolor=firebrick><div style='writing-mode:tb-rl; background-color:firebrick; width:%d; height:5;' /></td><td><font face=Helvetica SIZE=1>%0.3fs</font></td></tr></table></td></tr>"
103
+ REQ_PERF_BREAKDOWN_HTML ="<p><font size='2' face='Verdana'>Request Performance Breakdown</font></p>
104
+ <table cellspacing=0 width=100%>
105
+ <tr bgcolor=FF6633>
106
+ <th><font face=Helvetica size=2 color=#FFFFFF>Controller/Action</font></th>
107
+ <th><font face=Helvetica size=2 color=#FFFFFF>Request Count</font></th>
108
+ <th><font face=Helvetica size=2 color=#FFFFFF>Response Time</font></th>
109
+ <th><font face=Helvetica size=2 color=#FFFFFF>Action Time</font></th>
110
+ <th><font face=Helvetica size=2 color=#FFFFFF>Database IO Time</font></th>
111
+ <th><font face=Helvetica size=2 color=#FFFFFF>View Time</font></th>
112
+ <th><font face=Helvetica size=2 color=#FFFFFF>Dispatch Delay Time</font></th>
113
+ <th><font face=Helvetica size=2 color=#FFFFFF>Uncaptured Time</font></th>
114
+ </tr>%REQ_PERF_BREAKDOWN_TABLE%</table>
115
+ <font size='1' face='Verdana'>Note: Process components are results of functional decomposition which overlap each other. As a result, times do not add up to 100%. The hotspots of performance slowdowns, however, are easily accountable from functional standpoint.</font><br></br>"
116
+ REQ_PERF_BREAKDOWN_TABLE_ODD='<tr align=center>
117
+ <td align=left bgcolor=585858><font face=Helvetica size=2 color=white>%s</font></td>
118
+ <td bgcolor=C0C0C0><font face=Helvetica size=2 color=black>%d</font></td>
119
+ <td bgcolor=C0C0C0><font face=Helvetica size=2 color=black>%0.3fs</font></td>
120
+ <td bgcolor=C0C0C0><font face=Helvetica size=2 color=black>%0.3fs (%d%%)</font></td>
121
+ <td bgcolor=C0C0C0><font face=Helvetica size=2 color=black>%0.3fs (%d%%)</font></td>
122
+ <td bgcolor=C0C0C0><font face=Helvetica size=2 color=black>%0.3fs (%d%%)</font></td>
123
+ <td bgcolor=C0C0C0><font face=Helvetica size=2 color=black>%0.3fs (%d%%)</font></td>
124
+ <td bgcolor=C0C0C0><font face=Helvetica size=2 color=black>%0.3fs</font></td>
125
+ </tr>'
126
+ REQ_PERF_BREAKDOWN_TABLE_EVEN='<tr align=center>
127
+ <td align=left bgcolor=585858><font face=Helvetica size=2 color=white>%s</font></td>
128
+ <td><font face=Helvetica size=2 color=black>%d</font></td>
129
+ <td><font face=Helvetica size=2 color=black>%0.3fs </font></td>
130
+ <td><font face=Helvetica size=2 color=black>%0.3fs (%d%%)</font></td>
131
+ <td><font face=Helvetica size=2 color=black>%0.3fs (%d%%)</font></td>
132
+ <td><font face=Helvetica size=2 color=black>%0.3fs (%d%%)</font></td>
133
+ <td><font face=Helvetica size=2 color=black>%0.3fs (%d%%)</font></td>
134
+ <td><font face=Helvetica size=2 color=black>%0.3fs</font></td>
135
+ </tr>'
136
+ end
@@ -0,0 +1,64 @@
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/8/08 #
10
+ #----------------------------------------------------------------#
11
+ # #
12
+ # RubyRunHTMLWriter extends Ruby Logger. It makes use of the #
13
+ # rotating mechanism in Logger and it is used for writing the #
14
+ # reports in HTML format. The add_log_header method is overidden #
15
+ # because by default, Ruby Logger writes a header at the #
16
+ # beginning of each file it creates and such a header is not #
17
+ # required in the RubyRun reports. #
18
+ # #
19
+ #----------------------------------------------------------------#
20
+ require 'logger'
21
+
22
+ class RubyRunHTMLWriter < Logger
23
+
24
+ def initialize(logdev, header, shift_age = 0, shift_size = 1048576)
25
+ @progname = nil
26
+ @level = DEBUG
27
+ @default_formatter = Formatter.new
28
+ @formatter = nil
29
+ @logdev = nil
30
+ if logdev
31
+ @logdev = RubyRunHTMLDevice.new(logdev, header, :shift_age => shift_age,
32
+ :shift_size => shift_size)
33
+ end
34
+ end
35
+
36
+ def format_message (severity, timestamp, progname, msg)
37
+ msg
38
+ end
39
+
40
+ class RubyRunHTMLDevice < Logger::LogDevice
41
+
42
+ def initialize(log = nil, header = nil, opt = {})
43
+ @header = header
44
+ @dev = @filename = @shift_age = @shift_size = nil
45
+ @mutex = LogDeviceMutex.new
46
+ if log.respond_to?(:write) and log.respond_to?(:close)
47
+ @dev = log
48
+ else
49
+ @dev = open_logfile(log)
50
+ @dev.sync = true
51
+ @filename = log
52
+ @shift_age = opt[:shift_age] || 7
53
+ @shift_size = opt[:shift_size] || 1048576
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def add_log_header(file)
60
+ file.write(@header) if @header
61
+ end
62
+ end
63
+
64
+ end