gc_hacks 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 +5 -0
- data/Gemfile +4 -0
- data/LICENCSE +23 -0
- data/README.rdoc +54 -0
- data/Rakefile +16 -0
- data/bin/dump_heap +3 -0
- data/bin/start_gc_trace +3 -0
- data/bin/stop_gc_trace +3 -0
- data/gc_hacks.gemspec +21 -0
- data/init.rb +15 -0
- data/lib/gc_hacks/version.rb +3 -0
- data/lib/gc_hacks.rb +137 -0
- data/rails/init.rb +1 -0
- metadata +82 -0
data/Gemfile
ADDED
data/LICENCSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2009 {XING AG}[http://www.xing.com/]
|
4
|
+
Copyright (c) 2009 {Stefan Kaes}[http://railsexpress.de]
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
THE SOFTWARE.
|
23
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
= GC Hacks for Ruby
|
2
|
+
|
3
|
+
A plugin for Rails which allows one to obtain heap dumps from a running rails app for
|
4
|
+
example. It should work for mongrel and passenger hosted apps.
|
5
|
+
|
6
|
+
|
7
|
+
== Author
|
8
|
+
|
9
|
+
Stefan Kaes <skaes@railexpress.de>
|
10
|
+
|
11
|
+
|
12
|
+
== Usage
|
13
|
+
|
14
|
+
Obtain the processid of a rails process, then invoke one of the following commands:
|
15
|
+
|
16
|
+
* start_gc_trace pid # turns on gc tracing in the given process
|
17
|
+
* stop_gc_trace pid # stop gc tracing
|
18
|
+
* dump_heap pid # dump current heap to "#{tmp_dir}/heap.#{Process.pid}.#{@heap_dump_count}.dump"
|
19
|
+
|
20
|
+
|
21
|
+
== Prerequisites
|
22
|
+
|
23
|
+
You won't have much fun with this code unless you use a patched ruby which supports dumping
|
24
|
+
heap information, compiled with GC_DEBUG enabled.
|
25
|
+
|
26
|
+
I recommend my patched version of ruby 1.8.7, which you can get as follows:
|
27
|
+
|
28
|
+
git clone git://github.com/skaes/matzruby.git
|
29
|
+
cd matzruby
|
30
|
+
git checkout ruby187pl202patched
|
31
|
+
autoconf
|
32
|
+
./configure --enable-gcdebug --prefix=/usr/local/ruby187pl202
|
33
|
+
make
|
34
|
+
sudo make install
|
35
|
+
sudo make install-doc
|
36
|
+
|
37
|
+
|
38
|
+
== Installation
|
39
|
+
|
40
|
+
Place it in vendor/plugins, as usual.
|
41
|
+
|
42
|
+
|
43
|
+
== Acknowledgments
|
44
|
+
|
45
|
+
Development of this plugin was sponsored by Xing (http://www.xing.com)
|
46
|
+
|
47
|
+
|
48
|
+
== TODO
|
49
|
+
|
50
|
+
* I think this code should be a gem.
|
51
|
+
* move railsbench analyze_heap_dump command into the gem.
|
52
|
+
|
53
|
+
|
54
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/testtask'
|
6
|
+
require 'rake/rdoctask'
|
7
|
+
|
8
|
+
Rake::RDocTask.new do |rdoc|
|
9
|
+
rdoc.rdoc_dir = 'rdoc'
|
10
|
+
rdoc.title = 'gc_hacks'
|
11
|
+
rdoc.options << '--line-numbers' << '--inline-source' << '--quiet'
|
12
|
+
rdoc.rdoc_files.include('README*')
|
13
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
14
|
+
end
|
15
|
+
|
16
|
+
task :default => :rdoc
|
data/bin/dump_heap
ADDED
data/bin/start_gc_trace
ADDED
data/bin/stop_gc_trace
ADDED
data/gc_hacks.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "gc_hacks/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "gc_hacks"
|
7
|
+
s.version = GCHacks::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Stefan Kaes"]
|
10
|
+
s.email = ["skaes@railsexpress.de"]
|
11
|
+
s.homepage = "https://github.com/skaes/gc_hacks"
|
12
|
+
s.summary = %q{GC tracing/Heap dumping support for Rails processes}
|
13
|
+
s.description = %q{This gem allows you to send GC related commands to a running Rails process}
|
14
|
+
|
15
|
+
s.rubyforge_project = "gc_hacks"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
if defined?(PhusionPassenger)
|
2
|
+
class PhusionPassenger::AbstractRequestHandler
|
3
|
+
alias_method :original_reset_signal_handlers, :reset_signal_handlers
|
4
|
+
|
5
|
+
def reset_signal_handlers
|
6
|
+
original_reset_signal_handlers
|
7
|
+
GCHacks.install_signal_handlers
|
8
|
+
end
|
9
|
+
|
10
|
+
protected :original_reset_signal_handlers, :reset_signal_handlers
|
11
|
+
end
|
12
|
+
|
13
|
+
else
|
14
|
+
GCHacks.install_signal_handlers
|
15
|
+
end
|
data/lib/gc_hacks.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
module GCHacks
|
4
|
+
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def logger
|
8
|
+
@logger ||=
|
9
|
+
if defined?(Rails)
|
10
|
+
Rails.logger
|
11
|
+
elsif defined?(RAILS_DEFAULT_LOGGER)
|
12
|
+
RAILS_DEFAULT_LOGGER
|
13
|
+
else
|
14
|
+
Logger.new($stdout)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def root
|
19
|
+
@root ||=
|
20
|
+
if defined?(Rails)
|
21
|
+
Rails.root.to_s
|
22
|
+
elsif defined?(RAILS_ROOT)
|
23
|
+
RAILS_ROOT
|
24
|
+
else
|
25
|
+
find_rails_root
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_rails_root
|
30
|
+
while !cwd_is_a_rails_project?
|
31
|
+
if FileUtils.pwd == "/"
|
32
|
+
$stderr.puts "could not determine rails project root. please cd into your rails project"
|
33
|
+
exit 1
|
34
|
+
end
|
35
|
+
FileUtils.cd ".."
|
36
|
+
end
|
37
|
+
FileUtils.pwd
|
38
|
+
end
|
39
|
+
|
40
|
+
def cwd_is_a_rails_project?
|
41
|
+
File.exist?("config/environment.rb") && File.directory?("tmp") && File.directory?("log")
|
42
|
+
end
|
43
|
+
|
44
|
+
def install_signal_handlers
|
45
|
+
# WINCH is the only signal we can receive without mongrel/passenger side effects
|
46
|
+
trap("WINCH"){ check_and_run_commands }
|
47
|
+
end
|
48
|
+
|
49
|
+
def send_command(cmd, pid)
|
50
|
+
File.open(CMD_FILE, "w"){|f| f.puts cmd}
|
51
|
+
Process.kill("WINCH", pid)
|
52
|
+
end
|
53
|
+
|
54
|
+
def check_and_run_commands
|
55
|
+
read_command_file.each do |cmd|
|
56
|
+
case cmd.chomp
|
57
|
+
when 'HEAPDUMP' then heap_dump
|
58
|
+
when 'STARTTRACE' then start_trace
|
59
|
+
when 'STOPTRACE' then stop_trace
|
60
|
+
else
|
61
|
+
logger.info "unknown gc command: '#{cmd}'"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
ensure
|
65
|
+
remove_command_file
|
66
|
+
end
|
67
|
+
|
68
|
+
CMD_FILE = File.expand_path("#{root}/tmp/gc_command.txt")
|
69
|
+
|
70
|
+
def read_command_file
|
71
|
+
File.exist?(CMD_FILE) ? File.read(CMD_FILE) : []
|
72
|
+
end
|
73
|
+
|
74
|
+
def remove_command_file
|
75
|
+
File.exist?(CMD_FILE) && File.unlink(CMD_FILE)
|
76
|
+
end
|
77
|
+
|
78
|
+
def can_trace?
|
79
|
+
GC.respond_to?(:log_file)
|
80
|
+
end
|
81
|
+
|
82
|
+
def can_dump?
|
83
|
+
GC.respond_to?(:dump_file_and_line_info)
|
84
|
+
end
|
85
|
+
|
86
|
+
def log_dir
|
87
|
+
@log_dir ||= File.expand_path("#{root}/log")
|
88
|
+
end
|
89
|
+
|
90
|
+
def log_dir=(dir)
|
91
|
+
@log_dir = File.expand_path(dir)
|
92
|
+
end
|
93
|
+
|
94
|
+
def tmp_dir
|
95
|
+
@tmp_dir ||= File.expand_path("#{root}/tmp")
|
96
|
+
end
|
97
|
+
|
98
|
+
def tmp_dir=(dir)
|
99
|
+
@tmp_dir = File.expand_path(dir)
|
100
|
+
end
|
101
|
+
|
102
|
+
def start_trace
|
103
|
+
unless can_trace?
|
104
|
+
logger.info "cannot start GC trace. GC.enable_trace is undefined"
|
105
|
+
return
|
106
|
+
end
|
107
|
+
GC.log_file "#{log_dir}/gctrace-#{Process.pid}.log" unless GC.log_file
|
108
|
+
GC.enable_trace
|
109
|
+
logger.info "GC-tracing: enabled"
|
110
|
+
end
|
111
|
+
|
112
|
+
def stop_trace
|
113
|
+
unless can_trace?
|
114
|
+
logger.info "cannot stop GC trace. GC.disable_trace is undefined"
|
115
|
+
return
|
116
|
+
end
|
117
|
+
GC.disable_trace
|
118
|
+
logger.info "GC-tracing: disabled"
|
119
|
+
end
|
120
|
+
|
121
|
+
def heap_dump
|
122
|
+
unless can_dump?
|
123
|
+
logger.info "cannot dump heap. GC.dump_file_and_line_info is undefined"
|
124
|
+
return
|
125
|
+
end
|
126
|
+
@heap_dump_count ||= 0
|
127
|
+
filename = "#{tmp_dir}/heap.#{Process.pid}.#{@heap_dump_count}.dump"
|
128
|
+
msg = "** Dumping heap ..."
|
129
|
+
$stderr.puts msg; logger.info msg
|
130
|
+
GC.start;GC.start # two calls to get rid of finalizer created garbage
|
131
|
+
GC.dump_file_and_line_info(filename, true)
|
132
|
+
msg = "** Run 'railsbench analyze_heap_dump #{filename}' to analyze."
|
133
|
+
$stderr.puts msg; logger.info msg
|
134
|
+
@heap_dump_count += 1
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path('../../init.rb', __FILE__)
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gc_hacks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Stefan Kaes
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-04-08 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: This gem allows you to send GC related commands to a running Rails process
|
23
|
+
email:
|
24
|
+
- skaes@railsexpress.de
|
25
|
+
executables:
|
26
|
+
- dump_heap
|
27
|
+
- start_gc_trace
|
28
|
+
- stop_gc_trace
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- Gemfile
|
36
|
+
- LICENCSE
|
37
|
+
- README.rdoc
|
38
|
+
- Rakefile
|
39
|
+
- bin/dump_heap
|
40
|
+
- bin/start_gc_trace
|
41
|
+
- bin/stop_gc_trace
|
42
|
+
- gc_hacks.gemspec
|
43
|
+
- init.rb
|
44
|
+
- lib/gc_hacks.rb
|
45
|
+
- lib/gc_hacks/version.rb
|
46
|
+
- rails/init.rb
|
47
|
+
has_rdoc: true
|
48
|
+
homepage: https://github.com/skaes/gc_hacks
|
49
|
+
licenses: []
|
50
|
+
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 3
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project: gc_hacks
|
77
|
+
rubygems_version: 1.3.7
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: GC tracing/Heap dumping support for Rails processes
|
81
|
+
test_files: []
|
82
|
+
|