cpu 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|