jemal 0.1.0
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.
- 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: []
|