solaris-kstat 1.0.3 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|