bleak_house 3.1 → 3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data.tar.gz.sig +1 -0
- data/CHANGELOG +30 -5
- data/LICENSE_BSD +10 -0
- data/LICENSE_RUBY +53 -0
- data/Manifest +31 -18
- data/README +66 -13
- data/Rakefile +16 -56
- data/TODO +2 -0
- data/bin/bleak +15 -0
- data/ext/bleak_house/logger/extconf.rb +4 -0
- data/ext/bleak_house/logger/snapshot.c +153 -0
- data/ext/bleak_house/logger/snapshot.h +99 -0
- data/init.rb +2 -1
- data/install.rb +2 -1
- data/lib/bleak_house.rb +4 -10
- data/lib/bleak_house/analyzer.rb +5 -0
- data/lib/bleak_house/analyzer/analyzer.rb +203 -0
- data/lib/bleak_house/logger.rb +3 -0
- data/lib/bleak_house/logger/mem_usage.rb +13 -0
- data/lib/bleak_house/rails.rb +6 -0
- data/lib/bleak_house/{action_controller.rb → rails/action_controller.rb} +3 -2
- data/lib/bleak_house/rails/bleak_house.rb +58 -0
- data/lib/bleak_house/rails/dispatcher.rb +19 -0
- data/lib/bleak_house/support/build.rb +53 -0
- data/lib/bleak_house/{support_methods.rb → support/core_extensions.rb} +23 -0
- data/lib/bleak_house/{rake_task_redefine_task.rb → support/rake.rb} +0 -0
- data/lib/vendor/lightcsv.rb +168 -0
- data/patches/gc.c.patch +27 -0
- data/patches/parse.y.patch +16 -0
- data/test/misc/direct.rb +13 -0
- data/test/unit/test_bleak_house.rb +34 -0
- metadata +78 -50
- metadata.gz.sig +0 -0
- data/lib/bleak_house/analyze.rb +0 -139
- data/lib/bleak_house/bleak_house.rb +0 -33
- data/lib/bleak_house/dispatcher.rb +0 -23
- data/lib/bleak_house/gruff_hacks.rb +0 -56
- data/lib/bleak_house/mem_logger.rb +0 -54
- data/tasks/bleak_house_tasks.rake +0 -13
metadata.gz.sig
ADDED
|
Binary file
|
data/lib/bleak_house/analyze.rb
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
require 'rubygems'
|
|
3
|
-
require 'fileutils'
|
|
4
|
-
require 'base64'
|
|
5
|
-
|
|
6
|
-
gem 'gruff', '= 0.2.8'
|
|
7
|
-
require 'gruff'
|
|
8
|
-
|
|
9
|
-
# require, but make rdoc not whine
|
|
10
|
-
load "#{File.dirname(__FILE__)}/gruff_hacks.rb"
|
|
11
|
-
load "#{File.dirname(__FILE__)}/support_methods.rb"
|
|
12
|
-
|
|
13
|
-
Gruff::Base::LEFT_MARGIN = 200
|
|
14
|
-
Gruff::Base::NEGATIVE_TOP_MARGIN = 30
|
|
15
|
-
Gruff::Base::MAX_LEGENDS = 28
|
|
16
|
-
|
|
17
|
-
class BleakHouse
|
|
18
|
-
class Analyze
|
|
19
|
-
|
|
20
|
-
DIR = "#{RAILS_ROOT}/log/bleak_house/"
|
|
21
|
-
|
|
22
|
-
def initialize(data, increments, name)
|
|
23
|
-
@data = data
|
|
24
|
-
@increments = increments
|
|
25
|
-
@name = name
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def draw
|
|
29
|
-
g = Gruff::Line.new("1024x768")
|
|
30
|
-
g.title = @name
|
|
31
|
-
g.x_axis_label = "time"
|
|
32
|
-
g.legend_font_size = g.legend_box_size = 14
|
|
33
|
-
g.title_font_size = 24
|
|
34
|
-
g.marker_font_size = 14
|
|
35
|
-
|
|
36
|
-
@data.map do |key, values|
|
|
37
|
-
name = if key.to_s == ""
|
|
38
|
-
'[Unknown]'
|
|
39
|
-
else
|
|
40
|
-
# elsif key =~ Regexp.new(@specials.keys.join('|'))
|
|
41
|
-
# name = "#{key} (#{values.to_i.max / (2**10)} MB)"
|
|
42
|
-
# @specials.each do |regex, scale|
|
|
43
|
-
# values.map! {|x| x * scale} if key =~ regex
|
|
44
|
-
# end
|
|
45
|
-
# else
|
|
46
|
-
"#{key.gsub(/.*::/, '')} (#{values.to_i.max})"
|
|
47
|
-
end
|
|
48
|
-
[name, values]
|
|
49
|
-
end.sort_by do |key, values|
|
|
50
|
-
0 - key[/.*?([\d]+)\)$/, 1].to_i
|
|
51
|
-
end.each do |key, values|
|
|
52
|
-
g.data(key, values.to_i)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
labels = {}
|
|
56
|
-
mod = (@increments.size / 4.0).ceil
|
|
57
|
-
@increments.each_with_index do |increment, index|
|
|
58
|
-
labels[index] = increment.split(" ").last if (index % mod).zero?
|
|
59
|
-
end
|
|
60
|
-
g.labels = labels
|
|
61
|
-
|
|
62
|
-
g.minimum_value = 0
|
|
63
|
-
# g.maximum_value = @maximum
|
|
64
|
-
|
|
65
|
-
g.write(@name.to_filename + ".png")
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def self.aggregate(data, selector, namer)
|
|
69
|
-
aggregate_data = {}
|
|
70
|
-
increments = []
|
|
71
|
-
data.each_with_index do |frameset, index|
|
|
72
|
-
increments << frameset.time
|
|
73
|
-
frameset.data.keys.select do |key|
|
|
74
|
-
key =~ selector #or key =~ Regexp.new(specials.keys.join('|'))
|
|
75
|
-
end.each do |key|
|
|
76
|
-
aggregate_data[key.to_s[namer, 1]] ||= []
|
|
77
|
-
aggregate_data[key.to_s[namer, 1]][index] += frameset.data[key].to_i
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
[aggregate_data, increments]
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def self.build_all(filename)
|
|
84
|
-
unless File.exists? filename
|
|
85
|
-
puts "No data file found: #{filename}"
|
|
86
|
-
exit
|
|
87
|
-
end
|
|
88
|
-
FileUtils.rm_r(DIR) rescue nil
|
|
89
|
-
Dir.mkdir(DIR)
|
|
90
|
-
|
|
91
|
-
Dir.chdir(DIR) do
|
|
92
|
-
|
|
93
|
-
puts "Parsing data"
|
|
94
|
-
data = File.open(filename).readlines.map do |line|
|
|
95
|
-
Marshal.load Base64.decode64(line)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
# data_maximum = data.flatten.inject(0) do |max, el|
|
|
99
|
-
# if el.is_a? Hash
|
|
100
|
-
# current_max = el.merge({:"real memory" => 0, :"virtual memory" => 0}).values.to_i.max
|
|
101
|
-
# current_max if max < current_max
|
|
102
|
-
# end or max
|
|
103
|
-
# end
|
|
104
|
-
# mem_maximum = data.flatten.inject(0) do |max, el| # only real memory (RSS) for now
|
|
105
|
-
# (el["real memory"] if el.is_a?(Hash) and max < el["real memory"]) or max
|
|
106
|
-
# end
|
|
107
|
-
# mem_scale = data_maximum / mem_maximum.to_f
|
|
108
|
-
# scales = {/memory$/ => mem_scale}
|
|
109
|
-
|
|
110
|
-
puts "By controller"
|
|
111
|
-
controller_data, increments = aggregate(data, //, /^(.*?)($|\/|::::)/)
|
|
112
|
-
Analyze.new(controller_data, increments, "objects by controller").draw
|
|
113
|
-
|
|
114
|
-
# in each controller, by action
|
|
115
|
-
puts "By action"
|
|
116
|
-
controller_data.keys.each do |controller|
|
|
117
|
-
puts " ...in #{controller} controller"
|
|
118
|
-
Dir.descend(controller) do
|
|
119
|
-
action_data, increments = aggregate(data, /^#{controller}($|\/|::::)/, /\/(.*?)($|\/|::::)/)
|
|
120
|
-
Analyze.new(action_data, increments, "objects by action in /#{controller}").draw
|
|
121
|
-
|
|
122
|
-
# in each action, by object class
|
|
123
|
-
action_data.keys.each do |action|
|
|
124
|
-
action = "unknown" if action.to_s == ""
|
|
125
|
-
puts " ...in #{action} action"
|
|
126
|
-
Dir.descend(action) do
|
|
127
|
-
class_data, increments = aggregate(data, /^#{controller}#{"\/#{action}" unless action == "unknown"}($|\/|::::)/,
|
|
128
|
-
/::::(.*)/)
|
|
129
|
-
Analyze.new(class_data, increments, "objects by class in /#{controller}/#{action}").draw
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
end
|
|
139
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
class BleakHouse
|
|
3
|
-
cattr_accessor :last_request_name
|
|
4
|
-
cattr_accessor :dispatch_count
|
|
5
|
-
cattr_accessor :log_interval
|
|
6
|
-
|
|
7
|
-
self.dispatch_count = 0
|
|
8
|
-
self.log_interval = (ENV['INTERVAL'] and ENV['INTERVAL'].to_i or 10)
|
|
9
|
-
|
|
10
|
-
def self.set_request_name request, other = nil
|
|
11
|
-
self.last_request_name = "#{request.parameters['controller']}/#{request.parameters['action']}/#{request.request_method}#{other}"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def self.debug s
|
|
15
|
-
s = "#{name.underscore}: #{s}"
|
|
16
|
-
::ActiveRecord::Base.logger.debug s if ::ActiveRecord::Base.logger
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def self.warn s
|
|
20
|
-
s = "#{name.underscore}: #{s}"
|
|
21
|
-
if ::ActiveRecord::Base.logger
|
|
22
|
-
::ActiveRecord::Base.logger.warn s
|
|
23
|
-
else
|
|
24
|
-
$stderr.puts s
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
LOGFILE = "#{RAILS_ROOT}/log/bleak_house_#{RAILS_ENV}.dump"
|
|
29
|
-
if File.exists?(LOGFILE)
|
|
30
|
-
File.rename(LOGFILE, "#{LOGFILE}.old")
|
|
31
|
-
warn "renamed old logfile"
|
|
32
|
-
end
|
|
33
|
-
end
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
# crazyness
|
|
3
|
-
|
|
4
|
-
class Dispatcher
|
|
5
|
-
class << self
|
|
6
|
-
def prepare_application_with_bleak_house
|
|
7
|
-
prepare_application_without_bleak_house
|
|
8
|
-
BleakHouse.dispatch_count += 1
|
|
9
|
-
BleakHouse::MemLogger.snapshot('core rails')
|
|
10
|
-
end
|
|
11
|
-
alias_method_chain :prepare_application, :bleak_house
|
|
12
|
-
|
|
13
|
-
def reset_after_dispatch_with_bleak_house
|
|
14
|
-
BleakHouse::MemLogger.snapshot(BleakHouse.last_request_name || 'unknown')
|
|
15
|
-
if (BleakHouse.dispatch_count % BleakHouse.log_interval).zero?
|
|
16
|
-
BleakHouse.warn "wrote frameset (#{BleakHouse.dispatch_count} dispatches)"
|
|
17
|
-
BleakHouse::MemLogger.log(BleakHouse::LOGFILE)
|
|
18
|
-
end
|
|
19
|
-
reset_after_dispatch_without_bleak_house
|
|
20
|
-
end
|
|
21
|
-
alias_method_chain :reset_after_dispatch, :bleak_house
|
|
22
|
-
end
|
|
23
|
-
end
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
class Gruff::Base
|
|
3
|
-
|
|
4
|
-
def draw_legend
|
|
5
|
-
@legend_labels = @data.collect {|item| item[DATA_LABEL_INDEX] }
|
|
6
|
-
|
|
7
|
-
legend_square_width = @legend_box_size # small square with color of this item
|
|
8
|
-
legend_left = 10
|
|
9
|
-
|
|
10
|
-
current_x_offset = legend_left
|
|
11
|
-
current_y_offset = TOP_MARGIN + TITLE_MARGIN + @title_caps_height + LEGEND_MARGIN
|
|
12
|
-
|
|
13
|
-
debug { @d.line 0.0, current_y_offset, @raw_columns, current_y_offset }
|
|
14
|
-
|
|
15
|
-
@legend_labels.each_with_index do |legend_label, index|
|
|
16
|
-
|
|
17
|
-
next if index > MAX_LEGENDS
|
|
18
|
-
legend_label = "Some not shown" if index == MAX_LEGENDS
|
|
19
|
-
|
|
20
|
-
# Draw label
|
|
21
|
-
@d.fill = @font_color
|
|
22
|
-
@d.font = @font if @font
|
|
23
|
-
@d.pointsize = scale_fontsize(@legend_font_size)
|
|
24
|
-
@d.stroke('transparent')
|
|
25
|
-
@d.font_weight = NormalWeight
|
|
26
|
-
@d.gravity = WestGravity
|
|
27
|
-
@d = @d.annotate_scaled( @base_image,
|
|
28
|
-
@raw_columns, 1.0,
|
|
29
|
-
current_x_offset + (legend_square_width * 1.7), current_y_offset,
|
|
30
|
-
legend_label.to_s, @scale)
|
|
31
|
-
|
|
32
|
-
if index < MAX_LEGENDS
|
|
33
|
-
# Now draw box with color of this dataset
|
|
34
|
-
@d = @d.stroke('transparent')
|
|
35
|
-
@d = @d.fill @data[index][DATA_COLOR_INDEX]
|
|
36
|
-
@d = @d.rectangle(current_x_offset,
|
|
37
|
-
current_y_offset - legend_square_width / 2.0,
|
|
38
|
-
current_x_offset + legend_square_width,
|
|
39
|
-
current_y_offset + legend_square_width / 2.0)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
@d.pointsize = @legend_font_size
|
|
43
|
-
metrics = @d.get_type_metrics(@base_image, legend_label.to_s)
|
|
44
|
-
current_y_offset += metrics.height * 1.05
|
|
45
|
-
end
|
|
46
|
-
@color_index = 0
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
alias :setup_graph_measurements_without_top_margin :setup_graph_measurements
|
|
50
|
-
def setup_graph_measurements
|
|
51
|
-
setup_graph_measurements_without_top_margin
|
|
52
|
-
@graph_height += NEGATIVE_TOP_MARGIN
|
|
53
|
-
@graph_top -= NEGATIVE_TOP_MARGIN
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
end
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
require 'base64'
|
|
3
|
-
|
|
4
|
-
class BleakHouse::MemLogger
|
|
5
|
-
class << self
|
|
6
|
-
|
|
7
|
-
SEEN = {}
|
|
8
|
-
CURRENT = {}
|
|
9
|
-
TAGS = Hash.new(0)
|
|
10
|
-
TIMEFORMAT = '%Y-%m-%d %H:%M:%S'
|
|
11
|
-
VSZ = "virtual memory"
|
|
12
|
-
RSS = "real memory"
|
|
13
|
-
|
|
14
|
-
def log(path, with_mem = false)
|
|
15
|
-
File.open(path, 'a+') do |log|
|
|
16
|
-
log.sync = true
|
|
17
|
-
TAGS[VSZ], TAGS[RSS] = mem_usage if with_mem
|
|
18
|
-
log.puts Base64.encode64(Marshal.dump([Time.now.strftime(TIMEFORMAT), TAGS])).gsub("\n", '')
|
|
19
|
-
end
|
|
20
|
-
GC.start
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def snapshot(tag)
|
|
24
|
-
# puts "bleak_house: seen: #{SEEN.size}"
|
|
25
|
-
CURRENT.clear
|
|
26
|
-
ObjectSpace.each_object do |obj|
|
|
27
|
-
CURRENT[obj_id = obj._bleak_house_object_id] = true
|
|
28
|
-
unless SEEN[obj_id]
|
|
29
|
-
# symbols will rapidly stabilize; don't worry
|
|
30
|
-
SEEN[obj_id] = "#{tag}::::#{obj._bleak_house_class}".to_sym
|
|
31
|
-
TAGS[SEEN[obj_id]] += 1
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
# puts "bleak_house: current: #{CURRENT.size}"
|
|
35
|
-
SEEN.keys.each do |obj_id|
|
|
36
|
-
TAGS[SEEN.delete(obj_id)] -= 1 unless CURRENT[obj_id]
|
|
37
|
-
end
|
|
38
|
-
# puts "bleak_house: done"
|
|
39
|
-
CURRENT.clear
|
|
40
|
-
GC.start
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def mem_usage
|
|
44
|
-
`ps -x -o 'rss vsz' -p #{Process.pid}`.split(/\s+/)[-2..-1].map{|el| el.to_i}
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
class Object
|
|
51
|
-
alias :_bleak_house_object_id :object_id
|
|
52
|
-
alias :_bleak_house_class :class
|
|
53
|
-
end
|
|
54
|
-
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
namespace :bleak_house do
|
|
3
|
-
desc 'Analyze and chart all data'
|
|
4
|
-
task :analyze do
|
|
5
|
-
begin
|
|
6
|
-
require "#{File.dirname(__FILE__)}/../lib/bleak_house/analyze"
|
|
7
|
-
rescue LoadError
|
|
8
|
-
require 'bleak_house/analyze'
|
|
9
|
-
end
|
|
10
|
-
BleakHouse::Analyze.build_all("#{RAILS_ROOT}/log/bleak_house_#{RAILS_ENV}.dump")
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|