komamitsu-gc_monitor 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/History.txt +4 -0
- data/Manifest.txt +11 -0
- data/PostInstall.txt +7 -0
- data/README.rdoc +72 -0
- data/Rakefile +26 -0
- data/lib/gc_monitor.rb +143 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/test/test_gc_monitor.rb +11 -0
- data/test/test_helper.rb +3 -0
- metadata +64 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/PostInstall.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
= GcMonitor
|
2
|
+
GcMonitor is a Ruby module for monitoring GC.
|
3
|
+
|
4
|
+
== Examples
|
5
|
+
If you want to monitor the specific class, you may include GcMonitor into the class.
|
6
|
+
|
7
|
+
require 'gc_monitor'
|
8
|
+
|
9
|
+
class Foo
|
10
|
+
attr_accessor :dummy
|
11
|
+
include GcMonitor
|
12
|
+
end
|
13
|
+
|
14
|
+
Foo.release_hook('puts "i am released."') # This is option hook, for debug.
|
15
|
+
|
16
|
+
15.times do
|
17
|
+
o = Foo.new
|
18
|
+
o.dummy = 'x' * 100 * 1024 * 1024 # For GC.
|
19
|
+
sleep 0.5
|
20
|
+
end
|
21
|
+
|
22
|
+
GcMonitor.list.each{|rec| p rec} # Array of the objects not garbage collected.
|
23
|
+
|
24
|
+
# You can filter the objects with using :time keyword.
|
25
|
+
GcMonitor.list(:time => 8).each{|rec| p rec} # Only older than 8 sec.
|
26
|
+
|
27
|
+
Then "i am released." will be printed when some Foo instancees are collected. At the last codes, array of the remaining objects will be dumped.
|
28
|
+
|
29
|
+
i am released.
|
30
|
+
:
|
31
|
+
i am released.
|
32
|
+
["Foo__fdbef6946", {:time=>Sun Aug 09 00:11:49 +0900 2009, :caller=>["gc_monitor.rb:136:in `new'", "gc_monitor.rb:136", "gc_monitor.rb:135:in `times'", "gc_monitor.rb:135"]}]
|
33
|
+
["Foo__fdbef8f52", {:time=>Sun Aug 09 00:11:50 +0900 2009, :caller=>["gc_monitor.rb:136:in `new'", "gc_monitor.rb:136", "gc_monitor.rb:135:in `times'", "gc_monitor.rb:135"]}]
|
34
|
+
["Foo__fdbef8dfe", {:time=>Sun Aug 09 00:11:55 +0900 2009, :caller=>["gc_monitor.rb:136:in `new'", "gc_monitor.rb:136", "gc_monitor.rb:135:in `times'", "gc_monitor.rb:135"]}]
|
35
|
+
["Foo__fdbef8c28", {:time=>Sun Aug 09 00:11:57 +0900 2009, :caller=>["gc_monitor.rb:136:in `new'", "gc_monitor.rb:136", "gc_monitor.rb:135:in `times'", "gc_monitor.rb:135"]}]
|
36
|
+
["Foo__fdbef8912", {:time=>Sun Aug 09 00:11:59 +0900 2009, :caller=>["gc_monitor.rb:136:in `new'", "gc_monitor.rb:136", "gc_monitor.rb:135:in `times'", "gc_monitor.rb:135"]}]
|
37
|
+
["Foo__fdbef87c8", {:time=>Sun Aug 09 00:12:00 +0900 2009, :caller=>["gc_monitor.rb:136:in `new'", "gc_monitor.rb:136", "gc_monitor.rb:135:in `times'", "gc_monitor.rb:135"]}]
|
38
|
+
["Foo__fdbef84ee", {:time=>Sun Aug 09 00:12:02 +0900 2009, :caller=>["gc_monitor.rb:136:in `new'", "gc_monitor.rb:136", "gc_monitor.rb:135:in `times'", "gc_monitor.rb:135"]}]
|
39
|
+
["Foo__fdbef8386", {:time=>Sun Aug 09 00:12:04 +0900 2009, :caller=>["gc_monitor.rb:136:in `new'", "gc_monitor.rb:136", "gc_monitor.rb:135:in `times'", "gc_monitor.rb:135"]}]
|
40
|
+
["Foo__fdbef8110", {:time=>Sun Aug 09 00:12:05 +0900 2009, :caller=>["gc_monitor.rb:136:in `new'", "gc_monitor.rb:136", "gc_monitor.rb:135:in `times'", "gc_monitor.rb:135"]}]
|
41
|
+
["Foo__fdbef7ea4", {:time=>Sun Aug 09 00:12:07 +0900 2009, :caller=>["gc_monitor.rb:136:in `new'", "gc_monitor.rb:136", "gc_monitor.rb:135:in `times'", "gc_monitor.rb:135"]}]
|
42
|
+
["Foo__fdbef8f3e", {:time=>Sun Aug 09 00:12:09 +0900 2009, :caller=>["gc_monitor.rb:136:in `new'", "gc_monitor.rb:136", "gc_monitor.rb:135:in `times'", "gc_monitor.rb:135"]}]
|
43
|
+
["Foo__fdbef8f34", {:time=>Sun Aug 09 00:12:10 +0900 2009, :caller=>["gc_monitor.rb:136:in `new'", "gc_monitor.rb:136", "gc_monitor.rb:135:in `times'", "gc_monitor.rb:135"]}]
|
44
|
+
["Foo__fdbef8e76", {:time=>Sun Aug 09 00:12:12 +0900 2009, :caller=>["gc_monitor.rb:136:in `new'", "gc_monitor.rb:136", "gc_monitor.rb:135:in `times'", "gc_monitor.rb:135"]}]
|
45
|
+
i am released.
|
46
|
+
:
|
47
|
+
i am released.
|
48
|
+
|
49
|
+
You can monitor almost all of classes. Instead of "include GcMonitor" in the class definition, call GcMonitor.include_in_subclasses. But some classes are not supported, Time (sorry...) and implemented classes in ruby such as String and so on.
|
50
|
+
|
51
|
+
GcMonitor.include_in_subclasses(Object) # If you monitor IO and the sub classes, set the argument IO.
|
52
|
+
|
53
|
+
Finally, there is TCP/IP interface.
|
54
|
+
|
55
|
+
GcMonitor.tcp_server('0.0.0.0', 4321)
|
56
|
+
|
57
|
+
And using TCP/IP client, you can monitor the realtime information by 'list' command.
|
58
|
+
|
59
|
+
$ telnet localhost 4321
|
60
|
+
Trying ::1...
|
61
|
+
Trying 127.0.0.1...
|
62
|
+
Connected to localhost.
|
63
|
+
Escape character is '^]'.
|
64
|
+
list
|
65
|
+
now: Sun Aug 09 00:48:59 +0900 2009
|
66
|
+
["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'", "gc_monitor.rb:155", "gc_monitor.rb:154:in `times'", "gc_monitor.rb:154"]}]
|
67
|
+
:
|
68
|
+
list 8 <== Only the remaining objects older than 8 sec ago.
|
69
|
+
:
|
70
|
+
quit <== Command for quit TCP/IP interface.
|
71
|
+
|
72
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'hoe', '>= 2.1.0'
|
3
|
+
require 'hoe'
|
4
|
+
require 'fileutils'
|
5
|
+
require './lib/gc_monitor'
|
6
|
+
|
7
|
+
Hoe.plugin :newgem
|
8
|
+
# Hoe.plugin :website
|
9
|
+
# Hoe.plugin :cucumberfeatures
|
10
|
+
|
11
|
+
# Generate all the Rake tasks
|
12
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
13
|
+
$hoe = Hoe.spec 'gc_monitor' do
|
14
|
+
self.developer 'FIXME full name', 'FIXME email'
|
15
|
+
self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
16
|
+
self.rubyforge_name = self.name # TODO this is default value
|
17
|
+
# self.extra_deps = [['activesupport','>= 2.0.2']]
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'newgem/tasks'
|
22
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
23
|
+
|
24
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
25
|
+
# remove_task :default
|
26
|
+
# task :default => [:spec, :features]
|
data/lib/gc_monitor.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module GcMonitor
|
4
|
+
VERSION = '0.0.1'
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def remaining_objects
|
8
|
+
@remaining_objects ||= {}
|
9
|
+
@remaining_objects
|
10
|
+
end
|
11
|
+
|
12
|
+
def mutex
|
13
|
+
@mutex ||= Mutex.new
|
14
|
+
@mutex
|
15
|
+
end
|
16
|
+
|
17
|
+
def include_in_subclasses(klass = Object)
|
18
|
+
ObjectSpace.each_object(class << klass; self; end) do |cls|
|
19
|
+
next if cls.ancestors.include?(Exception)
|
20
|
+
next if [GcMonitor, Time].include?(cls)
|
21
|
+
cls.__send__(:include, GcMonitor)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def key(obj)
|
26
|
+
sprintf("%s__%0x", obj.class, obj.object_id)
|
27
|
+
end
|
28
|
+
|
29
|
+
def regist(obj, caller)
|
30
|
+
mutex.synchronize do
|
31
|
+
remaining_objects[GcMonitor.key(obj)] = {:time => Time.now, :caller => caller}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def release(obj, caller)
|
36
|
+
mutex.synchronize do
|
37
|
+
remaining_objects.delete(GcMonitor.key(obj))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def list(cond = {})
|
42
|
+
mutex.synchronize do
|
43
|
+
cond.keys.inject(remaining_objects) {|objs, cond_key|
|
44
|
+
new_objs = nil
|
45
|
+
|
46
|
+
case cond_key
|
47
|
+
when :time
|
48
|
+
now = Time.now
|
49
|
+
new_objs = objs.select do |obj_k, obj_v|
|
50
|
+
obj_v[:time] < now - cond[cond_key]
|
51
|
+
end
|
52
|
+
else
|
53
|
+
raise "Invalid list option [#{cond_key}]"
|
54
|
+
end
|
55
|
+
|
56
|
+
new_objs
|
57
|
+
}.sort_by{|k, v| v[:time]}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def release_proc(proc_str)
|
62
|
+
lambda {
|
63
|
+
instance_eval(proc_str)
|
64
|
+
GcMonitor.release(self)
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
def included(base)
|
69
|
+
class << base
|
70
|
+
return if @gc_monitor_included
|
71
|
+
@gc_monitor_included = true
|
72
|
+
end
|
73
|
+
return unless base.private_methods.include?("initialize")
|
74
|
+
begin
|
75
|
+
base.__send__(:alias_method, :initialize_without_gc_monitor_pre, :initialize)
|
76
|
+
rescue NameError
|
77
|
+
return
|
78
|
+
end
|
79
|
+
base.__send__(:alias_method, :initialize, :initialize_with_gc_monitor_pre)
|
80
|
+
|
81
|
+
def base.method_added(name)
|
82
|
+
return unless name == :initialize
|
83
|
+
return if @made_initialize_with_gc_monitor_post
|
84
|
+
@made_initialize_with_gc_monitor_post = true
|
85
|
+
alias_method :initialize_without_gc_monitor_post, :initialize
|
86
|
+
alias_method :initialize, :initialize_with_gc_monitor_post
|
87
|
+
end
|
88
|
+
|
89
|
+
def base.release_hook(proc_str)
|
90
|
+
@@gc_monitor_release_hook = proc_str
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def tcp_server(host, port)
|
95
|
+
require 'socket'
|
96
|
+
|
97
|
+
Thread.new do
|
98
|
+
s = TCPServer.new(host, port)
|
99
|
+
loop do
|
100
|
+
Thread.new(s.accept) do |c|
|
101
|
+
while command_line = c.gets.strip
|
102
|
+
next if command_line.empty?
|
103
|
+
|
104
|
+
command, *args = command_line.split(/\s+/)
|
105
|
+
|
106
|
+
case command
|
107
|
+
when 'list'
|
108
|
+
cond = args.empty? ? {} : {:time => Integer(args[0])}
|
109
|
+
c.puts "now: #{Time.now}"
|
110
|
+
GcMonitor.list(cond).each do |obj|
|
111
|
+
c.puts(obj.inspect)
|
112
|
+
end
|
113
|
+
when 'quit'
|
114
|
+
c.close
|
115
|
+
Thread.exit
|
116
|
+
else
|
117
|
+
c.puts 'unknown command'
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
def regist_gc_monitor(caller)
|
128
|
+
GcMonitor.regist(self, caller)
|
129
|
+
@@gc_monitor_release_hook ||= nil
|
130
|
+
ObjectSpace.define_finalizer(self, GcMonitor.release_proc(@@gc_monitor_release_hook))
|
131
|
+
end
|
132
|
+
|
133
|
+
def initialize_with_gc_monitor_pre(*args, &blk)
|
134
|
+
initialize_without_gc_monitor_pre(*args, &blk)
|
135
|
+
regist_gc_monitor(caller)
|
136
|
+
end
|
137
|
+
|
138
|
+
def initialize_with_gc_monitor_post(*args, &blk)
|
139
|
+
initialize_without_gc_monitor_post(*args, &blk)
|
140
|
+
regist_gc_monitor(caller)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/gc_monitor.rb'}"
|
9
|
+
puts "Loading gc_monitor gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: komamitsu-gc_monitor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- komamitsu
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-08-09 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Ruby module for monitoring GC
|
17
|
+
email: komamitsu@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- History.txt
|
26
|
+
- Manifest.txt
|
27
|
+
- PostInstall.txt
|
28
|
+
- README.rdoc
|
29
|
+
- Rakefile
|
30
|
+
- lib/gc_monitor.rb
|
31
|
+
- script/console
|
32
|
+
- script/destroy
|
33
|
+
- script/generate
|
34
|
+
- test/test_gc_monitor.rb
|
35
|
+
- test/test_helper.rb
|
36
|
+
has_rdoc: true
|
37
|
+
homepage: http://github.com/komamitsu/gc_monitor
|
38
|
+
licenses:
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
version:
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
requirements: []
|
57
|
+
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 1.3.5
|
60
|
+
signing_key:
|
61
|
+
specification_version: 2
|
62
|
+
summary: Ruby module for monitoring GC
|
63
|
+
test_files: []
|
64
|
+
|