bleak_house 7.1 → 7.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 +52 -12
- data/Manifest +20 -20
- data/README +84 -31
- data/Rakefile +10 -83
- data/TODO +10 -0
- data/bin/bleak +13 -0
- data/bleak_house.gemspec +36 -0
- data/ext/build_ruby.rb +114 -0
- data/ext/build_snapshot.rb +5 -0
- data/ext/extconf.rb +26 -0
- data/ext/snapshot.c +153 -0
- data/ext/snapshot.h +59 -0
- data/lib/bleak_house.rb +17 -12
- data/lib/bleak_house/analyzer.rb +54 -0
- data/lib/bleak_house/hook.rb +24 -0
- data/ruby/ruby-1.8.7-p174.tar.bz2 +0 -0
- data/ruby/ruby-1.8.7.patch +483 -0
- data/test/benchmark/bench.rb +16 -0
- data/test/test_helper.rb +6 -0
- data/test/unit/test_bleak_house.rb +44 -19
- metadata +100 -104
- metadata.gz.sig +2 -0
- data/init.rb +0 -2
- data/install.rb +0 -7
- data/lib/bleak_house/action_controller.rb +0 -17
- data/lib/bleak_house/analyze.rb +0 -196
- data/lib/bleak_house/bleak_house.rb +0 -47
- data/lib/bleak_house/c.rb +0 -230
- data/lib/bleak_house/dispatcher.rb +0 -19
- data/lib/bleak_house/gruff_hacks.rb +0 -62
- data/lib/bleak_house/rake_task_redefine_task.rb +0 -25
- data/lib/bleak_house/support_methods.rb +0 -50
- data/patches/gc.c.patch +0 -30
- data/tasks/bleak_house_tasks.rake +0 -16
- data/test/misc/direct.rb +0 -13
@@ -1,47 +0,0 @@
|
|
1
|
-
|
2
|
-
class BleakHouse
|
3
|
-
cattr_accessor :last_request_name
|
4
|
-
|
5
|
-
# Avoid making four more strings on each request.
|
6
|
-
CONTROLLER_KEY = 'controller'
|
7
|
-
ACTION_KEY = 'action'
|
8
|
-
GSUB_SEARCH = '/'
|
9
|
-
GSUB_REPLACEMENT = '__'
|
10
|
-
|
11
|
-
# Sets the request name on the BleakHouse object to match this Rails request. Called from <tt>ActionController::Base.process</tt>. Assign to <tt>last_request_name</tt> yourself if you are not using BleakHouse within Rails.
|
12
|
-
def self.set_request_name request, other = nil
|
13
|
-
self.last_request_name = "#{
|
14
|
-
request.parameters[CONTROLLER_KEY].gsub(GSUB_SEARCH, GSUB_REPLACEMENT) # mangle namespaced controller names
|
15
|
-
}/#{
|
16
|
-
request.parameters[ACTION_KEY]
|
17
|
-
}/#{
|
18
|
-
request.request_method
|
19
|
-
}#{
|
20
|
-
other
|
21
|
-
}"
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.debug s #:nodoc:
|
25
|
-
s = "** #{name.underscore}: #{s}"
|
26
|
-
RAILS_DEFAULT_LOGGER.debug s if RAILS_DEFAULT_LOGGER
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.warn s #:nodoc:
|
30
|
-
s = "** #{name.underscore}: #{s}"
|
31
|
-
if RAILS_DEFAULT_LOGGER
|
32
|
-
RAILS_DEFAULT_LOGGER.warn s
|
33
|
-
else
|
34
|
-
$stderr.puts s
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
LOGFILE = "#{RAILS_ROOT}/log/bleak_house_#{RAILS_ENV}.yaml.log"
|
39
|
-
if File.exists?(LOGFILE)
|
40
|
-
File.rename(LOGFILE, "#{LOGFILE}.old")
|
41
|
-
warn "renamed old logfile"
|
42
|
-
end
|
43
|
-
|
44
|
-
WITH_SPECIALS = false
|
45
|
-
|
46
|
-
MEMLOGGER = CLogger.new
|
47
|
-
end
|
data/lib/bleak_house/c.rb
DELETED
@@ -1,230 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'rubygems'
|
3
|
-
require 'inline'
|
4
|
-
|
5
|
-
class BleakHouse
|
6
|
-
|
7
|
-
=begin rdoc
|
8
|
-
This class performs the actual object logging of BleakHouse. To use it directly, you need to make calls to BleakHouse::CLogger#snapshot.
|
9
|
-
|
10
|
-
== Example
|
11
|
-
|
12
|
-
At the start of your app, put:
|
13
|
-
require 'rubygems'
|
14
|
-
require 'bleak_house/c'
|
15
|
-
$memlogger = BleakHouse::CLogger.new
|
16
|
-
File.delete($logfile = "/path/to/logfile") rescue nil
|
17
|
-
|
18
|
-
(This assumes you are using the gem version.)
|
19
|
-
|
20
|
-
Now, at the points of interest, put:
|
21
|
-
$memlogger.snapshot($logfile, "tag/subtag", false)
|
22
|
-
|
23
|
-
Run your app. Once you are done, analyze your data:
|
24
|
-
ruby -r rubygems -e 'require "bleak_house/analyze"; BleakHouse::Analyze.build_all("/path/to/logfile")'
|
25
|
-
|
26
|
-
You will get a <tt>bleak_house/</tt> folder in the same folder as your logfile.
|
27
|
-
|
28
|
-
=end
|
29
|
-
|
30
|
-
class CLogger
|
31
|
-
|
32
|
-
MAX_UNIQ_TAGS = 1536 # per frame
|
33
|
-
MAX_TAG_LENGTH = 192 # tag plus fully namespaced classname
|
34
|
-
|
35
|
-
# Returns an array of the running process's real and virtual memory usage, in kilobytes.
|
36
|
-
def mem_usage
|
37
|
-
a = `ps -o vsz,rss -p #{Process.pid}`.split(/\s+/)[-2..-1].map{|el| el.to_i}
|
38
|
-
[a.first - a.last, a.last]
|
39
|
-
end
|
40
|
-
|
41
|
-
inline do |builder|
|
42
|
-
builder.include '"node.h"' # struct RNode
|
43
|
-
builder.include '"st.h"' # struct st_table
|
44
|
-
builder.include '"re.h"' # struct RRegexp
|
45
|
-
builder.include '"env.h"' # various structs
|
46
|
-
|
47
|
-
builder.prefix <<-EOC
|
48
|
-
typedef struct RVALUE {
|
49
|
-
union {
|
50
|
-
struct {
|
51
|
-
unsigned long flags; /* always 0 for freed obj */
|
52
|
-
struct RVALUE *next;
|
53
|
-
} free;
|
54
|
-
struct RBasic basic;
|
55
|
-
struct RObject object;
|
56
|
-
struct RClass klass;
|
57
|
-
struct RFloat flonum;
|
58
|
-
struct RString string;
|
59
|
-
struct RArray array;
|
60
|
-
struct RRegexp regexp;
|
61
|
-
struct RHash hash;
|
62
|
-
struct RData data;
|
63
|
-
struct RStruct rstruct;
|
64
|
-
struct RBignum bignum;
|
65
|
-
struct RFile file;
|
66
|
-
struct RNode node;
|
67
|
-
struct RMatch match;
|
68
|
-
struct RVarmap varmap;
|
69
|
-
struct SCOPE scope;
|
70
|
-
} as;
|
71
|
-
} RVALUE;
|
72
|
-
|
73
|
-
struct heaps_slot {
|
74
|
-
void *membase;
|
75
|
-
RVALUE *slot;
|
76
|
-
int limit;
|
77
|
-
};
|
78
|
-
|
79
|
-
struct heaps_slot * rb_gc_heap_slots();
|
80
|
-
int rb_gc_heaps_used();
|
81
|
-
int rb_gc_heaps_length();
|
82
|
-
EOC
|
83
|
-
|
84
|
-
# number of struct heaps_slots used
|
85
|
-
builder.c <<-EOC
|
86
|
-
static int
|
87
|
-
heaps_used() {
|
88
|
-
return rb_gc_heaps_used();
|
89
|
-
}
|
90
|
-
EOC
|
91
|
-
|
92
|
-
# length of the struct heaps_slots allocated
|
93
|
-
builder.c <<-EOC
|
94
|
-
static int
|
95
|
-
heaps_length() {
|
96
|
-
return rb_gc_heaps_length();
|
97
|
-
}
|
98
|
-
EOC
|
99
|
-
|
100
|
-
OBJ_TYPES = ["T_NIL",
|
101
|
-
"T_OBJECT",
|
102
|
-
"T_CLASS",
|
103
|
-
"T_ICLASS",
|
104
|
-
"T_MODULE",
|
105
|
-
"T_FLOAT",
|
106
|
-
"T_STRING",
|
107
|
-
"T_REGEXP",
|
108
|
-
"T_ARRAY",
|
109
|
-
"T_FIXNUM",
|
110
|
-
"T_HASH",
|
111
|
-
"T_STRUCT",
|
112
|
-
"T_BIGNUM",
|
113
|
-
"T_FILE",
|
114
|
-
"T_TRUE",
|
115
|
-
"T_FALSE",
|
116
|
-
"T_DATA",
|
117
|
-
"T_SYMBOL",
|
118
|
-
"T_MATCH"]
|
119
|
-
|
120
|
-
RAW_TYPES = ["T_NONE",
|
121
|
-
"T_BLKTAG",
|
122
|
-
"T_UNDEF",
|
123
|
-
"T_VARMAP",
|
124
|
-
"T_SCOPE",
|
125
|
-
"T_NODE"]
|
126
|
-
|
127
|
-
# Counts the live objects on the heap and writes a single tagged YAML frame to the logfile. Set <tt>specials = true</tt> if you also want to count AST nodes and var scopes; otherwise, use <tt>false</tt>.
|
128
|
-
def snapshot(logfile, tag, specials)
|
129
|
-
# RDoc stub
|
130
|
-
end
|
131
|
-
|
132
|
-
builder.c <<-EOC
|
133
|
-
static void
|
134
|
-
VALUE snapshot(VALUE logfile, VALUE tag, VALUE _specials) {
|
135
|
-
Check_Type(logfile, T_STRING);
|
136
|
-
Check_Type(tag, T_STRING);
|
137
|
-
|
138
|
-
RVALUE *p, *pend;
|
139
|
-
struct heaps_slot * heaps = rb_gc_heap_slots();
|
140
|
-
|
141
|
-
int specials = RTEST(_specials);
|
142
|
-
|
143
|
-
FILE *obj_log = fopen(StringValueCStr(logfile), "r");
|
144
|
-
int is_new;
|
145
|
-
if (!(is_new = (obj_log == NULL)))
|
146
|
-
fclose(obj_log);
|
147
|
-
|
148
|
-
if ((obj_log = fopen(StringValueCStr(logfile), "a+")) == NULL)
|
149
|
-
rb_raise(rb_eRuntimeError, "couldn't open snapshot file");
|
150
|
-
|
151
|
-
if (is_new)
|
152
|
-
fprintf(obj_log, \"---\\n");
|
153
|
-
fprintf(obj_log, \"- - %i\\n", time(0));
|
154
|
-
VALUE mem = rb_funcall(self, rb_intern("mem_usage"), 0);
|
155
|
-
fprintf(obj_log, \" - :\\"memory usage/swap\\": %i\\n", NUM2INT(RARRAY_PTR(mem)[0]));
|
156
|
-
fprintf(obj_log, \" :\\"memory usage/real\\": %i\\n", NUM2INT(RARRAY_PTR(mem)[1]));
|
157
|
-
|
158
|
-
/* haha */
|
159
|
-
char tags[#{MAX_UNIQ_TAGS}][#{MAX_TAG_LENGTH}];
|
160
|
-
char current_tag[2048];
|
161
|
-
int counts[#{MAX_UNIQ_TAGS}];
|
162
|
-
int current_pos = 0;
|
163
|
-
|
164
|
-
int filled_slots = 0;
|
165
|
-
int free_slots = 0;
|
166
|
-
|
167
|
-
int i, j;
|
168
|
-
for (i = 0; i < rb_gc_heaps_used(); i++) {
|
169
|
-
p = heaps[i].slot;
|
170
|
-
pend = p + heaps[i].limit;
|
171
|
-
for (; p < pend; p++) {
|
172
|
-
if (p->as.basic.flags) { /* always 0 for freed objects */
|
173
|
-
filled_slots ++;
|
174
|
-
sprintf(current_tag, "");
|
175
|
-
switch (TYPE(p)) {
|
176
|
-
#{RAW_TYPES.map do |type|
|
177
|
-
"case #{type}:
|
178
|
-
if (specials)
|
179
|
-
sprintf(current_tag , \"%s::::_#{type[2..-1].downcase}\", StringValueCStr(tag));
|
180
|
-
break;
|
181
|
-
"
|
182
|
-
end.flatten.join}
|
183
|
-
default:
|
184
|
-
if (!p->as.basic.klass) {
|
185
|
-
sprintf(current_tag , "%s::::_unknown", StringValueCStr(tag));
|
186
|
-
} else {
|
187
|
-
sprintf(current_tag , "%s::::%s", StringValueCStr(tag), rb_obj_classname((VALUE)p));
|
188
|
-
}
|
189
|
-
}
|
190
|
-
if (strlen(current_tag) > #{MAX_TAG_LENGTH})
|
191
|
-
rb_raise(rb_eRuntimeError, "tag + classname too big; increase MAX_TAG_LENGTH (#{MAX_TAG_LENGTH})");
|
192
|
-
if (strcmp(current_tag, "")) {
|
193
|
-
for (j = 0; j < current_pos; j++) {
|
194
|
-
if (!strcmp(tags[j], current_tag)) {
|
195
|
-
counts[j] ++;
|
196
|
-
break;
|
197
|
-
}
|
198
|
-
}
|
199
|
-
if (j == current_pos) {
|
200
|
-
/* found a new one */
|
201
|
-
if (current_pos == #{MAX_UNIQ_TAGS})
|
202
|
-
rb_raise(rb_eRuntimeError, "exhausted tag array; increase MAX_UNIQ_TAGS (#{MAX_UNIQ_TAGS})");
|
203
|
-
sprintf(tags[current_pos], current_tag);
|
204
|
-
counts[current_pos] = 1;
|
205
|
-
current_pos ++;
|
206
|
-
}
|
207
|
-
}
|
208
|
-
} else {
|
209
|
-
free_slots ++;
|
210
|
-
}
|
211
|
-
}
|
212
|
-
}
|
213
|
-
fprintf(obj_log, \" :\\"heap usage/filled slots\\": %i\\n", filled_slots);
|
214
|
-
fprintf(obj_log, \" :\\"heap usage/free slots\\": %i\\n", free_slots);
|
215
|
-
for (j = 0; j < current_pos; j++) {
|
216
|
-
fprintf(obj_log, " :\\"%s\\": %i\\n", tags[j], counts[j]);
|
217
|
-
}
|
218
|
-
fclose(obj_log);
|
219
|
-
|
220
|
-
/* request GC run */
|
221
|
-
rb_funcall(rb_mGC, rb_intern("start"), 0);
|
222
|
-
/* to get a custom module: rb_const_get(parentModule, "ModuleName")
|
223
|
-
parent module can be something like rb_mKernel for the toplevel */
|
224
|
-
return Qtrue;
|
225
|
-
}
|
226
|
-
EOC
|
227
|
-
end
|
228
|
-
|
229
|
-
end
|
230
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
|
2
|
-
# Override Dispatcher#prepare and Dispatcher#reset_after_dispatch so that each request makes before-and-after usage snapshots.
|
3
|
-
class Dispatcher
|
4
|
-
class << self
|
5
|
-
|
6
|
-
def prepare_application_with_bleak_house
|
7
|
-
prepare_application_without_bleak_house
|
8
|
-
BleakHouse::MEMLOGGER.snapshot(BleakHouse::LOGFILE, 'core rails', BleakHouse::WITH_SPECIALS)
|
9
|
-
end
|
10
|
-
alias_method_chain :prepare_application, :bleak_house
|
11
|
-
|
12
|
-
def reset_after_dispatch_with_bleak_house
|
13
|
-
BleakHouse::MEMLOGGER.snapshot(BleakHouse::LOGFILE, BleakHouse.last_request_name || 'unknown', BleakHouse::WITH_SPECIALS)
|
14
|
-
reset_after_dispatch_without_bleak_house
|
15
|
-
end
|
16
|
-
alias_method_chain :reset_after_dispatch, :bleak_house
|
17
|
-
|
18
|
-
end
|
19
|
-
end
|
@@ -1,62 +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
|
-
alias :clip_value_if_greater_than_without_size_hacks :clip_value_if_greater_than
|
57
|
-
def clip_value_if_greater_than(arg1, arg2)
|
58
|
-
arg2 = arg2 / 2 if arg1 == @columns / (@norm_data.first[1].size * 2.5)
|
59
|
-
clip_value_if_greater_than_without_size_hacks(arg1, arg2)
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
|
2
|
-
# http://www.bigbold.com/snippets/posts/show/2032
|
3
|
-
module Rake
|
4
|
-
module TaskManager
|
5
|
-
def redefine_task(task_class, args, &block)
|
6
|
-
task_name, deps = resolve_args(args)
|
7
|
-
task_name = task_class.scope_name(@scope, task_name)
|
8
|
-
deps = [deps] unless deps.respond_to?(:to_ary)
|
9
|
-
deps = deps.collect {|d| d.to_s }
|
10
|
-
task = @tasks[task_name.to_s] = task_class.new(task_name, self)
|
11
|
-
task.application = self
|
12
|
-
task.add_comment(@last_comment)
|
13
|
-
@last_comment = nil
|
14
|
-
task.enhance(deps, &block)
|
15
|
-
task
|
16
|
-
end
|
17
|
-
end
|
18
|
-
class Task
|
19
|
-
class << self
|
20
|
-
def redefine_task(args, &block)
|
21
|
-
Rake.application.redefine_task(self, args, &block)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
|
2
|
-
class Array
|
3
|
-
alias :time :first
|
4
|
-
alias :data :last
|
5
|
-
|
6
|
-
def sum
|
7
|
-
inject(0) {|s, x| x + s}
|
8
|
-
end
|
9
|
-
|
10
|
-
def to_i
|
11
|
-
self.map{|s| s.to_i}
|
12
|
-
end
|
13
|
-
|
14
|
-
end
|
15
|
-
|
16
|
-
class Dir
|
17
|
-
def self.descend path, &block
|
18
|
-
path = path.split("/") unless path.is_a? Array
|
19
|
-
top = (path.shift or ".")
|
20
|
-
Dir.mkdir(top) unless File.exists? top
|
21
|
-
Dir.chdir(top) do
|
22
|
-
if path.any?
|
23
|
-
descend path, &block
|
24
|
-
else
|
25
|
-
block.call
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
class String
|
32
|
-
def to_filename
|
33
|
-
self.downcase.gsub(/[^\w\d\-]/, '_')
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
class NilClass
|
38
|
-
def +(op)
|
39
|
-
self.to_i + op
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
class Symbol
|
44
|
-
def =~ regex
|
45
|
-
self.to_s =~ regex
|
46
|
-
end
|
47
|
-
def [](*args)
|
48
|
-
self.to_s[*args]
|
49
|
-
end
|
50
|
-
end
|
data/patches/gc.c.patch
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
Index: gc.c
|
2
|
-
===================================================================
|
3
|
-
RCS file: /src/ruby/gc.c,v
|
4
|
-
retrieving revision 1.168.2.45
|
5
|
-
diff -p -u -r1.168.2.45 gc.c
|
6
|
-
--- gc.c 25 Aug 2006 08:12:46 -0000 1.168.2.45
|
7
|
-
+++ gc.c 31 Aug 2006 18:47:55 -0000
|
8
|
-
@@ -323,6 +323,22 @@ static struct heaps_slot {
|
9
|
-
static int heaps_length = 0;
|
10
|
-
static int heaps_used = 0;
|
11
|
-
|
12
|
-
+struct heaps_slot *
|
13
|
-
+rb_gc_heap_slots()
|
14
|
-
+{
|
15
|
-
+ return heaps;
|
16
|
-
+}
|
17
|
-
+
|
18
|
-
+int
|
19
|
-
+rb_gc_heaps_used() {
|
20
|
-
+ return heaps_used;
|
21
|
-
+}
|
22
|
-
+
|
23
|
-
+int
|
24
|
-
+rb_gc_heaps_length() {
|
25
|
-
+ return heaps_length;
|
26
|
-
+}
|
27
|
-
+
|
28
|
-
#define HEAP_MIN_SLOTS 10000
|
29
|
-
static int heap_slots = HEAP_MIN_SLOTS;
|
30
|
-
|