solaris-kstat 1.0.3 → 1.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.
- data/CHANGES +7 -0
- data/MANIFEST +4 -3
- data/README +44 -49
- data/Rakefile +33 -27
- data/lib/solaris/kstat.rb +321 -0
- data/lib/solaris/kstat/functions.rb +22 -0
- data/lib/solaris/kstat/structs.rb +260 -0
- data/solaris-kstat.gemspec +7 -5
- data/test/test_solaris_kstat.rb +91 -57
- data/test/test_solaris_kstat_structs.rb +60 -0
- metadata +93 -55
- data/ext/extconf.rb +0 -15
- data/ext/solaris/rkstat.c +0 -240
- data/ext/solaris/rkstat.h +0 -353
data/CHANGES
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
== 1.1.0 - 13-Oct-2013
|
2
|
+
* Converted code to use FFI.
|
3
|
+
* The Kstat::Error subclass was removed. Any internal FFI
|
4
|
+
functions that fail raise a SystemCallError instead.
|
5
|
+
* Refactored the test suite, and added some tests for FFI structs.
|
6
|
+
* Added test-unit 2 and rake as development dependencies.
|
7
|
+
|
1
8
|
== 1.0.3 - 30-Jul-2011
|
2
9
|
* Added test-unit as a development dependency.
|
3
10
|
* Fixed a switch statement bug (missing break).
|
data/MANIFEST
CHANGED
@@ -4,7 +4,8 @@
|
|
4
4
|
* Rakefile
|
5
5
|
* solaris-kstat.gemspec
|
6
6
|
* examples/example_kstat.rb
|
7
|
-
*
|
8
|
-
*
|
9
|
-
*
|
7
|
+
* lib/solaris/kstat.rb
|
8
|
+
* lib/solaris/kstat/structs.rb
|
9
|
+
* lib/solaris/kstat/functions.rb
|
10
10
|
* test/test_solaris_kstat.rb
|
11
|
+
* test/test_solaris_kstat_structs.b
|
data/README
CHANGED
@@ -1,31 +1,31 @@
|
|
1
1
|
== Description
|
2
|
-
|
2
|
+
A Ruby interface for the Solaris kstat library.
|
3
3
|
|
4
4
|
== Prerequisites
|
5
|
-
|
5
|
+
Solaris 8 (SunOS 2.8) or later.
|
6
6
|
|
7
7
|
== Installation
|
8
|
-
|
8
|
+
gem install solaris-kstat
|
9
9
|
|
10
10
|
== Synopsis
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
require 'solaris/kstat'
|
12
|
+
require 'pp'
|
13
|
+
include Solaris
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
k = Kstat.new('cpu_info', 0, 'cpu_info0')
|
16
|
+
pp k.record
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
18
|
+
{'cpu_info'=>
|
19
|
+
{0=>
|
20
|
+
{'cpu_info0'=>
|
21
|
+
{'chip_id'=>0,
|
22
|
+
'fpu_type'=>'sparcv9',
|
23
|
+
'device_ID'=>0,
|
24
|
+
'cpu_type'=>'sparcv9',
|
25
|
+
'implementation'=>'Unknown',
|
26
|
+
'clock_MHz'=>502,
|
27
|
+
'state_begin'=>1105974702,
|
28
|
+
'state'=>'on-line'}}}}
|
29
29
|
|
30
30
|
== Class Methods
|
31
31
|
Kstat.new(module=nil, instance=-1, name=nil)
|
@@ -43,53 +43,48 @@ Kstat#record
|
|
43
43
|
deeply that hash is nested depends on the values passed to the constructor.
|
44
44
|
The more specific your criterion, the less data you will receive.
|
45
45
|
|
46
|
-
== Error Classes
|
47
|
-
Kstat::Error < StandardError
|
48
|
-
Raised if anything goes wrong. Typically this will only occur if you
|
49
|
-
pass bad arguments to the constructor e.g. a module name that doesn't
|
50
|
-
exist, etc.
|
51
|
-
|
52
46
|
== Unsupported names
|
53
|
-
|
47
|
+
The following names will not return any meaningful value:
|
54
48
|
|
55
|
-
|
56
|
-
|
57
|
-
|
49
|
+
* kstat_headers
|
50
|
+
* sfmmu_global_stat
|
51
|
+
* sfmmu_percpu_stat
|
58
52
|
|
59
|
-
== Known
|
60
|
-
|
61
|
-
|
53
|
+
== Known Issues
|
54
|
+
None at this time. Please report any issues on the project page at
|
55
|
+
https://github.com/djberg96/solaris-kstat
|
62
56
|
|
63
57
|
== Designer's Notes
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
58
|
+
I have noticed that results from the cpu_stat module differ from the output
|
59
|
+
of the 'kstat' command line tool. I am convinced that my code is correct and
|
60
|
+
that there is a bug in the Solaris::Kstat Perl module. Unfortunately, the
|
61
|
+
source for the version of the Solaris::Kstat Perl module that works on
|
62
|
+
Solaris 8 and later is not available (the version on CPAN only works on
|
63
|
+
Solaris 6 and 7).
|
70
64
|
|
71
|
-
|
65
|
+
See http://tinyurl.com/karxw for more details.
|
72
66
|
|
73
67
|
== Acknowledgements
|
74
|
-
|
68
|
+
Thanks go to Charlie Mills for help with the 'volatile' issue for the
|
69
|
+
original C code.
|
75
70
|
|
76
71
|
== Future Plans
|
77
|
-
|
72
|
+
Add snaptime and crtime information to the statistics hash.
|
78
73
|
|
79
74
|
== License
|
80
|
-
|
75
|
+
Artistic 2.0
|
81
76
|
|
82
77
|
== Copyright
|
83
|
-
|
84
|
-
|
78
|
+
(C) 2003-2013 Daniel J. Berger
|
79
|
+
All Rights Reserved
|
85
80
|
|
86
81
|
== Warranty
|
87
|
-
|
88
|
-
|
89
|
-
|
82
|
+
This package is provided "as is" and without any express or
|
83
|
+
implied warranties, including, without limitation, the implied
|
84
|
+
warranties of merchantability and fitness for a particular purpose.
|
90
85
|
|
91
86
|
== Author
|
92
|
-
|
87
|
+
Daniel J. Berger
|
93
88
|
|
94
89
|
== See Also
|
95
|
-
|
90
|
+
kstat(1M)
|
data/Rakefile
CHANGED
@@ -2,36 +2,27 @@ require 'rake'
|
|
2
2
|
require 'rake/clean'
|
3
3
|
require 'rake/testtask'
|
4
4
|
require 'rbconfig'
|
5
|
-
include Config
|
6
5
|
|
7
6
|
CLEAN.include(
|
8
|
-
'**/*.gem',
|
9
|
-
'**/*.rbc',
|
10
|
-
'**/*.
|
11
|
-
'**/*.log', # Ruby extension build log
|
12
|
-
'**/Makefile', # C Makefile
|
13
|
-
'**/conftest.dSYM', # OS X build directory
|
14
|
-
"**/*.#{CONFIG['DLEXT']}" # C shared object
|
7
|
+
'**/*.gem', # Gem files
|
8
|
+
'**/*.rbc', # Rubinius
|
9
|
+
'**/*.rbx' # Rubinius
|
15
10
|
)
|
16
11
|
|
17
|
-
desc "Build the solaris-kstat source"
|
18
|
-
task :build do
|
19
|
-
Dir.chdir('ext') do
|
20
|
-
ruby 'extconf.rb'
|
21
|
-
sh 'make'
|
22
|
-
cp 'kstat.so', 'solaris'
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
12
|
namespace :gem do
|
27
13
|
desc "Create the solaris-kstat gem"
|
28
14
|
task :create => [:clean] do
|
29
15
|
spec = eval(IO.read('solaris-kstat.gemspec'))
|
30
|
-
Gem::
|
16
|
+
if Gem::VERSION < "2.0.0"
|
17
|
+
Gem::Builder.new(spec).build
|
18
|
+
else
|
19
|
+
require 'rubygems/package'
|
20
|
+
Gem::Package.build(spec)
|
21
|
+
end
|
31
22
|
end
|
32
23
|
|
33
24
|
desc "Install the solaris-kstat gem"
|
34
|
-
task :install => [:
|
25
|
+
task :install => [:create] do
|
35
26
|
ruby 'solaris-kstat.gemspec'
|
36
27
|
file = Dir["*.gem"].first
|
37
28
|
sh "gem install #{file}"
|
@@ -39,15 +30,30 @@ namespace :gem do
|
|
39
30
|
end
|
40
31
|
|
41
32
|
desc "Run the example program"
|
42
|
-
task :example
|
43
|
-
ruby "-
|
33
|
+
task :example do
|
34
|
+
ruby "-Ilib examples/example_kstat.rb"
|
44
35
|
end
|
45
36
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
37
|
+
namespace :test do
|
38
|
+
desc "Run base tests"
|
39
|
+
Rake::TestTask.new(:base) do |t|
|
40
|
+
t.test_files = FileList['test/test_solaris_kstat.rb']
|
41
|
+
t.verbose = true
|
42
|
+
t.warning = true
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "Run FFI struct tests"
|
46
|
+
Rake::TestTask.new(:structs) do |t|
|
47
|
+
t.test_files = FileList['test/test_solaris_kstat_structs.rb']
|
48
|
+
t.verbose = true
|
49
|
+
t.warning = true
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "Run all tests"
|
53
|
+
Rake::TestTask.new(:all) do |t|
|
54
|
+
t.verbose = true
|
55
|
+
t.warning = true
|
56
|
+
end
|
51
57
|
end
|
52
58
|
|
53
|
-
task :default => :
|
59
|
+
task :default => "test:all"
|
@@ -0,0 +1,321 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'kstat', 'structs')
|
2
|
+
require File.join(File.dirname(__FILE__), 'kstat', 'functions')
|
3
|
+
|
4
|
+
module Solaris
|
5
|
+
class Kstat
|
6
|
+
extend FFI::Library
|
7
|
+
include Solaris::Structs
|
8
|
+
include Solaris::Functions
|
9
|
+
|
10
|
+
# The version of the solaris-kstat library
|
11
|
+
VERSION = '1.1.0'
|
12
|
+
|
13
|
+
# The kstat module
|
14
|
+
attr_accessor :module
|
15
|
+
|
16
|
+
# The kstat instance number
|
17
|
+
attr_accessor :instance
|
18
|
+
|
19
|
+
# The kstat name
|
20
|
+
attr_accessor :name
|
21
|
+
|
22
|
+
# Creates and returns a Kstat object. This does NOT traverse the kstat
|
23
|
+
# chain. The Kstat#record method uses the values passed to actually
|
24
|
+
# retrieve data.
|
25
|
+
#
|
26
|
+
# You may specify a module, an instance and a name. The module defaults to
|
27
|
+
# nil (all modules), the instance defaults to -1 (all instances) and the
|
28
|
+
# name defaults to nil (all names).
|
29
|
+
#
|
30
|
+
# Examples:
|
31
|
+
#
|
32
|
+
# require 'solaris/kstat'
|
33
|
+
# include Kstat
|
34
|
+
#
|
35
|
+
# k1 = Kstat.new # Everything
|
36
|
+
# k2 = Kstat.new('cpu') # Just CPU info
|
37
|
+
# k3 = Kstat.new('cpu', 0) # Just CPU info for instance 0
|
38
|
+
# k4 = Kstat.new('cpu', 0, 'sys') # CPU info for instance 0 named 'sys'
|
39
|
+
#
|
40
|
+
def initialize(mod=nil, instance=-1, name=nil)
|
41
|
+
# Type checking added since invalid values could cause a segfault later on.
|
42
|
+
raise TypeError unless mod.is_a?(String) if mod
|
43
|
+
raise TypeError unless instance.is_a?(Fixnum)
|
44
|
+
raise TypeError unless name.is_a?(String) if name
|
45
|
+
|
46
|
+
@module = mod
|
47
|
+
@instance = instance
|
48
|
+
@name = name
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns a nested hash based on the values passed to the constructor. How
|
52
|
+
# deeply that hash is nested depends on the values passed to the constructor.
|
53
|
+
# The more specific your criterion, the less data you will receive.
|
54
|
+
#
|
55
|
+
def record
|
56
|
+
kptr = kstat_open()
|
57
|
+
|
58
|
+
if kptr.null?
|
59
|
+
raise SystemCallError.new('kstat_open', FFI.errno)
|
60
|
+
end
|
61
|
+
|
62
|
+
kstat = kstat_lookup(kptr, @module, @instance, @name)
|
63
|
+
|
64
|
+
if kstat.null?
|
65
|
+
kstat_close(kptr)
|
66
|
+
raise SystemCallError.new('kstat_lookup', FFI.errno)
|
67
|
+
end
|
68
|
+
|
69
|
+
mhash = {} # Holds modules
|
70
|
+
ihash = {} # Holds instances
|
71
|
+
nhash = {} # Holds names
|
72
|
+
shash = {} # Subhash for names
|
73
|
+
|
74
|
+
# Sync the chain with the kernel
|
75
|
+
if kstat_chain_update(kptr) < 0
|
76
|
+
raise SystemCallError.new('kstat_chain_update', FFI.errno)
|
77
|
+
end
|
78
|
+
|
79
|
+
begin
|
80
|
+
while !kstat.null?
|
81
|
+
break if kstat[:ks_next].null?
|
82
|
+
|
83
|
+
if @module && @module != kstat[:ks_module].to_s
|
84
|
+
kstat = KstatStruct.new(kstat[:ks_next])
|
85
|
+
next
|
86
|
+
end
|
87
|
+
|
88
|
+
if @instance != -1 && @instance != kstat[:ks_instance]
|
89
|
+
kstat = KstatStruct.new(kstat[:ks_next])
|
90
|
+
next
|
91
|
+
end
|
92
|
+
|
93
|
+
if @name && @name != kstat[:ks_name].to_s
|
94
|
+
kstat = KstatStruct.new(kstat[:ks_next])
|
95
|
+
next
|
96
|
+
end
|
97
|
+
|
98
|
+
if kstat_read(kptr, kstat, nil) < 0
|
99
|
+
raise SystemCallError.new('kstat_read', FFI.errno)
|
100
|
+
end
|
101
|
+
|
102
|
+
case kstat[:ks_type]
|
103
|
+
when 0 # KSTAT_TYPE_RAW
|
104
|
+
shash = map_raw_data_type(kstat)
|
105
|
+
when 1 # KS_TYPE_NAMED
|
106
|
+
shash = map_named_data_type(kstat)
|
107
|
+
when 2 # KS_TYPE_INTR
|
108
|
+
shash = map_intr_data_type(kstat)
|
109
|
+
when 3 # KS_TYPE_IO
|
110
|
+
shash = map_io_data_type(kstat)
|
111
|
+
when 4 # KS_TYPE_TIMER
|
112
|
+
shash = map_timer_data_type(kstat)
|
113
|
+
else
|
114
|
+
raise ArgumentError, 'unknown data record type'
|
115
|
+
end
|
116
|
+
|
117
|
+
# The various calls to .to_s here and elsewhere are necessary
|
118
|
+
# to convert FFI's CharArray to Ruby strings.
|
119
|
+
|
120
|
+
shash['class'] = kstat[:ks_class].to_s
|
121
|
+
|
122
|
+
ks_name = kstat[:ks_name].to_s
|
123
|
+
ks_instance = kstat[:ks_instance]
|
124
|
+
ks_module = kstat[:ks_module].to_s
|
125
|
+
|
126
|
+
nhash[ks_name] = shash
|
127
|
+
ihash[ks_instance] = nhash
|
128
|
+
mhash[ks_module] = ihash
|
129
|
+
|
130
|
+
kstat = KstatStruct.new(kstat[:ks_next])
|
131
|
+
end
|
132
|
+
ensure
|
133
|
+
kstat_close(kptr)
|
134
|
+
end
|
135
|
+
|
136
|
+
mhash
|
137
|
+
end # record
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def map_timer_data_type(kstat)
|
142
|
+
hash = {}
|
143
|
+
|
144
|
+
ktimer = KstatTimer.new(kstat[:ks_data])
|
145
|
+
|
146
|
+
ktime.members.each{ |m|
|
147
|
+
if m == :name
|
148
|
+
hash['name'] = ktimer[:name].to_s
|
149
|
+
else
|
150
|
+
hash[m.to_s] = ktimer[m]
|
151
|
+
end
|
152
|
+
}
|
153
|
+
|
154
|
+
hash
|
155
|
+
end
|
156
|
+
|
157
|
+
def map_io_data_type(kstat)
|
158
|
+
hash = {}
|
159
|
+
|
160
|
+
kio = KstatIo.new(kstat[:ks_data])
|
161
|
+
kio.members.each{ |m| hash[m.to_s] = kio[m] }
|
162
|
+
|
163
|
+
hash
|
164
|
+
end
|
165
|
+
|
166
|
+
def map_intr_data_type(kstat)
|
167
|
+
hash = {}
|
168
|
+
names = %w[hard soft watchdog spurious multiple_service]
|
169
|
+
|
170
|
+
0.upto(4){ |i|
|
171
|
+
ksi = KstatIntr.new(kstat[:ks_data] + (i * KstatIntr.size))
|
172
|
+
hash[names[i]] = ksi[:intrs][i]
|
173
|
+
}
|
174
|
+
|
175
|
+
hash
|
176
|
+
end
|
177
|
+
|
178
|
+
def map_raw_data_type(kstat)
|
179
|
+
hash = {}
|
180
|
+
|
181
|
+
if kstat[:ks_module].to_s == 'unix'
|
182
|
+
case kstat[:ks_name].to_s
|
183
|
+
when 'vminfo'
|
184
|
+
hash = map_raw_vminfo(kstat)
|
185
|
+
when 'flushmeter'
|
186
|
+
hash = map_raw_flushmeter(kstat)
|
187
|
+
when 'ncstats'
|
188
|
+
hash = map_raw_ncstats(kstat)
|
189
|
+
when 'sysinfo'
|
190
|
+
hash = map_raw_sysinfo(kstat)
|
191
|
+
when 'var'
|
192
|
+
hash = map_raw_var(kstat)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
if kstat[:ks_module].to_s == 'cpu_stat'
|
197
|
+
hash = map_raw_cpu_sysinfo(kstat)
|
198
|
+
end
|
199
|
+
|
200
|
+
if kstat[:ks_module].to_s == 'nfs'
|
201
|
+
if kstat[:ks_name].to_s == 'mntinfo'
|
202
|
+
hash = map_raw_mnt_info(kstat)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
hash
|
207
|
+
end
|
208
|
+
|
209
|
+
def map_raw_mnt_info(kstat)
|
210
|
+
hash = {}
|
211
|
+
|
212
|
+
mntinfo = Mntinfo.new(kstat[:ks_data])
|
213
|
+
|
214
|
+
mntinfo.members.each{ |m|
|
215
|
+
next if m == :mik_timers # TODO: Add this information
|
216
|
+
if [:mik_proto, :mik_curserver].include?(m)
|
217
|
+
hash[m.to_s] = mntinfo[m].to_s
|
218
|
+
else
|
219
|
+
hash[m.to_s] = mntinfo[m]
|
220
|
+
end
|
221
|
+
}
|
222
|
+
|
223
|
+
hash
|
224
|
+
end
|
225
|
+
|
226
|
+
def map_raw_cpu_sysinfo(kstat)
|
227
|
+
hash = {}
|
228
|
+
|
229
|
+
info = CpuSysinfo.new(kstat[:ks_data])
|
230
|
+
|
231
|
+
info.members.each{ |m|
|
232
|
+
if m == :cpu
|
233
|
+
hash['cpu_idle'] = info[:cpu][0]
|
234
|
+
hash['cpu_user'] = info[:cpu][1]
|
235
|
+
hash['cpu_kernel'] = info[:cpu][2]
|
236
|
+
hash['cpu_wait'] = info[:cpu][3]
|
237
|
+
elsif m == :wait
|
238
|
+
hash['wait_io'] = info[:wait][0]
|
239
|
+
hash['wait_swap'] = info[:wait][1]
|
240
|
+
hash['wait_pio'] = info[:wait][2]
|
241
|
+
else
|
242
|
+
hash[m.to_s] = info[m]
|
243
|
+
end
|
244
|
+
}
|
245
|
+
|
246
|
+
hash
|
247
|
+
end
|
248
|
+
|
249
|
+
def map_raw_vminfo(kstat)
|
250
|
+
hash = {}
|
251
|
+
|
252
|
+
vmi = Vminfo.new(kstat[:ks_data])
|
253
|
+
vmi.members.each{ |m| hash[m.to_s] = vmi[m] }
|
254
|
+
|
255
|
+
hash
|
256
|
+
end
|
257
|
+
|
258
|
+
def map_raw_flushmeter(kstat)
|
259
|
+
hash = {}
|
260
|
+
|
261
|
+
fm = Flushmeter.new(kstat[:ks_data])
|
262
|
+
fm.members.each{ |m| hash[m.to_s] = fm[m] }
|
263
|
+
|
264
|
+
hash
|
265
|
+
end
|
266
|
+
|
267
|
+
def map_raw_ncstats(kstat)
|
268
|
+
hash = {}
|
269
|
+
|
270
|
+
ncs = NcStats.new(kstat[:ks_data])
|
271
|
+
ncs.members.each{ |m| hash[m.to_s] = ncs[m] }
|
272
|
+
|
273
|
+
hash
|
274
|
+
end
|
275
|
+
|
276
|
+
def map_raw_sysinfo(kstat)
|
277
|
+
hash = {}
|
278
|
+
|
279
|
+
sys = Sysinfo.new(kstat[:ks_data])
|
280
|
+
sys.members.each{ |m| hash[m.to_s] = sys[m] }
|
281
|
+
|
282
|
+
hash
|
283
|
+
end
|
284
|
+
|
285
|
+
def map_raw_var(kstat)
|
286
|
+
hash = {}
|
287
|
+
|
288
|
+
var = Var.new(kstat[:ks_data])
|
289
|
+
var.members.each{ |m| hash[m.to_s] = var[m] }
|
290
|
+
|
291
|
+
hash
|
292
|
+
end
|
293
|
+
|
294
|
+
def map_named_data_type(kstat)
|
295
|
+
num = kstat[:ks_ndata]
|
296
|
+
hash = {}
|
297
|
+
|
298
|
+
0.upto(num-1){ |i|
|
299
|
+
knp = KstatNamed.new(kstat[:ks_data] + (i * KstatNamed.size))
|
300
|
+
name = knp[:name].to_s
|
301
|
+
|
302
|
+
case knp[:data_type]
|
303
|
+
when 0 # KSTAT_DATA_CHAR
|
304
|
+
hash[name] = knp[:value][:c].to_s
|
305
|
+
when 1 # KSTAT_DATA_INT32
|
306
|
+
hash[name] = knp[:value][:i32]
|
307
|
+
when 2 # KSTAT_DATA_UINT32
|
308
|
+
hash[name] = knp[:value][:ui32]
|
309
|
+
when 3 # KSTAT_DATA_INT64
|
310
|
+
hash[name] = knp[:value][:i64]
|
311
|
+
when 4 # KSTAT_DATA_UINT64
|
312
|
+
hash[name] = knp[:value][:ui64]
|
313
|
+
else
|
314
|
+
hash[name] = "unknown"
|
315
|
+
end
|
316
|
+
}
|
317
|
+
|
318
|
+
hash
|
319
|
+
end
|
320
|
+
end # Kstat
|
321
|
+
end # Solaris
|