gc_hacks 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|