Module: RubyRunCommander__

Included in:
RubyRunMonitor__
Defined in:
lib/rubyrun/rubyrun_commander__.rb

Overview

—————————————————————–#

                                                               #  
(C) Copyright Rubysophic Inc. 2007-2008                        #
All rights reserved.                                           #
                                                               #  
Use, duplication or disclosure of the code is not permitted    #
unless licensed.                                               #
                                                               #            
Last Updated: 7/18/08                                          #

—————————————————————–#

#

RubyRunCommander__ is a module which handles the commands # cmd_status, cmd_object_map, cmd_soft_kill and cmd_hard_kill #

#

—————————————————————–#

Instance Method Summary collapse

Instance Method Details

#dump_object_mapObject

Show the top 20 Ruby classes which have the largest no. of instances in memory The snapshot is taken after a gc call is made



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/rubyrun/rubyrun_commander__.rb', line 105

def dump_object_map
  start_time = Time.now
  $rubyrun_obj_map_reporter = RubyRunHTMLWriter.new($rubyrun_report_folder + '/' + $rubyrun_file_basename + '_' + $rubyrun_startup_id + '_object_map.html', nil, shift_age = 10, shift_size = 4096000) unless $rubyrun_obj_map_reporter
  object_map = Hash.new
  ttl_object = 0
  ObjectSpace.garbage_collect
  ObjectSpace.each_object { |obj|
    ttl_object += 1
    object_map.has_key?(obj.class) ? object_map[obj.class] += 1 : object_map[obj.class] = 1        
  }
  results = object_map.sort{|a,b| a[1]<=>b[1]}.reverse!
  table_content = ''
  odd_row ||=true
  20.times {|i|
    table_content += sprintf("#{odd_row ? OBJ_MAP_ODD_ROW : OBJ_MAP_EVEN_ROW}",
                            results[i][0], results[i][1].to_s)
    odd_row = !odd_row
  }
  html_content = OBJ_MAP_HTML.sub(/%START_TIMESTAMP%/,start_time.strftime("%H:%M:%S %m/%d/%Y"))
  html_content.sub!(/%OBJ_MAP_ROW%/,table_content)
  $rubyrun_obj_map_reporter.info(html_content)
end

#dump_reports(dump_all_reports = false) ⇒ Object

Dump Controller/Actions response time metrics metrics structure metrics Thread ID metrics Timestamp of the request metrics URL metrics Controller name metrics Action name metrics Response time metrics Action time metrics Database IO time metrics View time metrics Uncaptured time metrics Dispatch wait time



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/rubyrun/rubyrun_commander__.rb', line 46

def dump_reports(dump_all_reports = false)
  buffer = return_and_switch_buffer
  buffer.each { |metrics|
    # Last element is 1, representing a request count of 1, used for calculating average response time for this controller/action
    update_perf_metrics(metrics[3], {metrics[4] => [metrics[5],metrics[6],metrics[7],metrics[8],metrics[9],metrics[10],1]})
  }
  @rubyrun_req_count ||= 0
  @rubyrun_req_count += buffer.length
  if $rubyrun_config['OUTPUT'].include?(RUBYRUN_OUTPUT_PERF_SUMMARY)
    create_rss_channels if (!$rubyrun_perf_summary_rss && $rubyrun_rails_env)
    if dump_all_reports
      add_perf_summary_rss_item(@rubyrun_req_count)
      @rubyrun_req_count = 0
    end
  end
  if $rubyrun_config['OUTPUT'].include?(RUBYRUN_OUTPUT_TXN_LOG)
    create_csv_files unless $rubyrun_txn_log_reporter
    add_txn_log_csv_item(buffer)
  end
  buffer.clear # Clear the buffer so that the main thread will push the data into a blank bucket
end

#dump_thread_statusObject

Use Thread.list to list show thread status, and native code to display the last line # and function of the threads



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/rubyrun/rubyrun_commander__.rb', line 20

def dump_thread_status
  (unsupport_function; return) unless $rubyrun_native
  $rubyrun_thread_status_reporter = RubyRunHTMLWriter.new($rubyrun_report_folder + '/' + $rubyrun_file_basename + '_' + $rubyrun_startup_id + '_thread_status.html', nil, shift_age = 10, shift_size = 4096000) unless $rubyrun_thread_status_reporter
  start_time = Time.now
  th_data_hash = RubyRunNative__.get_all_top_stacks
  odd_row ||= true
  table_content = ''
  Thread.list.each {|th|
    thread_id = get_thread_id(th)
    table_content += sprintf("#{odd_row ? THREAD_STATUS_ODD_ROW : THREAD_STATUS_EVEN_ROW}",
                            thread_id, th.status, get_top_stack(th_data_hash, thread_id))
    odd_row = !odd_row
  }
  html_content = THREAD_STATUS_HTML.sub(/%START_TIMESTAMP%/,start_time.strftime("%H:%M:%S %m/%d/%Y"))
  html_content.sub!(/%THREAD_STATUS_ROW%/,table_content)
  $rubyrun_thread_status_reporter.info(html_content)
end

#exit_monitor?Boolean

If exists, indicate to the monitor thread to exit

Returns:

  • (Boolean)


189
190
191
# File 'lib/rubyrun/rubyrun_commander__.rb', line 189

def exit_monitor?
  File.exists?($rubyrun_working_dir + RUBYRUN_CMD_EXIT)   
end

#hard_kill?Boolean

If exists, indicate to the monitor thread that a hard kill (kill all threads including the main thread) command is sent

Returns:

  • (Boolean)


179
180
181
# File 'lib/rubyrun/rubyrun_commander__.rb', line 179

def hard_kill?
  File.exists?($rubyrun_working_dir + RUBYRUN_CMD_HARD_KILL + '_' + Process.pid.to_s)   
end

#kill_threads(monitor_thr) ⇒ Object

The way to do soft/hard kill is to performa a thr.raise on the thread from the thread monitor. Using the begin/rescue created around the block in Thread.new by RubyRunInstrumentor__, the raise will be rescued and $@ is then extracted to a global hash. Softkill only kills non-main threads. Hardkill kills the main thread also but as the last step.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/rubyrun/rubyrun_commander__.rb', line 74

def kill_threads(monitor_thr)
  (unsupport_function; return) unless $rubyrun_native
  if !$rubyrun_thread_dump_reporter
    $rubyrun_thread_dump_reporter = Logger.new($rubyrun_report_folder + '/' + $rubyrun_file_basename + '_' + $rubyrun_startup_id + '_thread_dump.txt', shift_age = 10, shift_size = 4096000)
    $rubyrun_thread_dump_reporter.level = Logger::INFO
    class << $rubyrun_thread_dump_reporter
      include RubyRunUtils__
      def format_message (severity, timestamp, progname, msg)
        "[#{timestamp.strftime("%Y-%m-%d %H:%M:%S")}.#{("%.3f" % timestamp.to_f).split('.')[1]}] #{get_thread_id} #{msg}\n"
      end
    end
  end
  th_data_hash = RubyRunNative__.get_all_top_stacks 
  j_th_id = return_joined_thread(th_data_hash)
  Thread.list.each {|th|
    th_id = get_thread_id(th)
    if th.status == 'sleep' && th_id != get_thread_id && th_id != get_thread_id(monitor_thr) &&
      th_id != get_thread_id(Thread.main) && th_id != j_th_id
      $rubyrun_thread_dump_reporter.info "*** Raising exception #{RUBYRUN_KILL_3_STRING} to #{get_thread_id(th)} ***"
      th.raise ThreadError, RUBYRUN_KILL_3_STRING
    end
  }
  sleep 3
  back_trace_all(th_data_hash)
  hard_kill = hard_kill?
  remove_cmd_folder     
  Thread.main.raise ThreadError, RUBYRUN_KILL_3_STRING if hard_kill    
end

#object_map?Boolean

If exists, indicate to the montior thread that a object map is requested

Returns:

  • (Boolean)


184
185
186
# File 'lib/rubyrun/rubyrun_commander__.rb', line 184

def object_map?
  File.exists?($rubyrun_working_dir + RUBYRUN_CMD_OBJECT_MAP)   
end

#remove_cmd_folderObject

Remove the cmd_kill-3 folder or file if any



157
158
159
160
161
162
163
# File 'lib/rubyrun/rubyrun_commander__.rb', line 157

def remove_cmd_folder
  [RUBYRUN_CMD_SOFT_KILL, RUBYRUN_CMD_HARD_KILL].each { |cmd|
    path = $rubyrun_working_dir + cmd + '_' + Process.pid.to_s
    next unless File.exist?(path)
    File.directory?(path) ? Dir.delete(path) : File.delete(path)          
  }
end

#return_joined_thread(th_data_hash) ⇒ Object

If a thread is joined this method returns the joining thread ID



147
148
149
150
151
152
153
154
# File 'lib/rubyrun/rubyrun_commander__.rb', line 147

def return_joined_thread(th_data_hash)
  th_data_hash.each {|th, top_stack|
    if th.to_s.include?(get_thread_id(Thread.main))
      top_stack[0] =~ /\*\*(.+?)\*\*/
      return $1
    end
  }
end

#soft_kill?Boolean

If exists, indicate to the monitor thread that a soft kill (kill all threads except the main thread) command is sent

Returns:

  • (Boolean)


173
174
175
# File 'lib/rubyrun/rubyrun_commander__.rb', line 173

def soft_kill?
  File.exists?($rubyrun_working_dir + RUBYRUN_CMD_SOFT_KILL + '_' + Process.pid.to_s)   
end

#thread_status?Boolean

If exists, indicate to the monitor thread that a thread status report is requested

Returns:

  • (Boolean)


167
168
169
# File 'lib/rubyrun/rubyrun_commander__.rb', line 167

def thread_status?
  File.exists?($rubyrun_working_dir + RUBYRUN_CMD_STATUS)   
end

#unsupport_functionObject

Log if native library can’t be loaded or not found



194
195
196
# File 'lib/rubyrun/rubyrun_commander__.rb', line 194

def unsupport_function
  $rubyrun_logger.info "Native library not available. Function not supported."
end

#update_perf_metrics(controller, action_metrics_hash) ⇒ Object

metrics hash is a global collection point for all metrics (averaged) for all actions by controller Use serialization before updating this global hash Structure of $rubyrun_metrics_hash: controller_name => => [response_time, action_time,

db_io_time, view_time,
uncaptured_time, dispatch_wait_time,
request_count]


136
137
138
139
140
141
142
143
144
# File 'lib/rubyrun/rubyrun_commander__.rb', line 136

def update_perf_metrics(controller, action_metrics_hash)
  $rubyrun_metrics_hash[controller].merge!(action_metrics_hash) {|action, o_metrics, new_metrics|
    o_metrics.each_index { |x|
      (o_metrics[x] += new_metrics[x]; break) if x == (o_metrics.length-1) # Calculate the total request count for this controller/action
      o_metrics[x] = (o_metrics[x] * o_metrics.last + new_metrics[x])/(o_metrics.last + new_metrics.last).to_f
    }
    o_metrics
  }
end