cpu 0.0.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/Gemfile +13 -0
- data/README.rdoc +0 -0
- data/Rakefile +121 -0
- data/VERSION +1 -0
- data/bin/coretemp +87 -0
- data/cpu.gemspec +33 -0
- data/lib/cpu.rb +121 -0
- data/lib/cpu/load.rb +57 -0
- data/lib/cpu/msr.rb +31 -0
- data/lib/cpu/processor.rb +72 -0
- data/lib/cpu/shared.rb +25 -0
- data/lib/cpu/usage.rb +65 -0
- data/lib/cpu/usage_sampler.rb +40 -0
- data/lib/cpu/version.rb +8 -0
- data/munin/coretemp +24 -0
- data/tests/load_test.rb +35 -0
- data/tests/msr_test.rb +32 -0
- data/tests/usage_test.rb +14 -0
- metadata +84 -0
data/Gemfile
ADDED
data/README.rdoc
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
# vim: set filetype=ruby et sw=2 ts=2:
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rubygems/package_task'
|
5
|
+
rescue LoadError
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rcov/rcovtask'
|
9
|
+
rescue LoadError
|
10
|
+
end
|
11
|
+
require 'rake/clean'
|
12
|
+
require 'rake/testtask'
|
13
|
+
require 'rbconfig'
|
14
|
+
include Config
|
15
|
+
|
16
|
+
PKG_NAME = 'cpu'
|
17
|
+
PKG_VERSION = File.read('VERSION').chomp
|
18
|
+
PKG_FILES = FileList[*`git ls-files`.split(/\n/)].exclude '.gitignore'
|
19
|
+
CLEAN.include 'coverage', 'doc'
|
20
|
+
CLOBBER.include 'pkg'
|
21
|
+
|
22
|
+
desc "Install executable/library into site_ruby directories"
|
23
|
+
task :install do
|
24
|
+
bindir = CONFIG['bindir']
|
25
|
+
libdir = CONFIG['sitelibdir']
|
26
|
+
cd 'lib' do
|
27
|
+
for file in Dir['**/*.rb']
|
28
|
+
dest = File.join(libdir, file)
|
29
|
+
mkdir_p File.dirname(dest)
|
30
|
+
install file, dest, :verbose => true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
install('bin/coretemp', bindir, :verbose => true, :mode => 0755)
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "Create documentation"
|
37
|
+
task :doc do
|
38
|
+
sh "sdoc -m README.rdoc -t 'CPU' README.rdoc #{Dir['lib/**/*.rb'] * ' '}"
|
39
|
+
end
|
40
|
+
|
41
|
+
namespace :gems do
|
42
|
+
desc "Install all gems from the Gemfile"
|
43
|
+
task :install do
|
44
|
+
sh 'bundle install'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
if defined? Gem
|
49
|
+
spec = Gem::Specification.new do |s|
|
50
|
+
s.name = PKG_NAME
|
51
|
+
s.version = PKG_VERSION
|
52
|
+
s.summary = "CPU information in Ruby/Linux"
|
53
|
+
s.description = "Library to gather CPU information (load averages, usage,"\
|
54
|
+
" temperature) in Ruby on Linux"
|
55
|
+
|
56
|
+
s.executables = 'coretemp'
|
57
|
+
s.files = PKG_FILES
|
58
|
+
|
59
|
+
s.require_path = 'lib'
|
60
|
+
|
61
|
+
s.add_dependency 'spruz', '~>0.2.2'
|
62
|
+
|
63
|
+
s.rdoc_options << '--main' << 'README.rdoc' << '--title' << 'CPU'
|
64
|
+
s.extra_rdoc_files << 'README.rdoc'
|
65
|
+
s.test_files.concat Dir['tests/test_*.rb']
|
66
|
+
|
67
|
+
s.author = "Florian Frank"
|
68
|
+
s.email = "flori@ping.de"
|
69
|
+
s.homepage = "http://flori.github.com/#{PKG_NAME}"
|
70
|
+
s.rubyforge_project = PKG_NAME
|
71
|
+
end
|
72
|
+
|
73
|
+
desc 'Create a gemspec file'
|
74
|
+
task :gemspec => :version do
|
75
|
+
File.open('cpu.gemspec', 'w') do |gemspec|
|
76
|
+
gemspec.write spec.to_ruby
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
Gem::PackageTask.new(spec) do |pkg|
|
81
|
+
pkg.need_tar = true
|
82
|
+
pkg.package_files += PKG_FILES
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
Rake::TestTask.new do |t|
|
88
|
+
t.test_files = FileList['tests/**/*_test.rb']
|
89
|
+
t.verbose = true
|
90
|
+
end
|
91
|
+
|
92
|
+
if defined? Rcov
|
93
|
+
Rcov::RcovTask.new do |t|
|
94
|
+
t.test_files = FileList['tests/**/*_test.rb']
|
95
|
+
t.verbose = true
|
96
|
+
t.rcov_opts = %w[-x '\\btests\/' -x '\\bgems\/']
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
desc m = "Writing version information for #{PKG_VERSION}"
|
101
|
+
task :version do
|
102
|
+
puts m
|
103
|
+
File.open(File.join('lib', 'cpu', 'version.rb'), 'w') do |v|
|
104
|
+
v.puts <<EOT
|
105
|
+
module CPU
|
106
|
+
# CPU version
|
107
|
+
VERSION = '#{PKG_VERSION}'
|
108
|
+
VERSION_ARRAY = VERSION.split(/\\./).map { |x| x.to_i } # :nodoc:
|
109
|
+
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
|
110
|
+
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
|
111
|
+
VERSION_BUILD = VERSION_ARRAY[2] # :nodoc:
|
112
|
+
end
|
113
|
+
EOT
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
desc "Run the tests by default"
|
118
|
+
task :default => [ :version, :test ]
|
119
|
+
|
120
|
+
desc "Prepare release of the library"
|
121
|
+
task :release => [ :clean, :gemspec, :package ]
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
data/bin/coretemp
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'cpu'
|
4
|
+
require 'spruz/go'
|
5
|
+
include Spruz::GO
|
6
|
+
require 'bullshit'
|
7
|
+
|
8
|
+
class ParseCommands
|
9
|
+
def self.parse(argv)
|
10
|
+
new(argv).parse
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(argv)
|
14
|
+
@argv = argv
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_t_j_max(temp)
|
18
|
+
t_j_max = temp and CPU.t_j_max = t_j_max.to_i
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_history_path(filename)
|
22
|
+
filename ||= '~/.coretemp-history'
|
23
|
+
File.expand_path(filename)
|
24
|
+
end
|
25
|
+
|
26
|
+
def measure_temps(opts)
|
27
|
+
set_t_j_max opts['t']
|
28
|
+
CPU.each_core.map(&:temperature)
|
29
|
+
end
|
30
|
+
|
31
|
+
def simple
|
32
|
+
opts = go 't', @argv
|
33
|
+
puts measure_temps(opts) * ' '
|
34
|
+
end
|
35
|
+
|
36
|
+
def collect
|
37
|
+
opts = go 'tf', @argv
|
38
|
+
filename = create_history_path opts['f']
|
39
|
+
temperatures = measure_temps(opts)
|
40
|
+
line = ([ Time.now.to_f ] + temperatures) * ' '
|
41
|
+
if filename
|
42
|
+
open(filename, 'a') do |o|
|
43
|
+
o.puts line
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def clear
|
49
|
+
opts = go 'f', @argv
|
50
|
+
filename = create_history_path opts['f']
|
51
|
+
if filename and File.exist?(filename)
|
52
|
+
File.unlink(filename)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def stats
|
57
|
+
opts = go 'f', @argv
|
58
|
+
filename = create_history_path opts['f']
|
59
|
+
if filename and File.exist?(filename)
|
60
|
+
history = open(filename, 'r') { |o|
|
61
|
+
o.map do |line|
|
62
|
+
time, *temps = line.split(/\s+/)
|
63
|
+
[ Time.at(Float(time)) ] + temps.map { |t| Integer(t) }
|
64
|
+
end
|
65
|
+
}
|
66
|
+
measurements_last = history.last[1..-1]
|
67
|
+
times, *measurements = history.transpose
|
68
|
+
analyses = measurements.map { |m| Bullshit::Analysis.new(m) }
|
69
|
+
puts " min: %s" % (analyses.map { |a| a.min } * ' ')
|
70
|
+
puts "last: %s" % (measurements_last * ' ')
|
71
|
+
puts " max: %s" % (analyses.map { |a| a.max } * ' ')
|
72
|
+
puts "mean: %s" % (analyses.map { |a| a.mean.round } * ' ')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def parse
|
77
|
+
case command = @argv.shift
|
78
|
+
when 'collect' then collect
|
79
|
+
when 'clear' then clear
|
80
|
+
when 'stats' then stats
|
81
|
+
when 'collect_stats' then collect; stats
|
82
|
+
else simple
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
ParseCommands.parse(ARGV)
|
data/cpu.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{cpu}
|
5
|
+
s.version = "0.0.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = [%q{Florian Frank}]
|
9
|
+
s.date = %q{2011-07-03}
|
10
|
+
s.description = %q{Library to gather CPU information (load averages, usage, temperature) in Ruby on Linux}
|
11
|
+
s.email = %q{flori@ping.de}
|
12
|
+
s.executables = [%q{coretemp}]
|
13
|
+
s.extra_rdoc_files = [%q{README.rdoc}]
|
14
|
+
s.files = [%q{Gemfile}, %q{README.rdoc}, %q{Rakefile}, %q{VERSION}, %q{bin/coretemp}, %q{cpu.gemspec}, %q{lib/cpu.rb}, %q{lib/cpu/load.rb}, %q{lib/cpu/msr.rb}, %q{lib/cpu/processor.rb}, %q{lib/cpu/shared.rb}, %q{lib/cpu/usage.rb}, %q{lib/cpu/usage_sampler.rb}, %q{lib/cpu/version.rb}, %q{munin/coretemp}, %q{tests/load_test.rb}, %q{tests/msr_test.rb}, %q{tests/usage_test.rb}]
|
15
|
+
s.homepage = %q{http://flori.github.com/cpu}
|
16
|
+
s.rdoc_options = [%q{--main}, %q{README.rdoc}, %q{--title}, %q{CPU}]
|
17
|
+
s.require_paths = [%q{lib}]
|
18
|
+
s.rubyforge_project = %q{cpu}
|
19
|
+
s.rubygems_version = %q{1.8.5}
|
20
|
+
s.summary = %q{CPU information in Ruby/Linux}
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
26
|
+
s.add_runtime_dependency(%q<spruz>, ["~> 0.2.2"])
|
27
|
+
else
|
28
|
+
s.add_dependency(%q<spruz>, ["~> 0.2.2"])
|
29
|
+
end
|
30
|
+
else
|
31
|
+
s.add_dependency(%q<spruz>, ["~> 0.2.2"])
|
32
|
+
end
|
33
|
+
end
|
data/lib/cpu.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spruz/uniq_by'
|
2
|
+
require 'cpu/shared'
|
3
|
+
require 'cpu/processor'
|
4
|
+
require 'cpu/msr'
|
5
|
+
require 'cpu/load'
|
6
|
+
require 'cpu/usage_sampler'
|
7
|
+
require 'cpu/usage'
|
8
|
+
|
9
|
+
# This module provides (read) access to the Model Specific Registers of Intel
|
10
|
+
# CPUs on Linux.
|
11
|
+
module CPU
|
12
|
+
class << self
|
13
|
+
# The t_j_max temperature of that is used as a default for this Processor
|
14
|
+
# if it cannot be queried (e.g. Core2 architecture). It defaults to 95
|
15
|
+
# which is the correct value for Core2 Duo E8400 (Wolfsdale). Be sure to
|
16
|
+
# set the correct value for your Core2 CPU here, otherwise your
|
17
|
+
# temperature measurements will be incorrect.
|
18
|
+
attr_accessor :t_j_max
|
19
|
+
end
|
20
|
+
self.t_j_max = 95
|
21
|
+
|
22
|
+
# XXX
|
23
|
+
class CPUError < StandardError; end
|
24
|
+
|
25
|
+
# This exception is thrown if an invalid ProcessorId is queried.
|
26
|
+
class InvalidProcessorIdError < CPUError; end
|
27
|
+
|
28
|
+
# XXX
|
29
|
+
class NoSampleDataError < CPUError; end
|
30
|
+
|
31
|
+
class << self
|
32
|
+
# The path to the modprobe binary which is used to load the required module
|
33
|
+
# if necessary.
|
34
|
+
attr_accessor :modprobe_path
|
35
|
+
end
|
36
|
+
self.modprobe_path = '/sbin/modprobe'
|
37
|
+
|
38
|
+
class << self
|
39
|
+
# Return an array of all Processor instances for this machine.
|
40
|
+
def processors
|
41
|
+
cpu_cores = {}
|
42
|
+
cpuinfos = File.read('/proc/cpuinfo').chomp.split(/^$/)
|
43
|
+
processor_ids =
|
44
|
+
cpuinfos.map { |l| l[/processor\s*:\s*(\d+)/, 1].to_i rescue nil }
|
45
|
+
core_ids = cpuinfos.map { |l| l[/core id\s*:\s*(\d+)/, 1].to_i rescue nil }
|
46
|
+
processor_ids.zip(core_ids) do |processor_id, core_id|
|
47
|
+
cpu_cores[processor_id] = core_id
|
48
|
+
end
|
49
|
+
processors = Dir.open('/dev/cpu').inject([]) do |ps, processor_id|
|
50
|
+
processor_id =~ /\A\d+\Z/ or next ps
|
51
|
+
ps << processor_id.to_i
|
52
|
+
end
|
53
|
+
processors.extend Spruz::UniqBy
|
54
|
+
processors.sort!
|
55
|
+
@num_processors = processors.size
|
56
|
+
@num_cores = cpu_cores.invert.size
|
57
|
+
processors.map! do |processor_id|
|
58
|
+
Processor.new(processor_id, cpu_cores[processor_id])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def num_processors
|
63
|
+
@num_processors or processors
|
64
|
+
@num_processors
|
65
|
+
end
|
66
|
+
|
67
|
+
def num_cores
|
68
|
+
@num_cores or processors
|
69
|
+
@num_cores
|
70
|
+
end
|
71
|
+
|
72
|
+
alias to_a processors
|
73
|
+
|
74
|
+
def each_core(&block)
|
75
|
+
processors.uniq_by(&:core_id).each(&block)
|
76
|
+
end
|
77
|
+
|
78
|
+
def cores
|
79
|
+
each_core.to_a
|
80
|
+
end
|
81
|
+
|
82
|
+
# Iterate over each Processor instance for this machine and yield to the
|
83
|
+
# block for each of them.
|
84
|
+
def each_processor(&block)
|
85
|
+
processors.each(&block)
|
86
|
+
end
|
87
|
+
|
88
|
+
alias each each_processor
|
89
|
+
|
90
|
+
include Enumerable
|
91
|
+
|
92
|
+
def load
|
93
|
+
Load.new
|
94
|
+
end
|
95
|
+
|
96
|
+
def usage(interval = 1)
|
97
|
+
before_usage = UsageSampler.new
|
98
|
+
if block_given?
|
99
|
+
yield
|
100
|
+
else
|
101
|
+
sleep interval
|
102
|
+
end
|
103
|
+
after_usage = UsageSampler.new
|
104
|
+
processors.each do |processor|
|
105
|
+
processor.usage = after_usage.usages[processor.processor_id] -
|
106
|
+
before_usage.usages[processor.processor_id]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def sum_usage_processor(interval = 1)
|
111
|
+
processors = usage(interval)
|
112
|
+
processor = Processor.new -1, -1
|
113
|
+
processor.num_processors = processor.num_cores = 1
|
114
|
+
processor.temperature = processors.map(&:temperature).max
|
115
|
+
processor.usage = processors.map(&:usage).inject { |s, u| s + u }
|
116
|
+
processor.usage.num_processors = processor.usage.num_cores = 1
|
117
|
+
processor.freeze
|
118
|
+
processor
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/lib/cpu/load.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
module CPU
|
2
|
+
class Load
|
3
|
+
include Shared
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@load_data = File.open('/proc/loadavg') do |loadavg|
|
7
|
+
loadavg.readline.split(/\s+/).first(3).map(&:to_f)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def last_minute
|
12
|
+
@load_data[0]
|
13
|
+
end
|
14
|
+
|
15
|
+
def last_minute_by_core
|
16
|
+
last_minute / num_cores
|
17
|
+
end
|
18
|
+
|
19
|
+
def last_minute_by_processor
|
20
|
+
last_minute / num_processors
|
21
|
+
end
|
22
|
+
|
23
|
+
def last_5_minutes
|
24
|
+
@load_data[1]
|
25
|
+
end
|
26
|
+
|
27
|
+
def last_5_minutes_by_core
|
28
|
+
last_5_minutes / num_cores
|
29
|
+
end
|
30
|
+
|
31
|
+
def last_5_minutes_by_processor
|
32
|
+
last_5_minutes / num_processors
|
33
|
+
end
|
34
|
+
|
35
|
+
def last_15_minutes
|
36
|
+
@load_data[2]
|
37
|
+
end
|
38
|
+
|
39
|
+
def last_15_minutes_by_core
|
40
|
+
last_15_minutes / num_cores
|
41
|
+
end
|
42
|
+
|
43
|
+
def last_15_minutes_by_processor
|
44
|
+
last_15_minutes / num_processors
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_a
|
48
|
+
@load_data.dup
|
49
|
+
end
|
50
|
+
|
51
|
+
def inspect
|
52
|
+
"#<#{self.class}: #{to_a * ' '}>"
|
53
|
+
end
|
54
|
+
|
55
|
+
alias to_s inspect
|
56
|
+
end
|
57
|
+
end
|
data/lib/cpu/msr.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module CPU
|
2
|
+
class MSR
|
3
|
+
# Returns true if the msr functionality is already available in the kernel
|
4
|
+
# (either compiled into it or via a module).
|
5
|
+
def self.available?
|
6
|
+
File.exist?('/dev/cpu/0/msr')
|
7
|
+
end
|
8
|
+
|
9
|
+
# Loads the msr module and sleeps for a second afterwards.
|
10
|
+
def self.load_module
|
11
|
+
system "#{CPU.modprobe_path} msr"
|
12
|
+
sleep 1
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(processor_id)
|
16
|
+
self.class.available? or self.class.load_module
|
17
|
+
begin
|
18
|
+
@io = IO.new IO.sysopen('/dev/cpu/%d/msr' % processor_id, 'rb')
|
19
|
+
rescue Errno::ENOENT
|
20
|
+
raise InvalidProcessorIdError, "'#{processor_id}' is not a valid processor_id on this machine"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the byte at +offset+ as an integer number.
|
25
|
+
def [](offset)
|
26
|
+
@io.sysseek(offset)
|
27
|
+
data, = @io.sysread(8).unpack('q')
|
28
|
+
data
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module CPU
|
2
|
+
class Processor
|
3
|
+
include Shared
|
4
|
+
|
5
|
+
# Returns a Processor instance for Processor +processor_id+. If this
|
6
|
+
# Processor doesn't exist an InvalidProcessorIdError exception is thrown.
|
7
|
+
def initialize(processor_id, core_id = nil)
|
8
|
+
@processor_id, @core_id = processor_id, core_id
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns the processor_id of this Processor, an integer in (0..(n - 1))
|
12
|
+
# for a n-core machine.
|
13
|
+
attr_reader :processor_id
|
14
|
+
|
15
|
+
# Returns the core_id of this Processor.
|
16
|
+
attr_reader :core_id
|
17
|
+
|
18
|
+
# Returns an msr object and caches it.
|
19
|
+
def msr
|
20
|
+
@msr ||= MSR.new(processor_id)
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_writer :usage
|
24
|
+
|
25
|
+
def usage(interval = 1)
|
26
|
+
unless @usage
|
27
|
+
if processor = CPU.usage(interval).find { |p| p.processor_id == processor_id }
|
28
|
+
@usage = processor.usage
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@usage
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the distance between the core temperature of this Processor and
|
35
|
+
# its t_j_max temperature as an integer number.
|
36
|
+
def t_j_max_distance
|
37
|
+
(msr[0x19c] >> 16) & 0x7f
|
38
|
+
end
|
39
|
+
|
40
|
+
# This method returns the t_j_max temperature of this Processor if the
|
41
|
+
# processor supports it (e. g. Intel i7 architecture) as an integer number,
|
42
|
+
# otherwise 0 is returned.
|
43
|
+
def t_j_max
|
44
|
+
(msr[0x1a2] >> 16) & 0x7f
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the core temperature of this Processor as an integer number. This
|
48
|
+
# should work on all Core2 architecures if you set CPU.t_j_max
|
49
|
+
# to the correct value for your Processor. On i7 architectures (and newer?)
|
50
|
+
# it should work without any further configuration.
|
51
|
+
def temperature
|
52
|
+
if @temperature
|
53
|
+
@temperature
|
54
|
+
else
|
55
|
+
my_t_j_max = t_j_max.nonzero? || CPU.t_j_max
|
56
|
+
my_t_j_max - t_j_max_distance
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
attr_writer :temperature
|
61
|
+
|
62
|
+
def inspect
|
63
|
+
if processor_id >= 0
|
64
|
+
result = "#<#{self.class}: #@processor_id"
|
65
|
+
result << " (core#@core_id)" if @core_id
|
66
|
+
result << '>'
|
67
|
+
else
|
68
|
+
"#<#{self.class}: total>"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/cpu/shared.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module CPU
|
2
|
+
module Shared
|
3
|
+
# Returns the number of processors in this computer
|
4
|
+
def num_processors
|
5
|
+
if @num_processors
|
6
|
+
@num_processors
|
7
|
+
else
|
8
|
+
CPU.num_processors
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_writer :num_processors
|
13
|
+
|
14
|
+
# Returns the number of cores in this computer
|
15
|
+
def num_cores
|
16
|
+
if @num_cores
|
17
|
+
@num_cores
|
18
|
+
else
|
19
|
+
CPU.num_cores
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_writer :num_cores
|
24
|
+
end
|
25
|
+
end
|
data/lib/cpu/usage.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
module CPU
|
2
|
+
class Usage < Struct.new('Usage', :usage, :processor_id, :user, :nice,
|
3
|
+
:system, :idle, :start_at, :stop_at)
|
4
|
+
include Shared
|
5
|
+
|
6
|
+
def +(other)
|
7
|
+
self.class.new(*(
|
8
|
+
[
|
9
|
+
usage,
|
10
|
+
processor_id == other.processor_id ? processor_id : 0,
|
11
|
+
] +
|
12
|
+
values_at(2..-3).zip(other.values_at(2..-3)).map { |x, y| x + y } +
|
13
|
+
[
|
14
|
+
[ start_at, other.start_at ].min,
|
15
|
+
[ stop_at, other.stop_at ].max
|
16
|
+
])
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def -(other)
|
21
|
+
self + other * -1
|
22
|
+
end
|
23
|
+
|
24
|
+
def *(scalar)
|
25
|
+
scalar = scalar.to_f
|
26
|
+
self.class.new(*(
|
27
|
+
[
|
28
|
+
usage,
|
29
|
+
processor_id,
|
30
|
+
] +
|
31
|
+
values_at(2..-3).map { |x| x * scalar } +
|
32
|
+
[
|
33
|
+
start_at,
|
34
|
+
stop_at,
|
35
|
+
])
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def /(scalar)
|
40
|
+
self * (1.0 / scalar)
|
41
|
+
end
|
42
|
+
|
43
|
+
def process_time
|
44
|
+
user + nice + system
|
45
|
+
end
|
46
|
+
|
47
|
+
def real_time
|
48
|
+
stop_at - start_at
|
49
|
+
end
|
50
|
+
|
51
|
+
def total_time
|
52
|
+
values_at(2..-3).inject(0.0) { |s, x| s + x }
|
53
|
+
end
|
54
|
+
|
55
|
+
def percentage(time = total_time)
|
56
|
+
100.0 * process_time / time
|
57
|
+
end
|
58
|
+
|
59
|
+
def inspect
|
60
|
+
"#<#{self.class}: #{percentage}>"
|
61
|
+
end
|
62
|
+
|
63
|
+
alias to_s inspect
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module CPU
|
2
|
+
class UsageSampler
|
3
|
+
def initialize
|
4
|
+
sample
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :usages
|
8
|
+
|
9
|
+
def sum_usages
|
10
|
+
@usages and @usages.values.inject(&:+)
|
11
|
+
end
|
12
|
+
|
13
|
+
CPU = /^cpu(\d+)#{'\s+(\d+)' * 4}/
|
14
|
+
|
15
|
+
USER_HZ = 100
|
16
|
+
|
17
|
+
def sample
|
18
|
+
total, @usages = nil, {}
|
19
|
+
timestamp = Time.now
|
20
|
+
File.foreach('/proc/stat') do |line|
|
21
|
+
case line
|
22
|
+
when /^cpu[^\d]/
|
23
|
+
next
|
24
|
+
when CPU
|
25
|
+
times = $~.captures
|
26
|
+
processor_id = times[0] = times[0].to_i
|
27
|
+
(1...times.size).each { |i| times[i] = times[i].to_f / USER_HZ }
|
28
|
+
times << timestamp << timestamp
|
29
|
+
@usages[processor_id] = Usage.new(self, *times)
|
30
|
+
else
|
31
|
+
break
|
32
|
+
end
|
33
|
+
end
|
34
|
+
if @usages.empty?
|
35
|
+
raise NoSampleDataError, "could not sample measurement data"
|
36
|
+
end
|
37
|
+
self
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/cpu/version.rb
ADDED
data/munin/coretemp
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'cpu'
|
4
|
+
|
5
|
+
if t_j_max = ENV['t_j_max']
|
6
|
+
CPU.t_j_max = t_j_max.to_i
|
7
|
+
end
|
8
|
+
|
9
|
+
case ARGV.shift
|
10
|
+
when 'config'
|
11
|
+
puts <<EOT
|
12
|
+
graph_title CPU Core temperatures
|
13
|
+
graph_args --base 1000 -l 0
|
14
|
+
graph_vtitle Celsius
|
15
|
+
graph_category sensors
|
16
|
+
EOT
|
17
|
+
for c in CPU.cores
|
18
|
+
puts "core#{c.core_id}.label Core #{c.core_id}"
|
19
|
+
end
|
20
|
+
else
|
21
|
+
for c in CPU.cores
|
22
|
+
puts "core#{c.core_id}.value #{c.temperature}"
|
23
|
+
end
|
24
|
+
end
|
data/tests/load_test.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'mocha'
|
3
|
+
require 'cpu'
|
4
|
+
|
5
|
+
module CPU
|
6
|
+
class LoadTest < Test::Unit::TestCase
|
7
|
+
def test_load_minutes
|
8
|
+
@load = CPU.load
|
9
|
+
@load.stubs(:num_processors).returns(4)
|
10
|
+
@load.stubs(:num_cores).returns(2)
|
11
|
+
for v in %w[last_minute last_5_minutes last_15_minutes]
|
12
|
+
assert_kind_of Float, @load.__send__(v)
|
13
|
+
assert_operator @load.__send__(v), :>=, 0
|
14
|
+
assert_in_delta\
|
15
|
+
4 * @load.__send__("#{v}_by_processor"),
|
16
|
+
@load.__send__(v),
|
17
|
+
1E-3
|
18
|
+
assert_in_delta\
|
19
|
+
2 * @load.__send__("#{v}_by_core"),
|
20
|
+
@load.__send__(v),
|
21
|
+
1E-3
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_load_array
|
26
|
+
load_array = CPU.load.to_a
|
27
|
+
assert_equal 3, load_array.size
|
28
|
+
for v in load_array
|
29
|
+
assert_kind_of Float, v
|
30
|
+
assert_operator v, :>=, 0
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
data/tests/msr_test.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'cpu'
|
3
|
+
|
4
|
+
module CPU
|
5
|
+
class MSRTest < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@cpu0, @cpu1 = CPU.to_a
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_loaded
|
11
|
+
assert CPU::MSR.available?
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_instance
|
15
|
+
assert_kind_of CPU::Processor, @cpu0
|
16
|
+
assert_equal 0, @cpu0.processor_id
|
17
|
+
assert_kind_of CPU::Processor, @cpu1
|
18
|
+
assert_equal 1, @cpu1.processor_id
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_temperature
|
22
|
+
assert_operator @cpu0.temperature, '>', 0
|
23
|
+
assert_operator @cpu1.temperature, '>', 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_wrong_processor_id
|
27
|
+
assert_raise(InvalidProcessorIdError) do
|
28
|
+
CPU::MSR.new(CPU.num_processors)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/tests/usage_test.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'mocha'
|
3
|
+
require 'cpu'
|
4
|
+
|
5
|
+
module CPU
|
6
|
+
class UsageTest < Test::Unit::TestCase
|
7
|
+
def test_sum_usage
|
8
|
+
@processor = CPU.sum_usage 0.1
|
9
|
+
assert_kind_of Processor, @processor
|
10
|
+
assert_operator @processor.usage.percentage, :>, 0
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cpu
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Florian Frank
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-07-03 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: spruz
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.2.2
|
24
|
+
type: :runtime
|
25
|
+
version_requirements: *id001
|
26
|
+
description: Library to gather CPU information (load averages, usage, temperature) in Ruby on Linux
|
27
|
+
email: flori@ping.de
|
28
|
+
executables:
|
29
|
+
- coretemp
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files:
|
33
|
+
- README.rdoc
|
34
|
+
files:
|
35
|
+
- Gemfile
|
36
|
+
- README.rdoc
|
37
|
+
- Rakefile
|
38
|
+
- VERSION
|
39
|
+
- bin/coretemp
|
40
|
+
- cpu.gemspec
|
41
|
+
- lib/cpu.rb
|
42
|
+
- lib/cpu/load.rb
|
43
|
+
- lib/cpu/msr.rb
|
44
|
+
- lib/cpu/processor.rb
|
45
|
+
- lib/cpu/shared.rb
|
46
|
+
- lib/cpu/usage.rb
|
47
|
+
- lib/cpu/usage_sampler.rb
|
48
|
+
- lib/cpu/version.rb
|
49
|
+
- munin/coretemp
|
50
|
+
- tests/load_test.rb
|
51
|
+
- tests/msr_test.rb
|
52
|
+
- tests/usage_test.rb
|
53
|
+
homepage: http://flori.github.com/cpu
|
54
|
+
licenses: []
|
55
|
+
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options:
|
58
|
+
- --main
|
59
|
+
- README.rdoc
|
60
|
+
- --title
|
61
|
+
- CPU
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: "0"
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: "0"
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project: cpu
|
79
|
+
rubygems_version: 1.8.5
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: CPU information in Ruby/Linux
|
83
|
+
test_files: []
|
84
|
+
|