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 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
- * ext/extconf.rb
8
- * ext/solaris/rkstat.c
9
- * ext/solaris/rkstat.h
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
- A Ruby interface for the Solaris kstat library.
2
+ A Ruby interface for the Solaris kstat library.
3
3
 
4
4
  == Prerequisites
5
- Solaris 8 (SunOS 2.8) or later.
5
+ Solaris 8 (SunOS 2.8) or later.
6
6
 
7
7
  == Installation
8
- gem install solaris-kstat
8
+ gem install solaris-kstat
9
9
 
10
10
  == Synopsis
11
- require 'solaris/kstat'
12
- require 'pp'
13
- include Solaris
11
+ require 'solaris/kstat'
12
+ require 'pp'
13
+ include Solaris
14
14
 
15
- k = Kstat.new('cpu_info', 0, 'cpu_info0')
16
- pp k.record
15
+ k = Kstat.new('cpu_info', 0, 'cpu_info0')
16
+ pp k.record
17
17
 
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'}}}}
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
- The following names will not return any meaningful value:
47
+ The following names will not return any meaningful value:
54
48
 
55
- * kstat_headers
56
- * sfmmu_global_stat
57
- * sfmmu_percpu_stat
49
+ * kstat_headers
50
+ * sfmmu_global_stat
51
+ * sfmmu_percpu_stat
58
52
 
59
- == Known Bugs
60
- You may receive a couple warnings during the build process. You
61
- can ignore these.
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
- I have noticed that results from the cpu_stat module differ from the output
65
- of the 'kstat' command line tool. I am convinced that my code is correct and
66
- that there is a bug in the Solaris::Kstat Perl module. Unfortunately, the
67
- source for the version of the Solaris::Kstat Perl module that works on
68
- Solaris 8 and later is not available (the version on CPAN only works on
69
- Solaris 6 and 7).
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
- See http://tinyurl.com/karxw for more details.
65
+ See http://tinyurl.com/karxw for more details.
72
66
 
73
67
  == Acknowledgements
74
- Thanks go to Charlie Mills for help with the 'volatile' issue.
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
- Add snaptime and crtime information to the statistics hash.
72
+ Add snaptime and crtime information to the statistics hash.
78
73
 
79
74
  == License
80
- Artistic 2.0
75
+ Artistic 2.0
81
76
 
82
77
  == Copyright
83
- (C) 2003-2011 Daniel J. Berger
84
- All Rights Reserved
78
+ (C) 2003-2013 Daniel J. Berger
79
+ All Rights Reserved
85
80
 
86
81
  == Warranty
87
- This package is provided "as is" and without any express or
88
- implied warranties, including, without limitation, the implied
89
- warranties of merchantability and fitness for a particular purpose.
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
- Daniel J. Berger
87
+ Daniel J. Berger
93
88
 
94
89
  == See Also
95
- kstat(1M)
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', # Gem files
9
- '**/*.rbc', # Rubinius
10
- '**/*.o', # C object file
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::Builder.new(spec).build
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 => [:build] do
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 => [:build] do
43
- ruby "-Iext examples/example_kstat.rb"
33
+ task :example do
34
+ ruby "-Ilib examples/example_kstat.rb"
44
35
  end
45
36
 
46
- Rake::TestTask.new do |t|
47
- task :test => [:build]
48
- t.libs << 'ext'
49
- t.verbose = true
50
- t.warning = true
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 => :test
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