sys-memory 0.1.3 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad8db02636f7f2e60a3cc6a56365b10eecfa117825aa4ce7992f8f8bf2a9ae61
4
- data.tar.gz: b336bd31a553a7b2e3a3ca24848152c5f73bf913f0bfdce402ae7164717050ba
3
+ metadata.gz: 457079deac5b71c2aa7827dc99fd05ad3a2096937db1d59d0824fa9d19307fc3
4
+ data.tar.gz: 8d3a0331639955099db7909cace2e596a5972573053ec88f4f7e9013051ae474
5
5
  SHA512:
6
- metadata.gz: 6634473e9c7ca86e19bbd341e88beff8d8094be5e64a7228049599171f31d486a52db9d0ef2c1e0dc2ffe040639936f3a0b1f639e0960cdc03760ef26ba64ec7
7
- data.tar.gz: e23244aacf479c8936a443458c6b584f9013fe8882bf23e7a18647ebda78fb79d9ea2f1006dc8e5760a524c64dd89a9b84f6bca428f155e966ba350a5fdc5f8f
6
+ metadata.gz: 3e3c784ef9a72e5c8f1a1ed0a8de66355381fb8391744440c621346b965dc45d55276737a9d4417ec8057550bb561ed54285dbf1935b33abbfc723b89cdeaef1
7
+ data.tar.gz: a8910b5ad70f11cea073144e08a9db16b876669ec2611bf75cb8e6624e6ac2d0c88dae07fe28f0ef387e1feca55c57d989dc5e042f79c351072a7137d107f36a
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGES.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 0.2.1 - 2-Jun-2026
2
+ * Fixed swap for FreeBSD.
3
+ * Rubocop fixes.
4
+ * Some spec updates.
5
+ * Minor Rakefile updates.
6
+
7
+ ## 0.2.0 - 12-Jun-2024
8
+ * Added support for DragonflyBSD. Currently the default implementation
9
+ for all BSD platforms.
10
+
1
11
  ## 0.1.3 - 12-Dec-2023
2
12
  * Fix "used" calculations on Linux. Thanks go to Splendide-Imaginarius
3
13
  for the spot and the patch.
data/README.md CHANGED
@@ -7,6 +7,9 @@ A Ruby interface for getting memory information.
7
7
  * Linux
8
8
  * Windows
9
9
  * OSX
10
+ * BSD
11
+
12
+ Note that only DragonflyBSD has been tested. I am not sure about other flavors yet.
10
13
 
11
14
  ## Installation
12
15
  `gem install sys-memory`
@@ -44,7 +47,7 @@ https://github.com/djberg96/sys-memory
44
47
  Apache-2.0
45
48
 
46
49
  ## Copyright
47
- (C) 2021-2023 Daniel J. Berger, All Rights Reserved
50
+ (C) 2021-2024 Daniel J. Berger, All Rights Reserved
48
51
 
49
52
  ## Warranty
50
53
  This package is provided "as is" and without any express or
data/Rakefile CHANGED
@@ -5,7 +5,10 @@ require 'rubocop/rake_task'
5
5
 
6
6
  CLEAN.include('**/*.gem', '**/*.rbc', '**/*.rbx', '**/*.lock')
7
7
 
8
- RSpec::Core::RakeTask.new(:spec)
8
+ RSpec::Core::RakeTask.new(:spec) do |t|
9
+ t.verbose = false
10
+ t.rspec_opts = '-f documentation -w'
11
+ end
9
12
 
10
13
  namespace 'gem' do
11
14
  desc "Create the sys-memory gem"
@@ -25,4 +28,9 @@ end
25
28
 
26
29
  RuboCop::RakeTask.new
27
30
 
31
+ # Clean up afterwards
32
+ Rake::Task[:spec].enhance do
33
+ Rake::Task[:clean].invoke
34
+ end
35
+
28
36
  task :default => :spec
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi'
4
+
5
+ # The Sys module serves only as a namespace.
6
+ module Sys
7
+ # The Memory module is a house for memory related singleton methods that don't require state.
8
+ module Memory
9
+ extend FFI::Library
10
+
11
+ ffi_lib FFI::Library::LIBC
12
+
13
+ attach_function :sysctlbyname, %i[string pointer pointer pointer size_t], :int
14
+
15
+ if RbConfig::CONFIG['host_os'] =~ /freebsd/i
16
+ ffi_lib FFI::Library::LIBC, FFI.map_library_name('kvm')
17
+
18
+ attach_function :kvm_openfiles, %i[string string string int pointer], :pointer
19
+ attach_function :kvm_geterr, [:pointer], :string
20
+ attach_function :kvm_getswapinfo, %i[pointer pointer int int], :int
21
+ attach_function :kvm_close, [:pointer], :int
22
+
23
+ # Private class wrapper for struct kvm_swap
24
+ class KvmSwap < FFI::Struct
25
+ layout(
26
+ :ksw_devname, [:char, 32],
27
+ :ksw_used, :uint,
28
+ :ksw_total, :uint,
29
+ :ksw_flags, :int,
30
+ :ksw_reserved1, :uint,
31
+ :ksw_reserved2, :uint
32
+ )
33
+ end
34
+
35
+ private_constant :KvmSwap
36
+ end
37
+
38
+ # Obtain detailed memory information about your host in the form of a hash.
39
+ # Note that the exact nature of this hash is largely dependent on your
40
+ # operating system.
41
+ #
42
+ def memory
43
+ page_size = get_by_name('hw.pagesize')
44
+
45
+ hash = {}
46
+ hash[:total] = get_by_name('hw.physmem')
47
+ hash[:active] = get_by_name('vm.stats.vm.v_active_count') * page_size
48
+ hash[:all] = get_by_name('vm.stats.vm.v_page_count') * page_size
49
+ hash[:cache] = get_by_name('vm.stats.vm.v_cache_count') * page_size
50
+ hash[:free] = get_by_name('vm.stats.vm.v_free_count') * page_size
51
+ hash[:inactive] = get_by_name('vm.stats.vm.v_inactive_count') * page_size
52
+ hash[:wire] = get_by_name('vm.stats.vm.v_wire_count') * page_size
53
+
54
+ if RbConfig::CONFIG['host_os'] =~ /dragonfly/i
55
+ hash[:swap_size] = get_by_name('vm.swap_size')
56
+ hash[:swap_free] = get_by_name('vm.swap_free')
57
+ elsif RbConfig::CONFIG['host_os'] =~ /freebsd/i
58
+ hash[:swap_size] = get_by_name('vm.swap_total')
59
+ hash[:swap_free] = hash[:swap_size] - get_freebsd_swap_used(page_size)
60
+ else
61
+ hash[:swap_size] = get_by_name('vm.swap_total')
62
+ hash[:swap_free] = hash[:swap_size] - get_by_name('vm.swap_reserved') # Best guess
63
+ end
64
+
65
+ hash
66
+ end
67
+
68
+ # Total memory in bytes. By default this is only physical memory, but
69
+ # if the +extended+ option is set to true, then swap memory is included as
70
+ # part of the total.
71
+ #
72
+ def total(extended: false)
73
+ hash = memory
74
+ extended ? hash[:total] + hash[:swap_size] : hash[:total]
75
+ end
76
+
77
+ # The memory currently available, in bytes. By default this is only
78
+ # physical memory, but if the +extended+ option is set to true, then free
79
+ # swap memory is also included.
80
+ #
81
+ def free(extended: false)
82
+ hash = memory
83
+ extended ? hash[:free] + hash[:swap_free] : hash[:free]
84
+ end
85
+
86
+ # The memory, in bytes, currently in use. By default this is only
87
+ # physical memory, but if the +extended+ option is set to true then
88
+ # swap is included in the calculation.
89
+ #
90
+ def used(extended: false)
91
+ total(extended: extended) - free(extended: extended)
92
+ end
93
+
94
+ # A number between 0 and 100 that specifies the approximate percentage of
95
+ # memory that is in use. If the +extended+ option is set to true then
96
+ # swap memory is included in the calculation.
97
+ #
98
+ def load(extended: false)
99
+ (used(extended: extended) / total(extended: extended).to_f).round(2) * 100
100
+ end
101
+
102
+ module_function :memory, :total, :free, :load, :used
103
+
104
+ private
105
+
106
+ def get_by_name(mib)
107
+ value = nil
108
+
109
+ begin
110
+ optr = FFI::MemoryPointer.new(:uint64_t)
111
+ size = FFI::MemoryPointer.new(:size_t)
112
+ size.write_int(optr.size)
113
+
114
+ if sysctlbyname(mib, optr, size, nil, 0) < 0
115
+ raise SystemCallError.new("sysctlbyname: #{mib}", FFI.errno)
116
+ end
117
+
118
+ value = optr.read_uint64
119
+ ensure
120
+ optr.free if optr && !optr.null?
121
+ size.free if size && !size.null?
122
+ end
123
+
124
+ value
125
+ end
126
+
127
+ def get_freebsd_swap_used(page_size)
128
+ kd = nil
129
+
130
+ begin
131
+ error = FFI::MemoryPointer.new(:char, 2048)
132
+ kd = kvm_openfiles(nil, File::NULL, nil, 0, error)
133
+
134
+ if kd.null?
135
+ message = error.read_string
136
+ raise SystemCallError, "kvm_openfiles: #{message.empty? ? 'unknown error' : message}"
137
+ end
138
+
139
+ swap = KvmSwap.new
140
+
141
+ if kvm_getswapinfo(kd, swap.pointer, 1, 0) < 0
142
+ raise SystemCallError, "kvm_getswapinfo: #{kvm_geterr(kd)}"
143
+ end
144
+
145
+ swap[:ksw_used] * page_size
146
+ ensure
147
+ kvm_close(kd) if kd && !kd.null?
148
+ error.free if error && !error.null?
149
+ end
150
+ end
151
+
152
+ module_function :get_by_name, :get_freebsd_swap_used
153
+ end
154
+ end
@@ -50,7 +50,7 @@ module Sys
50
50
  #
51
51
  def used(extended: false)
52
52
  hash = memory
53
- total(extended: extended) - free(extended: extended) - (hash['Buffers'] + hash['Cached'] + hash['Slab']) * 1024
53
+ total(extended: extended) - free(extended: extended) - ((hash['Buffers'] + hash['Cached'] + hash['Slab']) * 1024)
54
54
  end
55
55
 
56
56
  # A number between 0 and 100 that specifies the approximate percentage of
data/lib/sys/memory.rb CHANGED
@@ -8,6 +8,8 @@ case RbConfig::CONFIG['host_os']
8
8
  require_relative 'linux/memory'
9
9
  when /darwin|macos/i
10
10
  require_relative 'osx/memory'
11
+ when /bsd|dragonfly/i
12
+ require_relative 'bsd/memory'
11
13
  when /windows|win32|mingw/i
12
14
  require_relative 'windows/memory'
13
15
  end
@@ -7,6 +7,7 @@ module Sys
7
7
  # The Memory module is a house for memory related singleton methods that don't require state.
8
8
  module Memory
9
9
  extend FFI::Library
10
+
10
11
  ffi_lib FFI::Library::LIBC
11
12
 
12
13
  HOST_VM_INFO64 = 4
@@ -25,6 +26,7 @@ module Sys
25
26
 
26
27
  typedef :uint, :natural_t
27
28
 
29
+ # Private class wrapper for struct swap
28
30
  class Swap < FFI::Struct
29
31
  layout(
30
32
  :xsu_total, :uint64_t,
@@ -37,6 +39,7 @@ module Sys
37
39
 
38
40
  private_constant :Swap
39
41
 
42
+ # Private class wrapper for struct vmstat
40
43
  class VmStat < FFI::Struct
41
44
  layout(
42
45
  :free_count, :natural_t, # of pages free
data/lib/sys/version.rb CHANGED
@@ -3,6 +3,6 @@
3
3
  module Sys
4
4
  module Memory
5
5
  # The version of the sys-memory library.
6
- VERSION = '0.1.3'
6
+ VERSION = '0.2.1'
7
7
  end
8
8
  end
@@ -7,11 +7,13 @@ module Sys
7
7
  module Memory
8
8
  require 'ffi'
9
9
  extend FFI::Library
10
+
10
11
  ffi_lib 'kernel32'
11
12
 
12
13
  typedef :uint32, :dword
13
14
  typedef :uint64, :dwordlong
14
15
 
16
+ # Private wrapper class for the MEMORYSTATUSEX struct
15
17
  class MemoryStatusEx < FFI::Struct
16
18
  layout(
17
19
  :dwLength, :dword,
@@ -33,6 +35,7 @@ module Sys
33
35
 
34
36
  ffi_lib 'psapi'
35
37
 
38
+ # Private wrapper class for the PERFORMANCE_INFORMATION struct
36
39
  class PerformanceInformation < FFI::Struct
37
40
  layout(
38
41
  :cb, :dword,
@@ -4,9 +4,20 @@ require 'active_support/core_ext/numeric/bytes'
4
4
  require 'sys-memory'
5
5
 
6
6
  RSpec.describe Sys::Memory do
7
+ let(:memory) { described_class.memory }
8
+
9
+ let(:swap_keys) do
10
+ [
11
+ %i[swap_size swap_free],
12
+ %i[swap_total swap_available],
13
+ %w[SwapTotal SwapFree],
14
+ %w[TotalPageFile AvailPageFile]
15
+ ].find { |total_key, free_key| memory.key?(total_key) && memory.key?(free_key) }
16
+ end
17
+
7
18
  context 'Sys::Memory::VERSION' do
8
19
  example 'the version constant is set to the expected value' do
9
- expect(described_class::VERSION).to eq('0.1.3')
20
+ expect(described_class::VERSION).to eq('0.2.1')
10
21
  expect(described_class::VERSION).to be_frozen
11
22
  end
12
23
  end
@@ -17,9 +28,24 @@ RSpec.describe Sys::Memory do
17
28
  end
18
29
 
19
30
  example 'the memory singleton method returns the expected hash' do
20
- expect(described_class.memory).to be_kind_of(Hash)
31
+ expect(described_class.memory).to be_a(Hash)
21
32
  expect(described_class.memory.size).to be > 4
22
33
  end
34
+
35
+ example 'the memory singleton method returns non-negative numeric values' do
36
+ described_class.memory.each do |key, value|
37
+ expect(value).to be_a(Numeric), "#{key.inspect} should be numeric"
38
+ expect(value).to be >= 0
39
+ end
40
+ end
41
+
42
+ example 'the memory singleton method returns sane swap values' do
43
+ swap_total, swap_free = swap_keys
44
+ skip 'no swap values reported on this platform' unless swap_total && swap_free
45
+
46
+ expect(memory[swap_total]).to be >= 0
47
+ expect(memory[swap_free]).to be_between(0, memory[swap_total]).inclusive
48
+ end
23
49
  end
24
50
 
25
51
  context 'Sys::Memory.total' do
data/sys-memory.gemspec CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = 'sys-memory'
5
- spec.version = '0.1.3'
5
+ spec.version = '0.2.1'
6
6
  spec.author = 'Daniel J. Berger'
7
7
  spec.email = 'djberg96@gmail.com'
8
8
  spec.license = 'Apache-2.0'
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
13
13
  spec.cert_chain = ['certs/djberg96_pub.pem']
14
14
 
15
15
  spec.add_dependency('ffi', '~> 1.1')
16
- spec.add_development_dependency('activesupport', '~> 6.0')
16
+ spec.add_development_dependency('activesupport', '>= 6.0')
17
17
  spec.add_development_dependency('rspec', '~> 3.9')
18
18
  spec.add_development_dependency('rake', '~> 13.0')
19
19
  spec.add_development_dependency('rubocop')
@@ -27,7 +27,8 @@ Gem::Specification.new do |spec|
27
27
  'source_code_uri' => 'https://github.com/djberg96/sys-memory',
28
28
  'wiki_uri' => 'https://github.com/djberg96/sys-memory/wiki',
29
29
  'rubygems_mfa_required' => 'true',
30
- 'github_repo' => 'https://github.com/djberg96/sys-memory'
30
+ 'github_repo' => 'https://github.com/djberg96/sys-memory',
31
+ 'funding_uri' => 'https://github.com/sponsors/djberg96'
31
32
  }
32
33
 
33
34
  spec.description = <<-EOF
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,11 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sys-memory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel J. Berger
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain:
11
10
  - |
@@ -35,7 +34,7 @@ cert_chain:
35
34
  ORVCZpRuCPpmC8qmqxUnARDArzucjaclkxjLWvCVHeFa9UP7K3Nl9oTjJNv+7/jM
36
35
  WZs4eecIcUc4tKdHxcAJ0MO/Dkqq7hGaiHpwKY76wQ1+8xAh
37
36
  -----END CERTIFICATE-----
38
- date: 2023-12-12 00:00:00.000000000 Z
37
+ date: 1980-01-02 00:00:00.000000000 Z
39
38
  dependencies:
40
39
  - !ruby/object:Gem::Dependency
41
40
  name: ffi
@@ -55,14 +54,14 @@ dependencies:
55
54
  name: activesupport
56
55
  requirement: !ruby/object:Gem::Requirement
57
56
  requirements:
58
- - - "~>"
57
+ - - ">="
59
58
  - !ruby/object:Gem::Version
60
59
  version: '6.0'
61
60
  type: :development
62
61
  prerelease: false
63
62
  version_requirements: !ruby/object:Gem::Requirement
64
63
  requirements:
65
- - - "~>"
64
+ - - ">="
66
65
  - !ruby/object:Gem::Version
67
66
  version: '6.0'
68
67
  - !ruby/object:Gem::Dependency
@@ -138,6 +137,7 @@ files:
138
137
  - Rakefile
139
138
  - certs/djberg96_pub.pem
140
139
  - lib/sys-memory.rb
140
+ - lib/sys/bsd/memory.rb
141
141
  - lib/sys/linux/memory.rb
142
142
  - lib/sys/memory.rb
143
143
  - lib/sys/osx/memory.rb
@@ -157,7 +157,7 @@ metadata:
157
157
  wiki_uri: https://github.com/djberg96/sys-memory/wiki
158
158
  rubygems_mfa_required: 'true'
159
159
  github_repo: https://github.com/djberg96/sys-memory
160
- post_install_message:
160
+ funding_uri: https://github.com/sponsors/djberg96
161
161
  rdoc_options: []
162
162
  require_paths:
163
163
  - lib
@@ -172,8 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
172
172
  - !ruby/object:Gem::Version
173
173
  version: '0'
174
174
  requirements: []
175
- rubygems_version: 3.3.26
176
- signing_key:
175
+ rubygems_version: 4.0.6
177
176
  specification_version: 4
178
177
  summary: A Ruby interface for providing memory information
179
178
  test_files:
metadata.gz.sig CHANGED
Binary file