tunemygc 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +19 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +125 -0
- data/README.md +193 -0
- data/Rakefile +32 -0
- data/assets/discourse_bench.png +0 -0
- data/assets/tunemygc-graphic2x-80dac1571cacc70d9b272bb62ae9f6df.png +0 -0
- data/bin/tunemygc +6 -0
- data/ext/tunemygc/extconf.rb +92 -0
- data/ext/tunemygc/getRSS.c +122 -0
- data/ext/tunemygc/tunemygc_ext.c +156 -0
- data/ext/tunemygc/tunemygc_ext.h +21 -0
- data/lib/tunemygc/agent.rb +44 -0
- data/lib/tunemygc/cli.rb +86 -0
- data/lib/tunemygc/configurator.rb +25 -0
- data/lib/tunemygc/interposer.rb +60 -0
- data/lib/tunemygc/railtie.rb +13 -0
- data/lib/tunemygc/request_subscriber.rb +18 -0
- data/lib/tunemygc/snapshotter.rb +49 -0
- data/lib/tunemygc/spies/action_controller.rb +44 -0
- data/lib/tunemygc/subscriber.rb +14 -0
- data/lib/tunemygc/syncer.rb +91 -0
- data/lib/tunemygc/version.rb +5 -0
- data/lib/tunemygc.rb +22 -0
- data/test/fixtures.rb +9 -0
- data/test/helper.rb +30 -0
- data/test/test_agent.rb +21 -0
- data/test/test_interposer.rb +69 -0
- data/test/test_railtie.rb +21 -0
- data/test/test_snapshotter.rb +41 -0
- data/test/test_syncer.rb +146 -0
- data/tunemygc-1.0.gem +0 -0
- data/tunemygc.gemspec +50 -0
- metadata +194 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4401c1469c2636840a42026092dcd8d682d8825f
|
4
|
+
data.tar.gz: 5e44ddbc6336a4683b155393087bc8d39bc95cba
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 294670fabab8fa5203a580b6a4182ebb888d83d52710dfe8f82fb5545b6daefdeb3dad21d72c16b7bc2c6f0f0b3cc5ccd73d65bac3512468dd1c383f21a58568
|
7
|
+
data.tar.gz: 6bac1f88cad71533a4731332526dabc49e3b60b2d77c68a133dc7b6dea55ade00b8522cf372b2884777f572744028e70f59435303e27ac1ebfb1c3c8ad88f760
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
language: ruby
|
2
|
+
bundler_args: --quiet
|
3
|
+
rvm:
|
4
|
+
- 2.1.0
|
5
|
+
- 2.1.1
|
6
|
+
- 2.1.2
|
7
|
+
- 2.1.5
|
8
|
+
- 2.2.0
|
9
|
+
- ruby-head
|
10
|
+
script: "bundle exec rake"
|
11
|
+
env: RUBY_GC_TUNE=1 RUBY_GC_TUNE_DEBUG=1
|
12
|
+
gemfile:
|
13
|
+
- Gemfile
|
14
|
+
notifications:
|
15
|
+
recipients:
|
16
|
+
- lourens@methodmissing.com
|
17
|
+
branches:
|
18
|
+
only:
|
19
|
+
- master
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
tunemygc (1.0)
|
5
|
+
activesupport
|
6
|
+
certified
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
actionmailer (4.2.0)
|
12
|
+
actionpack (= 4.2.0)
|
13
|
+
actionview (= 4.2.0)
|
14
|
+
activejob (= 4.2.0)
|
15
|
+
mail (~> 2.5, >= 2.5.4)
|
16
|
+
rails-dom-testing (~> 1.0, >= 1.0.5)
|
17
|
+
actionpack (4.2.0)
|
18
|
+
actionview (= 4.2.0)
|
19
|
+
activesupport (= 4.2.0)
|
20
|
+
rack (~> 1.6.0)
|
21
|
+
rack-test (~> 0.6.2)
|
22
|
+
rails-dom-testing (~> 1.0, >= 1.0.5)
|
23
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.1)
|
24
|
+
actionview (4.2.0)
|
25
|
+
activesupport (= 4.2.0)
|
26
|
+
builder (~> 3.1)
|
27
|
+
erubis (~> 2.7.0)
|
28
|
+
rails-dom-testing (~> 1.0, >= 1.0.5)
|
29
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.1)
|
30
|
+
activejob (4.2.0)
|
31
|
+
activesupport (= 4.2.0)
|
32
|
+
globalid (>= 0.3.0)
|
33
|
+
activemodel (4.2.0)
|
34
|
+
activesupport (= 4.2.0)
|
35
|
+
builder (~> 3.1)
|
36
|
+
activerecord (4.2.0)
|
37
|
+
activemodel (= 4.2.0)
|
38
|
+
activesupport (= 4.2.0)
|
39
|
+
arel (~> 6.0)
|
40
|
+
activesupport (4.2.0)
|
41
|
+
i18n (~> 0.7)
|
42
|
+
json (~> 1.7, >= 1.7.7)
|
43
|
+
minitest (~> 5.1)
|
44
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
45
|
+
tzinfo (~> 1.1)
|
46
|
+
addressable (2.3.6)
|
47
|
+
arel (6.0.0)
|
48
|
+
builder (3.2.2)
|
49
|
+
certified (1.0.0)
|
50
|
+
crack (0.4.2)
|
51
|
+
safe_yaml (~> 1.0.0)
|
52
|
+
erubis (2.7.0)
|
53
|
+
globalid (0.3.0)
|
54
|
+
activesupport (>= 4.1.0)
|
55
|
+
hike (1.2.3)
|
56
|
+
i18n (0.7.0)
|
57
|
+
json (1.8.2)
|
58
|
+
loofah (2.0.1)
|
59
|
+
nokogiri (>= 1.5.9)
|
60
|
+
mail (2.6.3)
|
61
|
+
mime-types (>= 1.16, < 3)
|
62
|
+
mime-types (2.4.3)
|
63
|
+
mini_portile (0.6.2)
|
64
|
+
minitest (5.5.0)
|
65
|
+
multi_json (1.10.1)
|
66
|
+
nokogiri (1.6.5)
|
67
|
+
mini_portile (~> 0.6.0)
|
68
|
+
rack (1.6.0)
|
69
|
+
rack-test (0.6.2)
|
70
|
+
rack (>= 1.0)
|
71
|
+
rails (4.2.0)
|
72
|
+
actionmailer (= 4.2.0)
|
73
|
+
actionpack (= 4.2.0)
|
74
|
+
actionview (= 4.2.0)
|
75
|
+
activejob (= 4.2.0)
|
76
|
+
activemodel (= 4.2.0)
|
77
|
+
activerecord (= 4.2.0)
|
78
|
+
activesupport (= 4.2.0)
|
79
|
+
bundler (>= 1.3.0, < 2.0)
|
80
|
+
railties (= 4.2.0)
|
81
|
+
sprockets-rails
|
82
|
+
rails-deprecated_sanitizer (1.0.3)
|
83
|
+
activesupport (>= 4.2.0.alpha)
|
84
|
+
rails-dom-testing (1.0.5)
|
85
|
+
activesupport (>= 4.2.0.beta, < 5.0)
|
86
|
+
nokogiri (~> 1.6.0)
|
87
|
+
rails-deprecated_sanitizer (>= 1.0.1)
|
88
|
+
rails-html-sanitizer (1.0.1)
|
89
|
+
loofah (~> 2.0)
|
90
|
+
railties (4.2.0)
|
91
|
+
actionpack (= 4.2.0)
|
92
|
+
activesupport (= 4.2.0)
|
93
|
+
rake (>= 0.8.7)
|
94
|
+
thor (>= 0.18.1, < 2.0)
|
95
|
+
rake (10.4.2)
|
96
|
+
rake-compiler (0.9.5)
|
97
|
+
rake
|
98
|
+
safe_yaml (1.0.4)
|
99
|
+
sprockets (2.12.3)
|
100
|
+
hike (~> 1.2)
|
101
|
+
multi_json (~> 1.0)
|
102
|
+
rack (~> 1.0)
|
103
|
+
tilt (~> 1.1, != 1.3.0)
|
104
|
+
sprockets-rails (2.2.2)
|
105
|
+
actionpack (>= 3.0)
|
106
|
+
activesupport (>= 3.0)
|
107
|
+
sprockets (>= 2.8, < 4.0)
|
108
|
+
thor (0.19.1)
|
109
|
+
thread_safe (0.3.4)
|
110
|
+
tilt (1.4.1)
|
111
|
+
tzinfo (1.2.2)
|
112
|
+
thread_safe (~> 0.1)
|
113
|
+
webmock (1.20.4)
|
114
|
+
addressable (>= 2.3.6)
|
115
|
+
crack (>= 0.3.2)
|
116
|
+
|
117
|
+
PLATFORMS
|
118
|
+
ruby
|
119
|
+
|
120
|
+
DEPENDENCIES
|
121
|
+
rails
|
122
|
+
rake
|
123
|
+
rake-compiler (~> 0.9.3)
|
124
|
+
tunemygc!
|
125
|
+
webmock
|
data/README.md
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
## [TuneMyGC](https://www.tunemygc.com) - optimal MRI Ruby 2.1+ Garbage Collection
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/bear-metal/tunemygc.svg)](https://travis-ci.org/bear-metal/tunemygc)
|
4
|
+
|
5
|
+
The Ruby garbage collector has been flagged as the crux of Ruby performance and memory use for a long time. It has improved a lot over the last years, but there's still a lot to tune and control. *The default configuration is not suitable and optimal for Rails applications, and neither is there a one-size-fits-all set of tuned parameters that would suit every app.* However, hand-tuning the GC parameters is a slippery slope to navigate for most developers.
|
6
|
+
|
7
|
+
## We're fixing this
|
8
|
+
|
9
|
+
![tunemygc workflow diagram](https://raw.githubusercontent.com/bear-metal/tunemygc/master/assets/tunemygc-graphic2x-80dac1571cacc70d9b272bb62ae9f6df.png?token=AAABe8sM_ofiQkrCpNw7OYRbtHMLO9l5ks5UuQlYwA%3D%3D)
|
10
|
+
|
11
|
+
## Benefits
|
12
|
+
|
13
|
+
* Faster boot times
|
14
|
+
* Less major GC cycles during requests
|
15
|
+
* Less worst case memory usage - it's bound by sensible upper limits and growth factors
|
16
|
+
* No need to keep up to date with the C Ruby GC as an evolving moving target
|
17
|
+
* [in progress] A repeatable process to infer an optimal GC config for that app's current state within the context of a longer development cycle.
|
18
|
+
|
19
|
+
## Benchmarks
|
20
|
+
|
21
|
+
We used [Discourse](http://www.discourse.org) as our primary test harness as it's representative of most Rails applications and has been instrumental in asserting RGenC developments on Rails as well.
|
22
|
+
|
23
|
+
![tunemygc workflow diagram](https://raw.githubusercontent.com/bear-metal/tunemygc/master/assets/discourse_bench.png?token=AAABe8sM_ofiQkrCpNw7OYRbtHMLO9l5ks5UuQlYwA%3D%3D)
|
24
|
+
|
25
|
+
## Installing
|
26
|
+
|
27
|
+
#### OS X / Linux
|
28
|
+
|
29
|
+
Add to your Gemfile and run `bundle install`
|
30
|
+
|
31
|
+
``` sh
|
32
|
+
gem 'tunemygc', :require => 'tunemygc'
|
33
|
+
```
|
34
|
+
This gem linterposes itself into the Rails request/response lifecycles and piggy backs off the new GC events in Ruby 2.x for introspection. Tuning recommendations are handled through a web service at `https://tunemygc.com`. You will need a `rails > 4.1`, installation and MRI Ruby `2.1`, or later.
|
35
|
+
|
36
|
+
#### Windows
|
37
|
+
|
38
|
+
Has not been tested at all.
|
39
|
+
|
40
|
+
## Getting started
|
41
|
+
|
42
|
+
There isn't much setup other than adding the gem to your Gemfile and running a single command from your application root to register your application with the `https://tunemygc.com` service:
|
43
|
+
|
44
|
+
``` sh
|
45
|
+
Lourenss-MacBook-Air-2:discourse lourens$ bundle exec tunemygc -r lourens@bearmetal.eu
|
46
|
+
Application registered. Use RUBY_GC_TOKEN=08de9e8822c847244b31290cedfc1d51 in your environment.
|
47
|
+
```
|
48
|
+
|
49
|
+
We require a valid email address as a canonical reference for tuner tokens for your applications.
|
50
|
+
|
51
|
+
For the above command sequences, to sample your Rails app for tuning, run:
|
52
|
+
|
53
|
+
``` sh
|
54
|
+
RUBY_GC_TOKEN=08de9e8822c847244b31290cedfc1d51 RUBY_GC_TUNE=1 bundle exec rails s
|
55
|
+
```
|
56
|
+
|
57
|
+
The CLI interface supports retrieving configuration options for your application as well.
|
58
|
+
|
59
|
+
``` sh
|
60
|
+
Lourenss-MacBook-Air-2:discourse lourens$ bundle exec tunemygc
|
61
|
+
Usage: tunemygc [options]
|
62
|
+
-r, --register EMAIL Register this Rails app with the https://tunemygc.com service
|
63
|
+
-c, --config TOKEN Fetch the last known config for a given Rails app
|
64
|
+
-h, --help How to use the TuneMyGC agent CLI
|
65
|
+
```
|
66
|
+
|
67
|
+
## Configuration options
|
68
|
+
|
69
|
+
We fully embrace and encourage [12 factor](http://12factor.net) conventions and as such configuration is limited to a few environment variables. No config files and YAML or initializer cruft.
|
70
|
+
|
71
|
+
#### Basic
|
72
|
+
|
73
|
+
* `RUBY_GC_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
|
74
|
+
|
75
|
+
This application specific token is required for GC instrumentation. You can generate one from the CLI interface by registering for the service with a valid email address:
|
76
|
+
|
77
|
+
``` sh
|
78
|
+
Lourenss-MacBook-Air-2:discourse lourens$ bundle exec tunemygc -r lourens@bearmetal.eu
|
79
|
+
Application registered. Use RUBY_GC_TOKEN=08de9e8822c847244b31290cedfc1d51 in your environment.
|
80
|
+
```
|
81
|
+
|
82
|
+
* `RUBY_GC_TUNE=1`
|
83
|
+
|
84
|
+
Enables the interposer for taking a few lightweight snapshots and submitting them to `tunemygc.com`. Without this environment variable set, it won't interpose itself.
|
85
|
+
|
86
|
+
For the above command sequences, to sample my Rails app for tuning, I'd run:
|
87
|
+
|
88
|
+
``` sh
|
89
|
+
RUBY_GC_TOKEN=08de9e8822c847244b31290cedfc1d51 RUBY_GC_TUNE=1 bundle exec rails s
|
90
|
+
```
|
91
|
+
|
92
|
+
#### Advanced
|
93
|
+
|
94
|
+
* `RUBY_GC_TUNE_REQUESTS=x`
|
95
|
+
|
96
|
+
Controls the interposer lifetime for sampling requests. It will enable itself, then remove request instrumentation after `x` requests. A good minimum ballpark sample set would be 200
|
97
|
+
|
98
|
+
* `RUBY_GC_TUNE_DEBUG=1`
|
99
|
+
|
100
|
+
As above, but dumps snapshots to Rails logger or STDOUT prior to submission. Mostly for developer use/support.
|
101
|
+
|
102
|
+
## How do I use this?
|
103
|
+
|
104
|
+
This gem is only a lightweight agent and designed to not get in your way. There's not much workflow at the moment other than applying the suggested GC configuration to your application's environment.
|
105
|
+
|
106
|
+
#### Interpreting configurations
|
107
|
+
|
108
|
+
An instrumented process dumps a reccommended config to the Rails logger.
|
109
|
+
|
110
|
+
``` sh
|
111
|
+
[TuneMyGC] Syncing 688 snapshots
|
112
|
+
[TuneMyGC] ==== Recommended GC configs from https://tunemygc.com/configs/d739119e4abc38d42e183d1361991818.json
|
113
|
+
[TuneMyGC] export RUBY_GC_HEAP_INIT_SLOTS=382429
|
114
|
+
[TuneMyGC] export RUBY_GC_HEAP_FREE_SLOTS=603850
|
115
|
+
[TuneMyGC] export RUBY_GC_HEAP_GROWTH_FACTOR=1.2
|
116
|
+
[TuneMyGC] export RUBY_GC_HEAP_GROWTH_MAX_SLOTS=301925
|
117
|
+
[TuneMyGC] export RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=2.0
|
118
|
+
[TuneMyGC] export RUBY_GC_MALLOC_LIMIT=35818030
|
119
|
+
[TuneMyGC] export RUBY_GC_MALLOC_LIMIT_MAX=42981636
|
120
|
+
[TuneMyGC] export RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR=1.32
|
121
|
+
[TuneMyGC] export RUBY_GC_OLDMALLOC_LIMIT=32782669
|
122
|
+
[TuneMyGC] export RUBY_GC_OLDMALLOC_LIMIT_MAX=49174003.5
|
123
|
+
[TuneMyGC] export RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR=1.2
|
124
|
+
```
|
125
|
+
|
126
|
+
We're still in the process of building tools and a launcher shim around this. You can also retrieve the last known configuration for you app via the CLI interface:
|
127
|
+
|
128
|
+
``` sh
|
129
|
+
Lourenss-MacBook-Air-2:discourse lourens$ bundle exec tunemygc -c 3b8796e5627f97ec760f000d55d9b3f5
|
130
|
+
=== Suggested GC configuration:
|
131
|
+
|
132
|
+
export RUBY_GC_HEAP_INIT_SLOTS=382429
|
133
|
+
export RUBY_GC_HEAP_FREE_SLOTS=603850
|
134
|
+
export RUBY_GC_HEAP_GROWTH_FACTOR=1.2
|
135
|
+
export RUBY_GC_HEAP_GROWTH_MAX_SLOTS=301925
|
136
|
+
export RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=2.0
|
137
|
+
export RUBY_GC_MALLOC_LIMIT=35818030
|
138
|
+
export RUBY_GC_MALLOC_LIMIT_MAX=42981636
|
139
|
+
export RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR=1.32
|
140
|
+
export RUBY_GC_OLDMALLOC_LIMIT=32782669
|
141
|
+
export RUBY_GC_OLDMALLOC_LIMIT_MAX=49174003.5
|
142
|
+
export RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR=1.2
|
143
|
+
```
|
144
|
+
|
145
|
+
#### Heroku and 12 factor
|
146
|
+
|
147
|
+
We have a [Heroku](http://www.heroku.com) addon in Alpha testing and the Ruby GC lends itself well to tuning through 12 factor principles as it's designed around environment variables.
|
148
|
+
|
149
|
+
## Security and privacy concerns
|
150
|
+
|
151
|
+
We don't track any data specific to your application other than a simple environment header which allows us to pick the best tuner for your setup:
|
152
|
+
|
153
|
+
* Ruby version eg. "2.2.0"
|
154
|
+
* Rails version eg. "4.1.8"
|
155
|
+
* Compile time GC options eg. "["USE_RGENGC", "RGENGC_ESTIMATE_OLDMALLOC", "GC_ENABLE_LAZY_SWEEP"]"
|
156
|
+
* Compile time GC constants eg. "{"RVALUE_SIZE"=>40, "HEAP_OBJ_LIMIT"=>408, "HEAP_BITMAP_SIZE"=>56, "HEAP_BITMAP_PLANES"=>3}"
|
157
|
+
|
158
|
+
Samples hitting our tuner endpoint doesn't include any proprietary details from your application either - just data points about GC activity.
|
159
|
+
|
160
|
+
We do however ask for a valid email address as a canonical reference for tuner tokens for your applications.
|
161
|
+
|
162
|
+
## Feedback and issues
|
163
|
+
|
164
|
+
When trouble strikes, please file an [issue](https://www.github.com/bear-metal/tunemygc/issues) or email Lourens directly <lourens@bearmetal.eu>
|
165
|
+
|
166
|
+
[Bear Metal OÜ](http://www.bearmetal.eu) is also available for consulting around general Rails performance, heap dump analysis (more tools coming soon) and custom Ruby extension development.
|
167
|
+
|
168
|
+
## License
|
169
|
+
|
170
|
+
(The MIT License)
|
171
|
+
|
172
|
+
Copyright (c) 2015:
|
173
|
+
|
174
|
+
* [Bear Metal OÜ](http://bearmetal.eu)
|
175
|
+
|
176
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
177
|
+
a copy of this software and associated documentation files (the
|
178
|
+
'Software'), to deal in the Software without restriction, including
|
179
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
180
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
181
|
+
permit persons to whom the Software is furnished to do so, subject to
|
182
|
+
the following conditions:
|
183
|
+
|
184
|
+
The above copyright notice and this permission notice shall be
|
185
|
+
included in all copies or substantial portions of the Software.
|
186
|
+
|
187
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
188
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
189
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
190
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
191
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
192
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
193
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems' unless defined?(Gem)
|
4
|
+
require 'rake' unless defined?(Rake)
|
5
|
+
|
6
|
+
require 'rake/extensiontask'
|
7
|
+
require 'rake/testtask'
|
8
|
+
|
9
|
+
Rake::ExtensionTask.new('tunemygc') do |ext|
|
10
|
+
ext.name = 'tunemygc_ext'
|
11
|
+
ext.ext_dir = 'ext/tunemygc'
|
12
|
+
ext.lib_dir = "lib/tunemygc"
|
13
|
+
CLEAN.include 'lib/**/tunemygc_ext.*'
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Run tunemygc tests'
|
17
|
+
Rake::TestTask.new(:test) do |t|
|
18
|
+
t.libs << 'test'
|
19
|
+
t.pattern = "test/**/test_*.rb"
|
20
|
+
t.verbose = true
|
21
|
+
t.warning = true
|
22
|
+
end
|
23
|
+
|
24
|
+
namespace :debug do
|
25
|
+
desc "Run the test suite under gdb"
|
26
|
+
task :gdb do
|
27
|
+
system "gdb --args ruby rake"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
task :test => :compile
|
32
|
+
task :default => :test
|
Binary file
|
Binary file
|
data/bin/tunemygc
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'mkmf'
|
4
|
+
|
5
|
+
dir_config('tunemygc')
|
6
|
+
|
7
|
+
# Only defined for Ruby 2.1.x and 2.2.x
|
8
|
+
gc_events = have_const('RUBY_INTERNAL_EVENT_GC_END_SWEEP')
|
9
|
+
|
10
|
+
if gc_events
|
11
|
+
# From ko1/gc_tracer
|
12
|
+
# Piggy backs off the new methods to retrieve GC stats and latest cycle info without doing
|
13
|
+
# allocations - thus safe to call within a GC cycle and properly snapshot what we need without
|
14
|
+
# the results being skewed by postponed jobs, which isn't deterministic.
|
15
|
+
open("tunemygc_env.h", 'w'){|f|
|
16
|
+
gc_stat = GC.stat
|
17
|
+
gc_latest_info = GC.latest_gc_info
|
18
|
+
f.puts '#include "ruby/ruby.h"'
|
19
|
+
f.puts "static VALUE sym_gc_stat[#{gc_stat.keys.size}];"
|
20
|
+
f.puts "static VALUE sym_latest_gc_info[#{gc_latest_info.keys.size}];"
|
21
|
+
|
22
|
+
f.puts 'typedef struct {'
|
23
|
+
f.puts ' double ts;'
|
24
|
+
f.puts ' size_t peak_rss;'
|
25
|
+
f.puts ' size_t current_rss;'
|
26
|
+
f.puts ' VALUE stage;'
|
27
|
+
gc_stat.keys.each do |key|
|
28
|
+
f.puts " size_t #{key};"
|
29
|
+
end
|
30
|
+
gc_latest_info.each do |key, val|
|
31
|
+
f.puts " VALUE #{key};"
|
32
|
+
end
|
33
|
+
f.puts '} tunemygc_stat_record;'
|
34
|
+
|
35
|
+
f.puts "static void"
|
36
|
+
f.puts "tunemygc_set_stat_record(tunemygc_stat_record *record)"
|
37
|
+
f.puts "{"
|
38
|
+
#
|
39
|
+
gc_stat.keys.each.with_index{|k, i|
|
40
|
+
f.puts " record->#{k} = rb_gc_stat(sym_gc_stat[#{i}]);"
|
41
|
+
}
|
42
|
+
gc_latest_info.keys.each.with_index{|k, i|
|
43
|
+
f.puts " record->#{k} = rb_gc_latest_gc_info(sym_latest_gc_info[#{i}]);"
|
44
|
+
}
|
45
|
+
#
|
46
|
+
f.puts "}"
|
47
|
+
|
48
|
+
f.puts "static VALUE"
|
49
|
+
f.puts "tunemygc_get_stat_record(tunemygc_stat_record *record)"
|
50
|
+
f.puts "{"
|
51
|
+
#
|
52
|
+
f.puts " VALUE stat = rb_hash_new();"
|
53
|
+
f.puts " VALUE latest_info = rb_hash_new();"
|
54
|
+
f.puts " VALUE snapshot = rb_ary_new2(7);"
|
55
|
+
gc_stat.keys.each.with_index{|k, i|
|
56
|
+
f.puts " rb_hash_aset(stat, sym_gc_stat[#{i}], SIZET2NUM(record->#{k}));"
|
57
|
+
}
|
58
|
+
gc_latest_info.keys.each.with_index{|k, i|
|
59
|
+
f.puts " rb_hash_aset(latest_info, sym_latest_gc_info[#{i}], record->#{k});"
|
60
|
+
}
|
61
|
+
f.puts " rb_ary_store(snapshot, 0, DBL2NUM(record->ts));"
|
62
|
+
f.puts " rb_ary_store(snapshot, 1, SIZET2NUM(record->peak_rss));"
|
63
|
+
f.puts " rb_ary_store(snapshot, 2, SIZET2NUM(record->current_rss));"
|
64
|
+
f.puts " rb_ary_store(snapshot, 3, record->stage);"
|
65
|
+
f.puts " rb_ary_store(snapshot, 4, stat);"
|
66
|
+
f.puts " rb_ary_store(snapshot, 5, latest_info);"
|
67
|
+
f.puts " rb_ary_store(snapshot, 6, Qnil);"
|
68
|
+
f.puts " return snapshot;"
|
69
|
+
#
|
70
|
+
f.puts "}"
|
71
|
+
|
72
|
+
f.puts "static void"
|
73
|
+
f.puts "tunemygc_setup_trace_symbols(void)"
|
74
|
+
f.puts "{"
|
75
|
+
#
|
76
|
+
gc_stat.keys.each.with_index{|k, i|
|
77
|
+
f.puts " sym_gc_stat[#{i}] = ID2SYM(rb_intern(\"#{k}\"));"
|
78
|
+
}
|
79
|
+
gc_latest_info.keys.each.with_index{|k, i|
|
80
|
+
f.puts " sym_latest_gc_info[#{i}] = ID2SYM(rb_intern(\"#{k}\"));"
|
81
|
+
}
|
82
|
+
#
|
83
|
+
f.puts "}"
|
84
|
+
}
|
85
|
+
|
86
|
+
create_makefile('tunemygc/tunemygc_ext')
|
87
|
+
else
|
88
|
+
# do nothing if we're not running within Ruby 2.1.x or 2.2.x
|
89
|
+
File.open('Makefile', 'w') do |f|
|
90
|
+
f.puts "install:\n\t\n"
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
/*
|
2
|
+
* Author: David Robert Nadeau
|
3
|
+
* Site: http://NadeauSoftware.com/
|
4
|
+
* License: Creative Commons Attribution 3.0 Unported License
|
5
|
+
* http://creativecommons.org/licenses/by/3.0/deed.en_US
|
6
|
+
*/
|
7
|
+
|
8
|
+
#if defined(_WIN32)
|
9
|
+
#include <windows.h>
|
10
|
+
#include <psapi.h>
|
11
|
+
|
12
|
+
#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
|
13
|
+
#include <unistd.h>
|
14
|
+
#include <sys/resource.h>
|
15
|
+
|
16
|
+
#if defined(__APPLE__) && defined(__MACH__)
|
17
|
+
#include <mach/mach.h>
|
18
|
+
|
19
|
+
#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
|
20
|
+
#include <fcntl.h>
|
21
|
+
#include <procfs.h>
|
22
|
+
|
23
|
+
#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
|
24
|
+
#include <stdio.h>
|
25
|
+
|
26
|
+
#endif
|
27
|
+
|
28
|
+
#else
|
29
|
+
#error "Cannot define getPeakRSS( ) or getCurrentRSS( ) for an unknown OS."
|
30
|
+
#endif
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Returns the peak (maximum so far) resident set size (physical
|
38
|
+
* memory use) measured in bytes, or zero if the value cannot be
|
39
|
+
* determined on this OS.
|
40
|
+
*/
|
41
|
+
size_t getPeakRSS( )
|
42
|
+
{
|
43
|
+
#if defined(_WIN32)
|
44
|
+
/* Windows -------------------------------------------------- */
|
45
|
+
PROCESS_MEMORY_COUNTERS info;
|
46
|
+
GetProcessMemoryInfo( GetCurrentProcess( ), &info, sizeof(info) );
|
47
|
+
return (size_t)info.PeakWorkingSetSize;
|
48
|
+
|
49
|
+
#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
|
50
|
+
/* AIX and Solaris ------------------------------------------ */
|
51
|
+
struct psinfo psinfo;
|
52
|
+
int fd = -1;
|
53
|
+
if ( (fd = open( "/proc/self/psinfo", O_RDONLY )) == -1 )
|
54
|
+
return (size_t)0L; /* Can't open? */
|
55
|
+
if ( read( fd, &psinfo, sizeof(psinfo) ) != sizeof(psinfo) )
|
56
|
+
{
|
57
|
+
close( fd );
|
58
|
+
return (size_t)0L; /* Can't read? */
|
59
|
+
}
|
60
|
+
close( fd );
|
61
|
+
return (size_t)(psinfo.pr_rssize * 1024L);
|
62
|
+
|
63
|
+
#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
|
64
|
+
/* BSD, Linux, and OSX -------------------------------------- */
|
65
|
+
struct rusage rusage;
|
66
|
+
getrusage( RUSAGE_SELF, &rusage );
|
67
|
+
#if defined(__APPLE__) && defined(__MACH__)
|
68
|
+
return (size_t)rusage.ru_maxrss;
|
69
|
+
#else
|
70
|
+
return (size_t)(rusage.ru_maxrss * 1024L);
|
71
|
+
#endif
|
72
|
+
|
73
|
+
#else
|
74
|
+
/* Unknown OS ----------------------------------------------- */
|
75
|
+
return (size_t)0L; /* Unsupported. */
|
76
|
+
#endif
|
77
|
+
}
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
/**
|
84
|
+
* Returns the current resident set size (physical memory use) measured
|
85
|
+
* in bytes, or zero if the value cannot be determined on this OS.
|
86
|
+
*/
|
87
|
+
size_t getCurrentRSS( )
|
88
|
+
{
|
89
|
+
#if defined(_WIN32)
|
90
|
+
/* Windows -------------------------------------------------- */
|
91
|
+
PROCESS_MEMORY_COUNTERS info;
|
92
|
+
GetProcessMemoryInfo( GetCurrentProcess( ), &info, sizeof(info) );
|
93
|
+
return (size_t)info.WorkingSetSize;
|
94
|
+
|
95
|
+
#elif defined(__APPLE__) && defined(__MACH__)
|
96
|
+
/* OSX ------------------------------------------------------ */
|
97
|
+
struct mach_task_basic_info info;
|
98
|
+
mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
|
99
|
+
if ( task_info( mach_task_self( ), MACH_TASK_BASIC_INFO,
|
100
|
+
(task_info_t)&info, &infoCount ) != KERN_SUCCESS )
|
101
|
+
return (size_t)0L; /* Can't access? */
|
102
|
+
return (size_t)info.resident_size;
|
103
|
+
|
104
|
+
#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
|
105
|
+
/* Linux ---------------------------------------------------- */
|
106
|
+
long rss = 0L;
|
107
|
+
FILE* fp = NULL;
|
108
|
+
if ( (fp = fopen( "/proc/self/statm", "r" )) == NULL )
|
109
|
+
return (size_t)0L; /* Can't open? */
|
110
|
+
if ( fscanf( fp, "%*s%ld", &rss ) != 1 )
|
111
|
+
{
|
112
|
+
fclose( fp );
|
113
|
+
return (size_t)0L; /* Can't read? */
|
114
|
+
}
|
115
|
+
fclose( fp );
|
116
|
+
return (size_t)rss * (size_t)sysconf( _SC_PAGESIZE);
|
117
|
+
|
118
|
+
#else
|
119
|
+
/* AIX, BSD, Solaris, and Unknown OS ------------------------ */
|
120
|
+
return (size_t)0L; /* Unsupported. */
|
121
|
+
#endif
|
122
|
+
}
|