jemal 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []