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 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
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .ruby-version
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in jemal.gemspec
4
+ gemspec
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
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
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
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
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
@@ -0,0 +1,3 @@
1
+ module Jemal
2
+ VERSION = "0.1.0"
3
+ 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: []