memprof 0.2.9 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Rakefile +30 -0
- data/bin/memprof +158 -0
- data/ext/bin_api.h +6 -2
- data/ext/elf.c +146 -46
- data/ext/extconf.rb +39 -13
- data/ext/mach.c +3 -3
- data/ext/memprof.c +923 -95
- data/ext/tramp.c +2 -2
- data/ext/util.c +1 -0
- data/ext/util.h +34 -2
- data/lib/memprof/signal.rb +16 -0
- data/memprof.gemspec +5 -1
- data/spec/memprof_spec.rb +2 -2
- data/spec/memprof_uploader_spec.rb +117 -0
- metadata +35 -8
- data/lib/memprof/usr2.rb +0 -10
data/ext/tramp.c
CHANGED
@@ -123,7 +123,7 @@ hook_freelist(int entry, void *tramp)
|
|
123
123
|
void
|
124
124
|
insert_tramp(const char *trampee, void *tramp)
|
125
125
|
{
|
126
|
-
void *trampee_addr = bin_find_symbol(trampee, NULL);
|
126
|
+
void *trampee_addr = bin_find_symbol(trampee, NULL, 0);
|
127
127
|
int inline_ent = inline_tramp_size;
|
128
128
|
|
129
129
|
if (trampee_addr == NULL) {
|
@@ -136,7 +136,7 @@ insert_tramp(const char *trampee, void *tramp)
|
|
136
136
|
}
|
137
137
|
} else {
|
138
138
|
tramp_table[tramp_size].addr = tramp;
|
139
|
-
if (bin_update_image(trampee, &tramp_table[tramp_size]) != 0)
|
139
|
+
if (bin_update_image(trampee, &tramp_table[tramp_size], NULL) != 0)
|
140
140
|
errx(EX_SOFTWARE, "Failed to insert tramp for %s", trampee);
|
141
141
|
tramp_size++;
|
142
142
|
}
|
data/ext/util.c
CHANGED
data/ext/util.h
CHANGED
@@ -13,20 +13,52 @@
|
|
13
13
|
struct memprof_config {
|
14
14
|
void *gc_sweep;
|
15
15
|
size_t gc_sweep_size;
|
16
|
+
|
16
17
|
void *finalize_list;
|
17
18
|
size_t finalize_list_size;
|
19
|
+
|
18
20
|
void *rb_gc_force_recycle;
|
19
21
|
size_t rb_gc_force_recycle_size;
|
22
|
+
|
20
23
|
void *freelist;
|
21
24
|
void *classname;
|
22
25
|
void *add_freelist;
|
26
|
+
void *timeofday;
|
27
|
+
|
23
28
|
void *rb_mark_table_add_filename;
|
29
|
+
|
30
|
+
void *bm_mark;
|
31
|
+
void *blk_free;
|
32
|
+
void *thread_mark;
|
33
|
+
|
24
34
|
void *heaps;
|
25
35
|
void *heaps_used;
|
36
|
+
|
26
37
|
size_t sizeof_RVALUE;
|
27
38
|
size_t sizeof_heaps_slot;
|
28
|
-
|
29
|
-
|
39
|
+
|
40
|
+
size_t offset_heaps_slot_limit;
|
41
|
+
size_t offset_heaps_slot_slot;
|
42
|
+
|
43
|
+
size_t offset_BLOCK_body;
|
44
|
+
size_t offset_BLOCK_var;
|
45
|
+
size_t offset_BLOCK_cref;
|
46
|
+
size_t offset_BLOCK_prev;
|
47
|
+
size_t offset_BLOCK_self;
|
48
|
+
size_t offset_BLOCK_klass;
|
49
|
+
size_t offset_BLOCK_wrapper;
|
50
|
+
size_t offset_BLOCK_orig_thread;
|
51
|
+
size_t offset_BLOCK_block_obj;
|
52
|
+
size_t offset_BLOCK_scope;
|
53
|
+
size_t offset_BLOCK_dyna_vars;
|
54
|
+
|
55
|
+
size_t offset_METHOD_klass;
|
56
|
+
size_t offset_METHOD_rklass;
|
57
|
+
size_t offset_METHOD_recv;
|
58
|
+
size_t offset_METHOD_id;
|
59
|
+
size_t offset_METHOD_oid;
|
60
|
+
size_t offset_METHOD_body;
|
61
|
+
|
30
62
|
size_t pagesize;
|
31
63
|
};
|
32
64
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
begin
|
2
|
+
require File.expand_path('../../memprof', __FILE__)
|
3
|
+
rescue LoadError
|
4
|
+
require File.expand_path('../../../ext/memprof', __FILE__)
|
5
|
+
end
|
6
|
+
|
7
|
+
Memprof.start
|
8
|
+
old_handler = trap('URG'){
|
9
|
+
pid = Process.pid
|
10
|
+
fork{
|
11
|
+
GC.start
|
12
|
+
Memprof.dump_all("/tmp/memprof-#{pid}-#{Time.now.to_i}.json")
|
13
|
+
exit!
|
14
|
+
}
|
15
|
+
old_handler.call if old_handler
|
16
|
+
}
|
data/memprof.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
spec = Gem::Specification.new do |s|
|
2
2
|
s.name = 'memprof'
|
3
|
-
s.version = '0.
|
3
|
+
s.version = '0.3.0'
|
4
4
|
s.date = '2010-03-15'
|
5
5
|
s.summary = 'Ruby Memory Profiler'
|
6
6
|
s.description = "Ruby memory profiler similar to bleak_house, but without patches to the Ruby VM"
|
@@ -9,5 +9,9 @@ spec = Gem::Specification.new do |s|
|
|
9
9
|
s.authors = ["Joe Damato", "Aman Gupta", "Jake Douglas", "Rob Benson"]
|
10
10
|
s.email = ["joe@memprof.com", "aman@memprof.com", "jake@memprof.com"]
|
11
11
|
s.extensions = "ext/extconf.rb"
|
12
|
+
s.bindir = 'bin'
|
13
|
+
s.executables << 'memprof'
|
12
14
|
s.files = `git ls-files`.split
|
15
|
+
s.add_dependency('rest-client', '>= 1.4.2')
|
16
|
+
s.add_dependency('term-ansicolor')
|
13
17
|
end
|
data/spec/memprof_spec.rb
CHANGED
@@ -76,7 +76,7 @@ describe Memprof do
|
|
76
76
|
Memprof.stop
|
77
77
|
Memprof.dump_all(filename)
|
78
78
|
|
79
|
-
obj = File.open(filename, 'r').
|
79
|
+
obj = File.open(filename, 'r').readlines.find do |line|
|
80
80
|
line =~ /"dump out the entire heap"/
|
81
81
|
end
|
82
82
|
|
@@ -90,7 +90,7 @@ describe Memprof do
|
|
90
90
|
@str = "some random" + " string"
|
91
91
|
Memprof.dump_all(filename)
|
92
92
|
|
93
|
-
obj = File.open(filename, 'r').
|
93
|
+
obj = File.open(filename, 'r').readlines.find do |line|
|
94
94
|
line =~ /"some random string"/
|
95
95
|
end
|
96
96
|
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bacon'
|
3
|
+
Bacon.summary_on_exit
|
4
|
+
|
5
|
+
describe "MemprofUploader" do
|
6
|
+
|
7
|
+
it "should display help output with -h" do
|
8
|
+
output = `ruby bin/memprof -h`
|
9
|
+
output.should =~ /Memprof Uploader/
|
10
|
+
output.should =~ /Usage:/
|
11
|
+
$?.exitstatus.should == 0
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should fail without a pid being passed" do
|
15
|
+
output = `ruby bin/memprof -n SomeLabel -k abcdef`
|
16
|
+
output.should =~ /Missing PID!/
|
17
|
+
$?.exitstatus.should == 1
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should fail without a name being passed" do
|
21
|
+
output = `ruby bin/memprof -p 123 -k abcdef`
|
22
|
+
output.should =~ /Missing name!/
|
23
|
+
$?.exitstatus.should == 1
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should fail without an API key" do
|
27
|
+
output = `ruby bin/memprof -p 123 -n SomeLabel`
|
28
|
+
output.should =~ /Missing API key!/
|
29
|
+
$?.exitstatus.should == 1
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should fail with an invalid pid" do
|
33
|
+
output = `ruby bin/memprof -p 99999999 -n Label -k abcdef`
|
34
|
+
output.should =~ Regexp.new("No such process 99999999!")
|
35
|
+
$?.exitstatus.should == 1
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should fail when the target process does not create a new file within 5 sec" do
|
39
|
+
pid = fork { sleep 5; exit! }
|
40
|
+
Process.detach(pid)
|
41
|
+
output = `ruby bin/memprof -p #{pid} -n Label -k abcdef`
|
42
|
+
output.should =~ Regexp.new("Waiting 5 seconds for process #{pid} to create a new dump...")
|
43
|
+
output.should =~ Regexp.new("Timed out after waiting 5 seconds")
|
44
|
+
$?.exitstatus.should == 1
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should WORK and wait for a dump to complete if it's IN_PROGRESS" do
|
48
|
+
pid = fork {
|
49
|
+
# create a fake file
|
50
|
+
filename = "/tmp/memprof-#{Process.pid}-#{Time.now.to_i}.json.IN_PROGRESS"
|
51
|
+
# simulate dump in progress
|
52
|
+
trap("URG") { File.open(filename, "w") {|f| f.write("foo") }; sleep 1 }
|
53
|
+
# should get signaled somewhere in here and execute the handler before exiting.
|
54
|
+
sleep 5
|
55
|
+
# rename the file to simulate completion of the dump writeout.
|
56
|
+
File.rename(filename, filename.sub(/\.IN_PROGRESS/, ""))
|
57
|
+
exit!
|
58
|
+
}
|
59
|
+
Process.detach(pid)
|
60
|
+
output = `ruby bin/memprof -p #{pid} -n Label -k abcdef -t`
|
61
|
+
output.should =~ Regexp.new("Waiting 5 seconds for process #{pid} to create a new dump...")
|
62
|
+
output.should =~ Regexp.new("Found file /tmp/memprof-#{pid}-\\d*.json\\.?\\w*")
|
63
|
+
output.should =~ Regexp.new("Dump in progress. Waiting 60 seconds for it to complete...")
|
64
|
+
output.should =~ Regexp.new("Finished!")
|
65
|
+
file = output.slice(Regexp.new("/tmp/memprof-#{pid}-\\d*.json\\.?\\w*"))
|
66
|
+
# make sure both files are gone
|
67
|
+
File.exist?(file).should == false
|
68
|
+
File.exist?(file.sub(/\.IN_PROGRESS/, "") + ".gz").should == false
|
69
|
+
$?.exitstatus.should == 0
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should WORK and delete the dump file after it's done, by default" do
|
73
|
+
pid = fork {
|
74
|
+
require File.dirname(__FILE__) + "/../lib/memprof/signal"
|
75
|
+
# should get signaled somewhere in here and execute the handler before exiting.
|
76
|
+
sleep 5
|
77
|
+
exit!
|
78
|
+
}
|
79
|
+
Process.detach(pid)
|
80
|
+
sleep 2
|
81
|
+
output = `ruby bin/memprof -p #{pid} -n TestDump -k abcdef -t`
|
82
|
+
output.should =~ Regexp.new("Waiting 5 seconds for process #{pid} to create a new dump...")
|
83
|
+
output.should =~ Regexp.new("Found file /tmp/memprof-#{pid}-\\d*.json\\.?\\w*")
|
84
|
+
output.should =~ Regexp.new("Finished!")
|
85
|
+
file = output.slice(Regexp.new("/tmp/memprof-#{pid}-\\d*.json\\.?\\w*"))
|
86
|
+
# make sure both files are gone
|
87
|
+
File.exist?(file).should == false
|
88
|
+
File.exist?(file.sub(/\.IN_PROGRESS/, "") + ".gz").should == false
|
89
|
+
$?.exitstatus.should == 0
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should WORK and leave the dump file after it's done, with --no-delete" do
|
93
|
+
pid = fork {
|
94
|
+
require File.dirname(__FILE__) + "/../lib/memprof/signal"
|
95
|
+
# should get signaled somewhere in here and execute the handler before exiting.
|
96
|
+
sleep 5
|
97
|
+
exit!
|
98
|
+
}
|
99
|
+
Process.detach(pid)
|
100
|
+
sleep 2
|
101
|
+
output = `ruby bin/memprof -p #{pid} -n TestDump -k abcdef -t --no-delete`
|
102
|
+
output.should =~ Regexp.new("Waiting 5 seconds for process #{pid} to create a new dump...")
|
103
|
+
output.should =~ Regexp.new("Found file /tmp/memprof-#{pid}-\\d*.json\\w*")
|
104
|
+
output.should =~ Regexp.new("Finished!")
|
105
|
+
file = output.slice(Regexp.new("/tmp/memprof-#{pid}-\\d*.json\\.?\\w*"))
|
106
|
+
# Make sure it deleted the temporary one
|
107
|
+
if file =~ /\.IN_PROGRESS/
|
108
|
+
File.exist?(file).should == false
|
109
|
+
end
|
110
|
+
# make sure it left the completed one
|
111
|
+
file = file.sub(/\.IN_PROGRESS/, "") + ".gz"
|
112
|
+
File.exist?(file).should == true
|
113
|
+
File.delete(file)
|
114
|
+
$?.exitstatus.should == 0
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 3
|
8
|
+
- 0
|
9
|
+
version: 0.3.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Joe Damato
|
@@ -19,15 +19,40 @@ cert_chain: []
|
|
19
19
|
|
20
20
|
date: 2010-03-15 00:00:00 -07:00
|
21
21
|
default_executable:
|
22
|
-
dependencies:
|
23
|
-
|
22
|
+
dependencies:
|
23
|
+
- !ruby/object:Gem::Dependency
|
24
|
+
name: rest-client
|
25
|
+
prerelease: false
|
26
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 4
|
33
|
+
- 2
|
34
|
+
version: 1.4.2
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: term-ansicolor
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
24
49
|
description: Ruby memory profiler similar to bleak_house, but without patches to the Ruby VM
|
25
50
|
email:
|
26
51
|
- joe@memprof.com
|
27
52
|
- aman@memprof.com
|
28
53
|
- jake@memprof.com
|
29
|
-
executables:
|
30
|
-
|
54
|
+
executables:
|
55
|
+
- memprof
|
31
56
|
extensions:
|
32
57
|
- ext/extconf.rb
|
33
58
|
extra_rdoc_files: []
|
@@ -36,6 +61,7 @@ files:
|
|
36
61
|
- .gitignore
|
37
62
|
- README
|
38
63
|
- Rakefile
|
64
|
+
- bin/memprof
|
39
65
|
- ext/arch.h
|
40
66
|
- ext/bin_api.h
|
41
67
|
- ext/elf.c
|
@@ -55,9 +81,10 @@ files:
|
|
55
81
|
- ext/x86_64.h
|
56
82
|
- ext/x86_gen.h
|
57
83
|
- lib/memprof/middleware.rb
|
58
|
-
- lib/memprof/
|
84
|
+
- lib/memprof/signal.rb
|
59
85
|
- memprof.gemspec
|
60
86
|
- spec/memprof_spec.rb
|
87
|
+
- spec/memprof_uploader_spec.rb
|
61
88
|
has_rdoc: true
|
62
89
|
homepage: http://github.com/ice799/memprof
|
63
90
|
licenses: []
|