leakmon 0.0.1
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/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +98 -0
- data/Rakefile +8 -0
- data/leakmon.gemspec +18 -0
- data/lib/leakmon.rb +163 -0
- data/lib/leakmon/version.rb +3 -0
- data/spec/leakmon_spec.rb +126 -0
- data/spec/spec_helper.rb +7 -0
- metadata +68 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Mitsunori Komatsu
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
# Leakmon
|
2
|
+
|
3
|
+
A Ruby library to monitor leaked objects
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'leakmon'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install leakmon
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
If you want to monitor the objects of specific class, then include Leakmon into the class.
|
22
|
+
|
23
|
+
require 'leakmon'
|
24
|
+
|
25
|
+
class Foo
|
26
|
+
attr_accessor :dummy
|
27
|
+
end
|
28
|
+
|
29
|
+
class Foo
|
30
|
+
include Leakmon
|
31
|
+
end
|
32
|
+
|
33
|
+
Foo.release_hook('puts "i am released."') # This is option hook, for debug.
|
34
|
+
|
35
|
+
15.times do
|
36
|
+
o = Foo.new
|
37
|
+
o.dummy = 'x' * 100 * 1024 * 1024 # For GC.
|
38
|
+
sleep 0.5
|
39
|
+
end
|
40
|
+
|
41
|
+
Leakmon.list_remaining_objects.each{|rec| p rec} # Array of the remaining objects (not garbage collected).
|
42
|
+
|
43
|
+
# You can filter the objects by :time keyword.
|
44
|
+
Leakmon.list_remaining_objects(:time => 8).each{|rec| p rec} # Only older than 8 sec.
|
45
|
+
|
46
|
+
Then "i am released." will be printed when some Foo instances are collected. At the last code, the remaining objects will be dumped.
|
47
|
+
|
48
|
+
i am released.
|
49
|
+
:
|
50
|
+
i am released.
|
51
|
+
["Foo__fdbef6946", {:time=>Sun Aug 09 00:11:49 +0900 2009, :caller=>["leakmon.rb:136:in `new'", "leakmon.rb:136", "leakmon.rb:135:in `times'", "leakmon.rb:135"]}]
|
52
|
+
["Foo__fdbef8f52", {:time=>Sun Aug 09 00:11:50 +0900 2009, :caller=>["leakmon.rb:136:in `new'", "leakmon.rb:136", "leakmon.rb:135:in `times'", "leakmon.rb:135"]}]
|
53
|
+
["Foo__fdbef8dfe", {:time=>Sun Aug 09 00:11:55 +0900 2009, :caller=>["leakmon.rb:136:in `new'", "leakmon.rb:136", "leakmon.rb:135:in `times'", "leakmon.rb:135"]}]
|
54
|
+
["Foo__fdbef8c28", {:time=>Sun Aug 09 00:11:57 +0900 2009, :caller=>["leakmon.rb:136:in `new'", "leakmon.rb:136", "leakmon.rb:135:in `times'", "leakmon.rb:135"]}]
|
55
|
+
["Foo__fdbef8912", {:time=>Sun Aug 09 00:11:59 +0900 2009, :caller=>["leakmon.rb:136:in `new'", "leakmon.rb:136", "leakmon.rb:135:in `times'", "leakmon.rb:135"]}]
|
56
|
+
["Foo__fdbef87c8", {:time=>Sun Aug 09 00:12:00 +0900 2009, :caller=>["leakmon.rb:136:in `new'", "leakmon.rb:136", "leakmon.rb:135:in `times'", "leakmon.rb:135"]}]
|
57
|
+
["Foo__fdbef84ee", {:time=>Sun Aug 09 00:12:02 +0900 2009, :caller=>["leakmon.rb:136:in `new'", "leakmon.rb:136", "leakmon.rb:135:in `times'", "leakmon.rb:135"]}]
|
58
|
+
["Foo__fdbef8386", {:time=>Sun Aug 09 00:12:04 +0900 2009, :caller=>["leakmon.rb:136:in `new'", "leakmon.rb:136", "leakmon.rb:135:in `times'", "leakmon.rb:135"]}]
|
59
|
+
["Foo__fdbef8110", {:time=>Sun Aug 09 00:12:05 +0900 2009, :caller=>["leakmon.rb:136:in `new'", "leakmon.rb:136", "leakmon.rb:135:in `times'", "leakmon.rb:135"]}]
|
60
|
+
["Foo__fdbef7ea4", {:time=>Sun Aug 09 00:12:07 +0900 2009, :caller=>["leakmon.rb:136:in `new'", "leakmon.rb:136", "leakmon.rb:135:in `times'", "leakmon.rb:135"]}]
|
61
|
+
["Foo__fdbef8f3e", {:time=>Sun Aug 09 00:12:09 +0900 2009, :caller=>["leakmon.rb:136:in `new'", "leakmon.rb:136", "leakmon.rb:135:in `times'", "leakmon.rb:135"]}]
|
62
|
+
["Foo__fdbef8f34", {:time=>Sun Aug 09 00:12:10 +0900 2009, :caller=>["leakmon.rb:136:in `new'", "leakmon.rb:136", "leakmon.rb:135:in `times'", "leakmon.rb:135"]}]
|
63
|
+
["Foo__fdbef8e76", {:time=>Sun Aug 09 00:12:12 +0900 2009, :caller=>["leakmon.rb:136:in `new'", "leakmon.rb:136", "leakmon.rb:135:in `times'", "leakmon.rb:135"]}]
|
64
|
+
i am released.
|
65
|
+
:
|
66
|
+
i am released.
|
67
|
+
|
68
|
+
You can monitor almost all of classes. Instead of "include Leakmon" in the class definition, call Leakmon.include_in_subclasses. But some classes are not supported, Time (sorry...) and implemented classes in ruby such as String and so on.
|
69
|
+
|
70
|
+
Leakmon.include_in_subclasses(Object) # If you monitor IO and the sub classes, set the argument IO.
|
71
|
+
|
72
|
+
Finally, there is TCP/IP interface.
|
73
|
+
|
74
|
+
Leakmon.tcp_server('0.0.0.0', 4321)
|
75
|
+
|
76
|
+
And using TCP/IP client, you can monitor the realtime information by 'list' command.
|
77
|
+
|
78
|
+
$ telnet localhost 4321
|
79
|
+
Trying ::1...
|
80
|
+
Trying 127.0.0.1...
|
81
|
+
Connected to localhost.
|
82
|
+
Escape character is '^]'.
|
83
|
+
list
|
84
|
+
now: Sun Aug 09 00:48:59 +0900 2009
|
85
|
+
["Rational__fdbe7752e", {:time=>Sun Aug 09 00:48:56 +0900 2009, :caller=>["/usr/lib/ruby/1.8/rational.rb:94:in `new'", "/usr/lib/ruby/1.8/rational.rb:94:in `new!'", "/usr/lib/ruby/1.8/rational.rb:337:in `coerce'", "/usr/lib/ruby/1.8/date.rb:503:in `-'", "/usr/lib/ruby/1.8/date.rb:503:in `jd_to_ajd'", "/usr/lib/ruby/1.8/date.rb:754:in `new'", "leakmon.rb:155", "leakmon.rb:154:in `times'", "leakmon.rb:154"]}]
|
86
|
+
:
|
87
|
+
list 8 <== Only the remaining objects older than 8 sec ago.
|
88
|
+
:
|
89
|
+
quit <== Command for quit TCP/IP interface.
|
90
|
+
|
91
|
+
|
92
|
+
## Contributing
|
93
|
+
|
94
|
+
1. Fork it
|
95
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
96
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
97
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
98
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/leakmon.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/leakmon/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Mitsunori Komatsu"]
|
6
|
+
gem.email = ["komamitsu@gmail.com"]
|
7
|
+
gem.description = %q{A Ruby library to monitor leaked objects}
|
8
|
+
gem.summary = %q{A Ruby library to monitor leaked objects}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "leakmon"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Leakmon::VERSION
|
17
|
+
gem.add_development_dependency "rspec", "~> 2.13.0"
|
18
|
+
end
|
data/lib/leakmon.rb
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
require "leakmon/version"
|
2
|
+
|
3
|
+
module Leakmon
|
4
|
+
class LeakmonString < String; end
|
5
|
+
|
6
|
+
class LeakmonArray < Array; end
|
7
|
+
|
8
|
+
class LeakmonHash < Hash; end
|
9
|
+
|
10
|
+
class LeakmonTime < Time; end
|
11
|
+
|
12
|
+
class LeakmonMutex < Mutex; end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def include_with_subclasses(klass = Object)
|
16
|
+
ObjectSpace.each_object(class << klass; self; end) do |cls|
|
17
|
+
next if out_of_scope?(cls)
|
18
|
+
cls.__send__(:include, Leakmon)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def list_remaining_objects(cond = {})
|
23
|
+
leakmon_mutex.synchronize do
|
24
|
+
cond.keys.inject(remaining_objects) {|objs, cond_key|
|
25
|
+
new_objs = nil
|
26
|
+
|
27
|
+
case cond_key
|
28
|
+
when :time
|
29
|
+
now = LeakmonTime.now
|
30
|
+
new_objs = objs.select do |obj_k, obj_v|
|
31
|
+
obj_v[:time] < now - cond[cond_key]
|
32
|
+
end
|
33
|
+
else
|
34
|
+
raise "Invalid list option [#{cond_key}]"
|
35
|
+
end
|
36
|
+
|
37
|
+
new_objs
|
38
|
+
}.sort_by{|k, v| v[:time]}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def clear_remaining_objects
|
43
|
+
leakmon_mutex.synchronize do
|
44
|
+
@remaining_objects = LeakmonHash.new
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def included(base)
|
49
|
+
class << base
|
50
|
+
@leakmon_included ||= false
|
51
|
+
return if @leakmon_included
|
52
|
+
@leakmon_included = true
|
53
|
+
end
|
54
|
+
|
55
|
+
return unless base.private_methods.include?(:initialize)
|
56
|
+
begin
|
57
|
+
base.__send__(:alias_method, :initialize_without_leakmon, :initialize)
|
58
|
+
rescue NameError
|
59
|
+
return
|
60
|
+
end
|
61
|
+
base.__send__(:alias_method, :initialize, :initialize_with_leakmon)
|
62
|
+
|
63
|
+
def base.release_hook(proc_str)
|
64
|
+
@@leakmon_release_hook = proc_str
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def tcp_server(host, port)
|
69
|
+
require 'thread'
|
70
|
+
require 'socket'
|
71
|
+
|
72
|
+
Thread.new do
|
73
|
+
@leakmon_tcp_server = TCPServer.new(host, port)
|
74
|
+
@leakmon_tcp_server.setsockopt(:SOCKET, :REUSEADDR, true)
|
75
|
+
loop do
|
76
|
+
Thread.new(@leakmon_tcp_server.accept) do |c|
|
77
|
+
while command_line = c.gets.strip
|
78
|
+
next if command_line.empty?
|
79
|
+
|
80
|
+
command, *args = command_line.split(/\s+/)
|
81
|
+
|
82
|
+
case command
|
83
|
+
when 'list'
|
84
|
+
cond = args.empty? ? {} : {:time => Integer(args[0])}
|
85
|
+
c.puts "now: #{Time.now}"
|
86
|
+
Leakmon.list_remaining_objects(cond).each do |obj|
|
87
|
+
c.puts(obj.inspect)
|
88
|
+
end
|
89
|
+
when 'quit'
|
90
|
+
c.close
|
91
|
+
Thread.exit
|
92
|
+
else
|
93
|
+
c.puts 'unknown command'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def leakmon_register(obj, caller)
|
102
|
+
return if out_of_scope?(obj.class)
|
103
|
+
leakmon_mutex.synchronize do
|
104
|
+
remaining_objects[Leakmon.leakmon_key(obj)] = {:time => Time.now, :caller => caller}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def leakmon_key(obj)
|
109
|
+
sprintf("%s__%0x", obj.class, obj.object_id)
|
110
|
+
end
|
111
|
+
|
112
|
+
def leakmon_release_proc(klass, key, proc_str)
|
113
|
+
proc {
|
114
|
+
instance_eval(proc_str)
|
115
|
+
leakmon_release(klass, key)
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
def leakmon_release(klass, key)
|
120
|
+
return if out_of_scope?(klass)
|
121
|
+
leakmon_mutex.synchronize do
|
122
|
+
remaining_objects.delete(key)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
def leakmon_mutex
|
128
|
+
@leakmon_mutex ||= LeakmonMutex.new
|
129
|
+
@leakmon_mutex
|
130
|
+
end
|
131
|
+
|
132
|
+
def out_of_scope?(klass)
|
133
|
+
[Leakmon, LeakmonTime, LeakmonMutex, LeakmonString, LeakmonArray].include?(klass)
|
134
|
+
end
|
135
|
+
|
136
|
+
def remaining_objects
|
137
|
+
@remaining_objects ||= LeakmonHash.new
|
138
|
+
@remaining_objects
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
def register_leakmon(caller)
|
144
|
+
Leakmon.leakmon_register(self, caller)
|
145
|
+
@@leakmon_release_hook ||= nil
|
146
|
+
prc = Leakmon.leakmon_release_proc(
|
147
|
+
self.class,
|
148
|
+
Leakmon.leakmon_key(self),
|
149
|
+
@@leakmon_release_hook
|
150
|
+
)
|
151
|
+
ObjectSpace.define_finalizer(self, prc)
|
152
|
+
# ObjectSpace.define_finalizer(self, proc {|id| puts "hoge #{id}"})
|
153
|
+
end
|
154
|
+
|
155
|
+
def initialize_with_leakmon(*args, &blk)
|
156
|
+
return if caller.detect do |c|
|
157
|
+
c =~ /in `initialize_with_leakmon'/ || c =~ /in `include_with_subclasses'/
|
158
|
+
end
|
159
|
+
initialize_without_leakmon(*args, &blk)
|
160
|
+
register_leakmon(caller)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Leakmon do
|
4
|
+
context "when a class including Leakmon has instantiated some objects finalized or not" do
|
5
|
+
class Foo
|
6
|
+
include Leakmon
|
7
|
+
end
|
8
|
+
|
9
|
+
total_obj_num = 10
|
10
|
+
gc_obj_num = 4
|
11
|
+
|
12
|
+
$output = StringIO.new
|
13
|
+
Foo.release_hook("$output.puts 'hoge'")
|
14
|
+
|
15
|
+
foos = []
|
16
|
+
total_obj_num.times do |i|
|
17
|
+
sleep 1 if i == total_obj_num - 1
|
18
|
+
foos << Foo.new
|
19
|
+
end
|
20
|
+
|
21
|
+
gc_obj_num.times do |i|
|
22
|
+
foos[i] = nil
|
23
|
+
end
|
24
|
+
GC.start
|
25
|
+
|
26
|
+
describe "release_fook" do
|
27
|
+
it "the block is evaluated whenever the object is finalized" do
|
28
|
+
msg_count = 0
|
29
|
+
$output.rewind
|
30
|
+
$output.each_line do |line|
|
31
|
+
msg_count += 1
|
32
|
+
line.should == "hoge\n"
|
33
|
+
end
|
34
|
+
msg_count.should == gc_obj_num
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "list_remaining_objects" do
|
39
|
+
it "prints remaining objects to stdout" do
|
40
|
+
remains = Leakmon.list_remaining_objects
|
41
|
+
remains.size.should == (total_obj_num - gc_obj_num)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "tcp_server" do
|
46
|
+
require 'socket'
|
47
|
+
port = 9876
|
48
|
+
|
49
|
+
Leakmon.tcp_server('0.0.0.0', port)
|
50
|
+
|
51
|
+
sleep 0.3
|
52
|
+
client = TCPSocket.new('127.0.0.1', port)
|
53
|
+
|
54
|
+
def test_list(client, cmd, expected_obj_count)
|
55
|
+
count = 0
|
56
|
+
t = Thread.new do
|
57
|
+
client.puts cmd
|
58
|
+
client.each_line do |l|
|
59
|
+
l.should =~ (count.zero? ? %r|\Anow: | : %r|\A\["Foo__|)
|
60
|
+
count += 1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
sleep 0.2
|
64
|
+
Thread.kill t
|
65
|
+
count.should == 1 + expected_obj_count
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'list' do
|
69
|
+
it "returns remaining objects as response" do
|
70
|
+
test_list(client, 'list', total_obj_num - gc_obj_num)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'list (sec)' do
|
75
|
+
it "returns remaining objects survive the specified seconds as response" do
|
76
|
+
test_list(client, 'list 1', total_obj_num - gc_obj_num - 1)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'quit' do
|
81
|
+
it "disconnect the connection" do
|
82
|
+
client.puts 'quit'
|
83
|
+
client.gets.should be_nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'include_with_subclasses' do
|
90
|
+
it do
|
91
|
+
Leakmon.clear_remaining_objects
|
92
|
+
|
93
|
+
class User
|
94
|
+
attr_accessor :name, :created_at
|
95
|
+
def initialize(name)
|
96
|
+
@name = name
|
97
|
+
@created_at = Time.now
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class PremiumUser < User
|
102
|
+
end
|
103
|
+
|
104
|
+
Leakmon.include_with_subclasses(Object)
|
105
|
+
|
106
|
+
users = []
|
107
|
+
users << User.new('komamitsu')
|
108
|
+
users << User.new('hogehoge')
|
109
|
+
users << PremiumUser.new('hogehoge')
|
110
|
+
|
111
|
+
user_count = 0
|
112
|
+
premium_user_count = 0
|
113
|
+
Leakmon.list_remaining_objects.each do |obj_info|
|
114
|
+
case obj_info.first
|
115
|
+
when /\AUser__/ then user_count += 1
|
116
|
+
when /\APremiumUser__/ then premium_user_count += 1
|
117
|
+
else raise "An unexpected remaining object: #{obj_info.first}"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
user_count.should eq(2)
|
122
|
+
premium_user_count.should eq(1)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: leakmon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Mitsunori Komatsu
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &12735160 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.13.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *12735160
|
25
|
+
description: A Ruby library to monitor leaked objects
|
26
|
+
email:
|
27
|
+
- komamitsu@gmail.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- .gitignore
|
33
|
+
- Gemfile
|
34
|
+
- LICENSE
|
35
|
+
- README.md
|
36
|
+
- Rakefile
|
37
|
+
- leakmon.gemspec
|
38
|
+
- lib/leakmon.rb
|
39
|
+
- lib/leakmon/version.rb
|
40
|
+
- spec/leakmon_spec.rb
|
41
|
+
- spec/spec_helper.rb
|
42
|
+
homepage: ''
|
43
|
+
licenses: []
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.8.11
|
63
|
+
signing_key:
|
64
|
+
specification_version: 3
|
65
|
+
summary: A Ruby library to monitor leaked objects
|
66
|
+
test_files:
|
67
|
+
- spec/leakmon_spec.rb
|
68
|
+
- spec/spec_helper.rb
|