jemal 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +217 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/jemal.gemspec +29 -0
- data/lib/jemal/interface.rb +148 -0
- data/lib/jemal/version.rb +3 -0
- data/lib/jemal.rb +294 -0
- metadata +141 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 77f60c008c7ce2c7830ad626989eadcee68e7b65
|
4
|
+
data.tar.gz: 092152b3eb68c593cc1078f2425a74358a261332
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1e0a37a570b342421d8c655b8598625177a6b3ce229739472aed54aa6314919e0666bfd7cf72186cb8949133e137799a9ee7a97365b8ec010be1e5d2f8ceffac
|
7
|
+
data.tar.gz: 80fb210de552cdb7513d4de2c54be69f9fdcc834d5c2c432ac2ff4b81a3ed9fefd4febbe6b4a64b3c150e4236800fd949c8ff8114829f308cadfa5c9e48e48dd
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 oleg dashevskii
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,217 @@
|
|
1
|
+
# Jemal
|
2
|
+
|
3
|
+
This gem provides interface to your MRI built with [jemalloc](canonware.com/jemalloc/).
|
4
|
+
Of course you heard that
|
5
|
+
Ruby 2.2.0 [introduced jemalloc support](https://www.ruby-lang.org/en/news/2014/12/25/ruby-2-2-0-released/).
|
6
|
+
|
7
|
+
Primary goal of this gem is to provide access to jemalloc statistics.
|
8
|
+
|
9
|
+
Currently jemalloc 3.6.0 is supported (certain Ruby gems can't yet be built
|
10
|
+
with 4.0.0 due to stdbool.h conflict).
|
11
|
+
|
12
|
+
Note that there's another [jemalloc-related gem](https://github.com/kzk/jemalloc-rb) on RubyGems.
|
13
|
+
It doesn't provide interface to builtin jemalloc, but rather aims at injecting jemalloc in runtime with LD_PRELOAD.
|
14
|
+
|
15
|
+
## Jemalloc installation
|
16
|
+
|
17
|
+
Ubuntu:
|
18
|
+
|
19
|
+
$ sudo apt-get install libjemalloc-dev
|
20
|
+
|
21
|
+
OS X:
|
22
|
+
|
23
|
+
$ brew install jemalloc
|
24
|
+
|
25
|
+
|
26
|
+
Note that if you want to use allocation profiling, you'll have to build
|
27
|
+
jemalloc from source (`./configure --enable-prof`). Both ubuntu and homebrew versions
|
28
|
+
are built without this option.
|
29
|
+
|
30
|
+
## Ruby with jemalloc installation
|
31
|
+
|
32
|
+
|
33
|
+
Instructions are [here](http://groguelon.fr/post/106221222318/how-to-install-ruby-220-with-jemalloc-support).
|
34
|
+
|
35
|
+
## Gem installation
|
36
|
+
|
37
|
+
Add this line to your application's Gemfile:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
gem 'jemal'
|
41
|
+
```
|
42
|
+
|
43
|
+
And then execute:
|
44
|
+
|
45
|
+
$ bundle
|
46
|
+
|
47
|
+
Or install it yourself as:
|
48
|
+
|
49
|
+
$ gem install jemal
|
50
|
+
|
51
|
+
## Usage
|
52
|
+
|
53
|
+
To be sure that your Ruby is jemalloc-powered, use `Jemal.jemalloc_builtin?`.
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
require 'jemal'
|
57
|
+
|
58
|
+
Jemal.jemalloc_builtin?
|
59
|
+
# => true
|
60
|
+
```
|
61
|
+
|
62
|
+
You can also check jemalloc version:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
Jemal.version
|
66
|
+
# => "3.6.0-0-g46c0af68bd248b04df75e4f92d5fb804c3d75340"
|
67
|
+
```
|
68
|
+
|
69
|
+
Look at the flags jemalloc has been built with:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
Jemal.build_configuration
|
73
|
+
# => {:debug=>false, :dss=>false, :fill=>true, :lazy_lock=>false, :mremap=>false, :munmap=>true,
|
74
|
+
# :prof=>false, :prof_libgcc=>false, :prof_libunwind=>false, :stats=>true, :tcache=>true,
|
75
|
+
# :tls=>false, :utrace=>false, :valgrind=>false, :xmalloc=>false}
|
76
|
+
```
|
77
|
+
|
78
|
+
Most of these options aren't very interesting, however be sure that `Jemal.build_configuration[:stats]` is `true`,
|
79
|
+
otherwise you won't be able to collect statistics.
|
80
|
+
|
81
|
+
Find out how many arenas are used by jemalloc:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
Jemal.arenas_count
|
85
|
+
# => 16
|
86
|
+
```
|
87
|
+
|
88
|
+
Each arena is basically a separate allocator with its own memory. Multiple arenas provide more speed for heavily
|
89
|
+
multithreaded applications by allowing parallel threads to allocate memory simultaneously. This doesn't help MRI much, because its heap is still shared between all threads, so we can set `MALLOC_CONF=narenas:1` in environment and have single arena.
|
90
|
+
|
91
|
+
Now to statistics:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
Jemal.stats
|
95
|
+
# => {:allocated=>23587776, :active=>24416256, :metadata=>0, :resident=>0, :mapped=>29360128,
|
96
|
+
# :cactive=>37748736, :chunks=>{:current=>7, :total=>7, :high=>7},
|
97
|
+
# :arenas=>[ ... ]}
|
98
|
+
```
|
99
|
+
|
100
|
+
The returned hash is quite big, it contains joint statistics, as well as statistics for each arena. Read
|
101
|
+
[jemalloc man page](http://www.unix.com/man-page/freebsd/3/jemalloc/) to be able to understand the figures (look for `stats.*`).
|
102
|
+
|
103
|
+
If you just want human-compatible stats in text form and `STDERR` is okay, you are also covered:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
Jemal.stats_print
|
107
|
+
```
|
108
|
+
|
109
|
+
You'll get a jemalloc stats dump which looks like this:
|
110
|
+
|
111
|
+
```
|
112
|
+
___ Begin jemalloc statistics ___
|
113
|
+
Version: 3.6.0-0-g46c0af68bd248b04df75e4f92d5fb804c3d75340
|
114
|
+
Assertions disabled
|
115
|
+
Run-time option settings:
|
116
|
+
opt.abort: false
|
117
|
+
opt.lg_chunk: 22
|
118
|
+
opt.dss: "secondary"
|
119
|
+
opt.narenas: 16
|
120
|
+
opt.lg_dirty_mult: 3
|
121
|
+
opt.stats_print: false
|
122
|
+
opt.junk: false
|
123
|
+
opt.quarantine: 0
|
124
|
+
opt.redzone: false
|
125
|
+
opt.zero: false
|
126
|
+
opt.tcache: true
|
127
|
+
opt.lg_tcache_max: 15
|
128
|
+
CPUs: 4
|
129
|
+
Arenas: 16
|
130
|
+
Pointer size: 8
|
131
|
+
Quantum size: 16
|
132
|
+
Page size: 4096
|
133
|
+
Min active:dirty page ratio per arena: 8:1
|
134
|
+
Maximum thread-cached size class: 32768
|
135
|
+
Chunk size: 4194304 (2^22)
|
136
|
+
Allocated: 23781096, active: 25649152, mapped: 33554432
|
137
|
+
Current active ceiling: 37748736
|
138
|
+
chunks: nchunks highchunks curchunks
|
139
|
+
8 8 8
|
140
|
+
huge: nmalloc ndalloc allocated
|
141
|
+
0 0 0
|
142
|
+
|
143
|
+
arenas[0]:
|
144
|
+
assigned threads: 1
|
145
|
+
dss allocation precedence: disabled
|
146
|
+
dirty pages: 6262:52 active:dirty, 0 sweeps, 0 madvises, 0 purged
|
147
|
+
allocated nmalloc ndalloc nrequests
|
148
|
+
small: 17493736 317316 120581 678822
|
149
|
+
large: 6287360 432 56 1269
|
150
|
+
total: 23781096 317748 120637 680091
|
151
|
+
active: 25649152
|
152
|
+
mapped: 29360128
|
153
|
+
bins: bin size regs pgs allocated nmalloc ndalloc nrequests nfills nflushes newruns reruns curruns
|
154
|
+
0 8 501 1 87688 12194 1233 19899 246 25 23 32 23
|
155
|
+
1 16 252 1 200480 22361 9831 31078 279 113 81 162 52
|
156
|
+
2 32 126 1 1017088 36067 4283 81314 433 57 267 373 264
|
157
|
+
3 48 84 1 4174848 137466 50490 250234 1644 603 1140 3707 1072
|
158
|
+
4 64 63 1 331392 9969 4791 35396 335 98 115 273 104
|
159
|
+
5 80 50 1 691840 14013 5365 52709 353 122 197 682 182
|
160
|
+
6 96 84 2 269664 3921 1112 12273 87 47 45 55 40
|
161
|
+
7 112 72 2 433664 4481 609 7083 95 32 61 65 57
|
162
|
+
8 128 63 2 207488 2086 465 11407 84 35 32 42 28
|
163
|
+
9 160 51 2 2482880 49495 33977 106753 997 672 486 2895 313
|
164
|
+
10 192 63 3 205632 1643 572 3659 61 42 24 24 19
|
165
|
+
11 224 72 4 422688 2339 452 3222 62 35 31 17 28
|
166
|
+
12 256 63 4 152320 1049 454 1383 51 39 19 11 11
|
167
|
+
13 320 63 5 2549760 8587 619 11026 179 27 135 39 132
|
168
|
+
14 384 63 6 167040 749 314 1105 29 38 11 3 8
|
169
|
+
15 448 63 7 689472 2861 1322 5053 227 51 38 39 29
|
170
|
+
16 512 63 8 161280 551 236 552 35 35 11 5 6
|
171
|
+
17 640 51 8 693120 3729 2646 39605 119 74 42 224 25
|
172
|
+
18 768 47 9 328704 677 249 723 36 36 15 8 10
|
173
|
+
19 896 45 10 272384 560 256 660 36 35 14 9 7
|
174
|
+
20 1024 63 16 273408 475 208 464 31 37 7 4 5
|
175
|
+
21 1280 51 16 478720 686 312 1367 39 38 14 9 8
|
176
|
+
22 1536 42 16 259584 323 154 545 40 34 6 3 5
|
177
|
+
23 1792 38 17 236544 304 172 454 30 30 7 5 4
|
178
|
+
24 2048 65 33 182272 236 147 198 29 30 3 1 2
|
179
|
+
25 2560 52 33 245760 227 131 393 24 30 4 1 3
|
180
|
+
26 3072 43 33 181248 156 97 173 30 30 3 7 2
|
181
|
+
27 3584 39 35 96768 111 84 94 21 24 2 0 1
|
182
|
+
large: size pages nmalloc ndalloc nrequests curruns
|
183
|
+
4096 1 63 21 520 42
|
184
|
+
8192 2 89 17 465 72
|
185
|
+
12288 3 21 9 23 12
|
186
|
+
16384 4 243 3 244 240
|
187
|
+
20480 5 2 2 2 0
|
188
|
+
24576 6 2 2 2 0
|
189
|
+
28672 7 3 1 3 2
|
190
|
+
32768 8 6 1 7 5
|
191
|
+
[11]
|
192
|
+
81920 20 1 0 1 1
|
193
|
+
[2]
|
194
|
+
94208 23 1 0 1 1
|
195
|
+
[232]
|
196
|
+
1048576 256 1 0 1 1
|
197
|
+
[762]
|
198
|
+
--- End jemalloc statistics ---
|
199
|
+
```
|
200
|
+
|
201
|
+
Other methods of less interest:
|
202
|
+
|
203
|
+
```ruby
|
204
|
+
Jemal.options # Returns runtime jemalloc options
|
205
|
+
# => { :abort=>false, ... }
|
206
|
+
|
207
|
+
Jemal.sizes # Returns some constant sizes (page, bin, lrun)
|
208
|
+
# => { :page_size=>4096, ... }
|
209
|
+
```
|
210
|
+
|
211
|
+
## Contributing
|
212
|
+
|
213
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/be9/jemal.
|
214
|
+
|
215
|
+
## License
|
216
|
+
|
217
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "jemal"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/jemal.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'jemal/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "jemal"
|
8
|
+
spec.version = Jemal::VERSION
|
9
|
+
spec.authors = ["oleg dashevskii"]
|
10
|
+
spec.email = ["olegdashevskii@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Interface to jemalloc}
|
13
|
+
spec.description = %q{Means to access jemalloc options and statistics for MRI compiled with jemalloc support.}
|
14
|
+
spec.homepage = "https://github.com/be9/jemal"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency 'ffi', '~> 1.9.10'
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "minitest"
|
27
|
+
spec.add_development_dependency "pry"
|
28
|
+
spec.add_development_dependency "log_buddy"
|
29
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module Jemal
|
4
|
+
module Interface
|
5
|
+
extend FFI::Library
|
6
|
+
|
7
|
+
ffi_lib [FFI::CURRENT_PROCESS, 'jemalloc']
|
8
|
+
|
9
|
+
# int mallctl(const char *name, void *oldp, size_t *oldlenp,
|
10
|
+
# void *newp, size_t newlen);
|
11
|
+
attach_function :mallctl, [:string, :pointer, :pointer, :pointer, :size_t], :int
|
12
|
+
|
13
|
+
# void malloc_stats_print(void (*write_cb) (void *, const char *),
|
14
|
+
# void *cbopaque, const char *opts);
|
15
|
+
# TODO
|
16
|
+
attach_function :malloc_stats_print, [:pointer, :pointer, :pointer], :void
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
# Private: Use mallctl to read boolean value.
|
21
|
+
#
|
22
|
+
# name - the String with parameter name.
|
23
|
+
#
|
24
|
+
# Returns true or false.
|
25
|
+
def get_bool(name)
|
26
|
+
ptr = FFI::MemoryPointer.new :bool
|
27
|
+
mallctl name, ptr, size_pointer(ptr), nil, 0
|
28
|
+
ptr.get_uchar(0) > 0
|
29
|
+
end
|
30
|
+
|
31
|
+
# Private: Use mallctl to read unsigned value.
|
32
|
+
#
|
33
|
+
# name - the String with parameter name.
|
34
|
+
#
|
35
|
+
# Returns Numeric value.
|
36
|
+
def get_uint(name)
|
37
|
+
ptr = FFI::MemoryPointer.new :uint
|
38
|
+
mallctl name, ptr, size_pointer(ptr), nil, 0
|
39
|
+
|
40
|
+
ptr.read_uint
|
41
|
+
end
|
42
|
+
|
43
|
+
# Private: Use mallctl to read unsigned 32-bit value.
|
44
|
+
#
|
45
|
+
# name - the String with parameter name.
|
46
|
+
#
|
47
|
+
# Returns Numeric value.
|
48
|
+
def get_uint32(name)
|
49
|
+
ptr = FFI::MemoryPointer.new :uint32
|
50
|
+
mallctl name, ptr, size_pointer(ptr), nil, 0
|
51
|
+
ptr.read_uint32
|
52
|
+
end
|
53
|
+
|
54
|
+
# Private: Use mallctl to read unsigned 64-bit value.
|
55
|
+
#
|
56
|
+
# name - the String with parameter name.
|
57
|
+
#
|
58
|
+
# Returns Numeric value.
|
59
|
+
def get_uint64(name)
|
60
|
+
ptr = FFI::MemoryPointer.new :uint64
|
61
|
+
mallctl name, ptr, size_pointer(ptr), nil, 0
|
62
|
+
ptr.read_uint64
|
63
|
+
end
|
64
|
+
|
65
|
+
# Private: Use mallctl to read size_t value.
|
66
|
+
#
|
67
|
+
# name - the String with parameter name.
|
68
|
+
#
|
69
|
+
# Returns Numeric value.
|
70
|
+
def get_size_t(name)
|
71
|
+
ptr = FFI::MemoryPointer.new :size_t
|
72
|
+
mallctl name, ptr, size_pointer(ptr), nil, 0
|
73
|
+
|
74
|
+
read_size_t(ptr)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Private: Use mallctl to read size_t value.
|
78
|
+
#
|
79
|
+
# name - the String with parameter name.
|
80
|
+
#
|
81
|
+
# Returns Numeric value.
|
82
|
+
def get_ssize_t(name)
|
83
|
+
ptr = FFI::MemoryPointer.new :ssize_t
|
84
|
+
mallctl name, ptr, size_pointer(ptr), nil, 0
|
85
|
+
|
86
|
+
read_ssize_t(ptr)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Private: Use mallctl to read string value.
|
90
|
+
#
|
91
|
+
# name - the String with parameter name.
|
92
|
+
#
|
93
|
+
# Returns String or nil.
|
94
|
+
def get_string(name)
|
95
|
+
ptr = FFI::MemoryPointer.new :pointer
|
96
|
+
|
97
|
+
mallctl name, ptr, size_pointer(ptr), nil, 0
|
98
|
+
|
99
|
+
strptr = ptr.read_pointer
|
100
|
+
strptr.null? ? nil : strptr.read_string
|
101
|
+
end
|
102
|
+
|
103
|
+
# Private: Set up a size_t pointer.
|
104
|
+
#
|
105
|
+
# Creates a pointer to size_t value, which is set to
|
106
|
+
# baseptr.size.
|
107
|
+
#
|
108
|
+
# baseptr - a FFI::MemoryPointer instance.
|
109
|
+
#
|
110
|
+
# Returns a FFI::MemoryPointer.
|
111
|
+
def size_pointer(baseptr)
|
112
|
+
sizeptr = FFI::MemoryPointer.new :size_t
|
113
|
+
write_size_t(sizeptr, baseptr.size)
|
114
|
+
sizeptr
|
115
|
+
end
|
116
|
+
|
117
|
+
case FFI::Platform::ADDRESS_SIZE
|
118
|
+
when 64
|
119
|
+
def read_size_t(ptr)
|
120
|
+
ptr.read_uint64
|
121
|
+
end
|
122
|
+
|
123
|
+
def write_size_t(ptr, value)
|
124
|
+
ptr.write_uint64 value
|
125
|
+
end
|
126
|
+
|
127
|
+
def read_ssize_t(ptr)
|
128
|
+
ptr.read_int64
|
129
|
+
end
|
130
|
+
|
131
|
+
when 32
|
132
|
+
def read_size_t(ptr)
|
133
|
+
ptr.read_uint32
|
134
|
+
end
|
135
|
+
|
136
|
+
def write_size_t(ptr, value)
|
137
|
+
ptr.write_uint32 value
|
138
|
+
end
|
139
|
+
|
140
|
+
def read_ssize_t(ptr)
|
141
|
+
ptr.read_int32
|
142
|
+
end
|
143
|
+
|
144
|
+
else
|
145
|
+
raise "Unsupported platform address size = #{FFI::Platform::ADDRESS_SIZE}"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
data/lib/jemal.rb
ADDED
@@ -0,0 +1,294 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'jemal/version'
|
3
|
+
require 'jemal/interface'
|
4
|
+
|
5
|
+
module Jemal
|
6
|
+
extend Jemal::Interface
|
7
|
+
|
8
|
+
# Public: Check if Ruby was built with jemalloc.
|
9
|
+
#
|
10
|
+
# Returns true if it was, false otherwise.
|
11
|
+
def self.jemalloc_builtin?
|
12
|
+
require 'rbconfig'
|
13
|
+
|
14
|
+
!!(RbConfig::CONFIG["configure_args"] =~ /jemalloc/)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Public: Get jemalloc version.
|
18
|
+
#
|
19
|
+
# Examples
|
20
|
+
#
|
21
|
+
# Jemal.version
|
22
|
+
# # => "3.6.0-0-g46c0af68bd248b04df75e4f92d5fb804c3d75340"
|
23
|
+
#
|
24
|
+
# Returns the String version.
|
25
|
+
def self.version
|
26
|
+
get_string "version"
|
27
|
+
end
|
28
|
+
|
29
|
+
CFG_PARAMS = %i(debug dss fill lazy_lock mremap munmap prof prof_libgcc
|
30
|
+
prof_libunwind stats tcache tls utrace valgrind xmalloc)
|
31
|
+
|
32
|
+
# Public: Get jemalloc build configuration.
|
33
|
+
#
|
34
|
+
# Returns all config.* parameters in a single Hash.
|
35
|
+
#
|
36
|
+
# Examples
|
37
|
+
#
|
38
|
+
# Jemal.build_configuration
|
39
|
+
# # => {:debug=>false, :dss=>false, :fill=>true, :lazy_lock=>false, ... }
|
40
|
+
#
|
41
|
+
# Returns a Hash with Symbol keys and boolean values.
|
42
|
+
def self.build_configuration
|
43
|
+
CFG_PARAMS.inject({}) do |hash, param|
|
44
|
+
hash[param] = get_bool("config.#{param}")
|
45
|
+
hash
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Public: Get options (opt.*) as a Hash
|
50
|
+
#
|
51
|
+
# Returns a Hash with 24 options.
|
52
|
+
def self.options
|
53
|
+
res = {}
|
54
|
+
|
55
|
+
OPT_BOOL.each { |o| res[o] = get_bool("opt.#{o}") }
|
56
|
+
OPT_SIZE_T.each { |o| res[o] = get_size_t("opt.#{o}") }
|
57
|
+
OPT_SSIZE_T.each { |o| res[o] = get_ssize_t("opt.#{o}") }
|
58
|
+
OPT_CHARP.each { |o| res[o] = get_string("opt.#{o}") }
|
59
|
+
|
60
|
+
res
|
61
|
+
end
|
62
|
+
|
63
|
+
OPT_BOOL = %i(abort stats_print redzone zero utrace xmalloc tcache
|
64
|
+
prof prof_active prof_accum prof_gdump prof_final prof_leak)
|
65
|
+
# 4.0.0: prof_thread_active_init
|
66
|
+
|
67
|
+
OPT_SIZE_T = %i(lg_chunk narenas quarantine lg_tcache_max)
|
68
|
+
OPT_SSIZE_T = %i(lg_prof_sample lg_dirty_mult lg_prof_interval)
|
69
|
+
OPT_CHARP = %i(dss junk prof_prefix)
|
70
|
+
|
71
|
+
# Public: Get current number of arenas.
|
72
|
+
#
|
73
|
+
# Returns Integer value.
|
74
|
+
def self.arenas_count
|
75
|
+
get_uint "arenas.narenas"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Public: Get initialized arenas.
|
79
|
+
#
|
80
|
+
# Returns a Set with integer arena indices. Every index is in
|
81
|
+
# [0, arenas_count-1] range.
|
82
|
+
def self.initialized_arenas
|
83
|
+
n = arenas_count
|
84
|
+
ptr = FFI::MemoryPointer.new :bool, n
|
85
|
+
mallctl "arenas.initialized", ptr, size_pointer(ptr), nil, 0
|
86
|
+
|
87
|
+
(0...n).inject(Set.new) do |indices, i|
|
88
|
+
if ptr.get_uchar(i) > 0
|
89
|
+
indices << i
|
90
|
+
else
|
91
|
+
indices
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Public: Get various sizes.
|
97
|
+
#
|
98
|
+
# Page size, bin sizes, large run sizes, etc.
|
99
|
+
#
|
100
|
+
# Sizes are believed to be constant, therefore this method result is cached.
|
101
|
+
# Second and successive calls will return cached value.
|
102
|
+
#
|
103
|
+
# Returns a Hash with all sizes.
|
104
|
+
def self.sizes
|
105
|
+
return @sizes if defined?(@sizes)
|
106
|
+
|
107
|
+
res = {
|
108
|
+
# Quantum size
|
109
|
+
quantum: get_size_t("arenas.quantum"),
|
110
|
+
# Page size
|
111
|
+
page_size: get_size_t("arenas.page"),
|
112
|
+
# Maximum thread-cached size class
|
113
|
+
tcache_max: get_size_t("arenas.tcache_max"),
|
114
|
+
# Total number of thread cache bin size classes.
|
115
|
+
nhbins: get_uint("arenas.nhbins")
|
116
|
+
}
|
117
|
+
|
118
|
+
# Number of bin size classes.
|
119
|
+
nbins = get_uint("arenas.nbins")
|
120
|
+
|
121
|
+
# Total number of large size classes.
|
122
|
+
nlruns = get_uint("arenas.nlruns")
|
123
|
+
|
124
|
+
# Total number of huge size classes (4.0.0)
|
125
|
+
#nhchunks = get_uint("arenas.nhchunks")
|
126
|
+
|
127
|
+
res[:bins] = (0...nbins).map do |i|
|
128
|
+
prefix = "arenas.bin.#{i}."
|
129
|
+
{
|
130
|
+
# Maximum size supported by size class
|
131
|
+
size: get_size_t("#{prefix}size"),
|
132
|
+
# Number of regions per page run
|
133
|
+
nregs: get_uint32("#{prefix}nregs"),
|
134
|
+
# Number of bytes per page run
|
135
|
+
run_size: get_size_t("#{prefix}run_size")
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
res[:lruns] = (0...nlruns).map do |i|
|
140
|
+
# Maximum size supported by this large size class
|
141
|
+
get_size_t("arenas.lrun.#{i}.size")
|
142
|
+
end
|
143
|
+
|
144
|
+
# 4.0.0
|
145
|
+
#res[:hchunks] = (0...nhchunks).map do |i|
|
146
|
+
## Maximum size supported by this huge size class
|
147
|
+
#get_size_t("arenas.hchunk.#{i}.size")
|
148
|
+
#end
|
149
|
+
|
150
|
+
@sizes = res
|
151
|
+
end
|
152
|
+
|
153
|
+
# Public: Get current statistics.
|
154
|
+
#
|
155
|
+
# Returns stats as one big Hash.
|
156
|
+
def self.stats
|
157
|
+
res = {}
|
158
|
+
|
159
|
+
GLOBAL_STATS.each { |s| res[s] = get_size_t("stats.#{s}") }
|
160
|
+
res[:cactive] = read_size_t(cactive_ptr)
|
161
|
+
|
162
|
+
res[:chunks] = chunks = {}
|
163
|
+
CHUNK_STATS.each { |s| chunks[s] = get_size_t("stats.chunks.#{s}") }
|
164
|
+
|
165
|
+
res[:arenas] = arenas = Array.new(arenas_count)
|
166
|
+
|
167
|
+
initialized_arenas.each do |i|
|
168
|
+
arenas[i] = arena_stats(i)
|
169
|
+
end
|
170
|
+
|
171
|
+
res
|
172
|
+
end
|
173
|
+
|
174
|
+
GLOBAL_STATS = %i(allocated active metadata resident mapped)
|
175
|
+
CHUNK_STATS = %i(current total high)
|
176
|
+
|
177
|
+
ARN_SIZE_T = %i(pactive pdirty mapped metadata.mapped metadata.allocated)
|
178
|
+
ARN_UINT64 = %i(npurge nmadvise purged)
|
179
|
+
|
180
|
+
BIN_PARAMS = %i(allocated nmalloc ndalloc nrequests)
|
181
|
+
BIN_SIZES = %i(small large)
|
182
|
+
|
183
|
+
# Public: Get arena stats.
|
184
|
+
#
|
185
|
+
# i - the Integer arena index (0..arenas_count-1).
|
186
|
+
#
|
187
|
+
# Returns stats as a Hash.
|
188
|
+
def self.arena_stats(i)
|
189
|
+
prefix = "stats.arenas.#{i}."
|
190
|
+
|
191
|
+
res = {
|
192
|
+
#dss: get_string("#{prefix}dss"),
|
193
|
+
lg_dirty_mult: get_ssize_t("#{prefix}lg_dirty_mult"),
|
194
|
+
nthreads: get_uint("#{prefix}nthreads"),
|
195
|
+
}
|
196
|
+
|
197
|
+
ARN_SIZE_T.each { |p| res[p] = get_size_t("#{prefix}#{p}") }
|
198
|
+
ARN_UINT64.each { |p| res[p] = get_uint64("#{prefix}#{p}") }
|
199
|
+
|
200
|
+
BIN_SIZES.each do |sz|
|
201
|
+
res[sz] = h = {}
|
202
|
+
|
203
|
+
BIN_PARAMS.each do |p|
|
204
|
+
h[p] = get_uint64("#{prefix}#{sz}.#{p}")
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
res[:bins] = bins = {}
|
209
|
+
bin_sizes = sizes[:bins]
|
210
|
+
|
211
|
+
(0...bin_sizes.size).each do |i|
|
212
|
+
binprefix = "#{prefix}bins.#{i}."
|
213
|
+
nruns = get_uint64("#{binprefix}nruns")
|
214
|
+
next if nruns == 0
|
215
|
+
|
216
|
+
bins[bin_sizes[i][:size]] = {
|
217
|
+
# Current number of bytes allocated by bin.
|
218
|
+
allocated: get_size_t("#{binprefix}allocated"),
|
219
|
+
|
220
|
+
# Cumulative number of allocations served by bin.
|
221
|
+
nmalloc: get_uint64("#{binprefix}nmalloc"),
|
222
|
+
|
223
|
+
# Cumulative number of allocations returned to bin.
|
224
|
+
ndalloc: get_uint64("#{binprefix}ndalloc"),
|
225
|
+
|
226
|
+
# Cumulative number of allocation requests.
|
227
|
+
nrequests: get_uint64("#{binprefix}nrequests"),
|
228
|
+
|
229
|
+
# Cumulative number of tcache fills.
|
230
|
+
nfills: get_uint64("#{binprefix}nfills"),
|
231
|
+
|
232
|
+
# Cumulative number of tcache flushes.
|
233
|
+
nflushes: get_uint64("#{binprefix}nflushes"),
|
234
|
+
|
235
|
+
# Cumulative number of times the current run from which to allocate changed.
|
236
|
+
nreruns: get_uint64("#{binprefix}nreruns"),
|
237
|
+
|
238
|
+
# Current number of runs.
|
239
|
+
curruns: get_size_t("#{binprefix}curruns"),
|
240
|
+
|
241
|
+
# Cumulative number of runs created.
|
242
|
+
nruns: nruns
|
243
|
+
}
|
244
|
+
end
|
245
|
+
|
246
|
+
res[:lruns] = lruns = {}
|
247
|
+
lrun_sizes = sizes[:lruns]
|
248
|
+
|
249
|
+
(0...lrun_sizes.size).each do |i|
|
250
|
+
lrunprefix = "#{prefix}lruns.#{i}."
|
251
|
+
nreqs = get_uint64("#{lrunprefix}nrequests")
|
252
|
+
next if nreqs == 0
|
253
|
+
|
254
|
+
lruns[lrun_sizes[i]] = {
|
255
|
+
# Cumulative number of allocation requests for this size class served
|
256
|
+
# directly by the arena.
|
257
|
+
nmalloc: get_uint64("#{lrunprefix}nmalloc"),
|
258
|
+
|
259
|
+
# Cumulative number of deallocation requests for this size class served
|
260
|
+
# directly by the arena.
|
261
|
+
ndalloc: get_uint64("#{lrunprefix}ndalloc"),
|
262
|
+
|
263
|
+
# Cumulative number of allocation requests for this size class.
|
264
|
+
nrequests: nreqs,
|
265
|
+
|
266
|
+
# Current number of runs for this size class.
|
267
|
+
curruns: get_size_t("#{lrunprefix}curruns"),
|
268
|
+
}
|
269
|
+
end
|
270
|
+
|
271
|
+
res
|
272
|
+
end
|
273
|
+
|
274
|
+
# Public: Print current stats.
|
275
|
+
#
|
276
|
+
# Invoke jemalloc's own stats reporting function, which
|
277
|
+
# prints all stats to STDERR.
|
278
|
+
#
|
279
|
+
# Returns nothing.
|
280
|
+
def self.stats_print
|
281
|
+
malloc_stats_print nil,nil,nil
|
282
|
+
end
|
283
|
+
|
284
|
+
protected
|
285
|
+
|
286
|
+
def self.cactive_ptr
|
287
|
+
@cactive_ptr ||=
|
288
|
+
begin
|
289
|
+
ptr = FFI::MemoryPointer.new :pointer
|
290
|
+
mallctl "stats.cactive", ptr, size_pointer(ptr), nil, 0
|
291
|
+
ptr.read_pointer
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
metadata
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jemal
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- oleg dashevskii
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ffi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.9.10
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.9.10
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.10'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.10'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: log_buddy
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Means to access jemalloc options and statistics for MRI compiled with
|
98
|
+
jemalloc support.
|
99
|
+
email:
|
100
|
+
- olegdashevskii@gmail.com
|
101
|
+
executables: []
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- ".gitignore"
|
106
|
+
- ".travis.yml"
|
107
|
+
- Gemfile
|
108
|
+
- LICENSE.txt
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- bin/console
|
112
|
+
- bin/setup
|
113
|
+
- jemal.gemspec
|
114
|
+
- lib/jemal.rb
|
115
|
+
- lib/jemal/interface.rb
|
116
|
+
- lib/jemal/version.rb
|
117
|
+
homepage: https://github.com/be9/jemal
|
118
|
+
licenses:
|
119
|
+
- MIT
|
120
|
+
metadata: {}
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
require_paths:
|
124
|
+
- lib
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
requirements: []
|
136
|
+
rubyforge_project:
|
137
|
+
rubygems_version: 2.4.5.1
|
138
|
+
signing_key:
|
139
|
+
specification_version: 4
|
140
|
+
summary: Interface to jemalloc
|
141
|
+
test_files: []
|