jkr 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,110 @@
1
+ require 'thor'
2
+ require 'term/ansicolor'
3
+
4
+ module Jkr
5
+ class CLI < ::Thor
6
+ include Term::ANSIColor
7
+
8
+ class_option :debug, :type => :boolean
9
+ class_option :directory, :type => :string, :default => Dir.pwd, :aliases => :C
10
+
11
+ def self.exit_on_failure?
12
+ true
13
+ end
14
+
15
+ desc "init", "Initialize a Jkr environment"
16
+ def init()
17
+ dir = options[:directory]
18
+
19
+ jkr_dir = File.join(dir, "jkr")
20
+ result_dir = File.join(dir, "jkr", "result")
21
+ plan_dir = File.join(dir, "jkr", "plan")
22
+ script_dir = File.join(dir, "jkr", "script")
23
+
24
+ puts "Preparing a new Jkr environment ... @ #{dir}"
25
+
26
+ [jkr_dir, result_dir, plan_dir, script_dir].each do |dir|
27
+ puts " making directory: #{dir}"
28
+ FileUtils.mkdir(dir)
29
+ end
30
+
31
+ [result_dir, plan_dir, script_dir].each do |dir|
32
+ File.open(File.expand_path(".gitdir", dir), "w") do |_|
33
+ end
34
+ end
35
+
36
+ puts " preparing an example plan: example.plan"
37
+ FileUtils.cp(File.expand_path("../../etc/example.plan", __dir__),
38
+ File.join(plan_dir, "example.plan"))
39
+
40
+ puts ""
41
+ puts "... done"
42
+ end
43
+
44
+ desc "list", "List executable plans"
45
+ def list()
46
+ begin
47
+ @jkr_env = Jkr::Env.new(options[:directory])
48
+ rescue Errno::ENOENT
49
+ puts(red("[ERROR] jkr dir not found at #{@options[:directory]}"))
50
+ puts(red(" Maybe you are in a wrong directory."))
51
+ exit(false)
52
+ end
53
+
54
+ plans = Dir.glob("#{@jkr_env.jkr_plan_dir}/*.plan").map do |plan_file_path|
55
+ plan = Jkr::Plan.create_by_name(@jkr_env, File.basename(plan_file_path, ".plan"))
56
+ [File.basename(plan_file_path, ".plan"), plan.title]
57
+ end
58
+
59
+ if ENV["JKR_ZSHCOMP_HELPER"]
60
+ plans.each do |plan_name, plan_title|
61
+ puts "#{plan_name}[#{plan_title}]"
62
+ end
63
+ return
64
+ end
65
+
66
+ puts "Existing plans:"
67
+ puts
68
+ maxlen = plans.map{|plan| plan[0].size}.max
69
+ plans.each do |plan|
70
+ printf(" %#{maxlen}s : %s\n", plan[0], plan[1])
71
+ end
72
+ puts
73
+ end
74
+
75
+ desc "execute <plan> [<plan> ...]", "Execute plans"
76
+ def execute(*plan_names)
77
+ @jkr_env = Jkr::Env.new(options[:directory])
78
+
79
+ if options[:debug]
80
+ delete_files_on_error = false
81
+ else
82
+ delete_files_on_error = true
83
+ end
84
+
85
+ if plan_names.size > 0
86
+ plan_name = plan_names.first
87
+ plan = Jkr::Plan.create_by_name(@jkr_env, plan_name)
88
+ Jkr::Trial.run(@jkr_env, plan, delete_files_on_error)
89
+ end
90
+ end
91
+
92
+ desc "analyze <result> [<result> ...]", "Run analysis script for executed results"
93
+ def analyze(*result_ids)
94
+ @jkr_env = Jkr::Env.new(options[:directory])
95
+
96
+ result_ids.each do |arg|
97
+ Jkr::Analysis.analyze(@jkr_env, arg)
98
+ end
99
+ end
100
+
101
+ no_commands do
102
+ def find_plan_file(plan_name)
103
+ @jkr_env.plans.find do |plan_file_path|
104
+ File.basename(plan_file_path) == plan_name + ".plan"
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ end # Jkr
@@ -0,0 +1,81 @@
1
+
2
+ module Jkr
3
+ class CpuUsageMonitor
4
+ def initialize
5
+ @checkpoint1 = nil
6
+ @checkpoint2 = nil
7
+
8
+ self.checkpoint
9
+ end
10
+
11
+ def read_stat
12
+ stat_str = `cat /proc/stat`
13
+ cpu_total = nil
14
+ cpus = Array.new
15
+ stat_str.each_line do |line|
16
+ case line
17
+ when /cpu (.*)$/
18
+ user, nice, sys, idle, *rest = $~[1].strip.split.map(&:to_i)
19
+ rest = rest.inject(&:+)
20
+ cpu_total = {:user => user, :sys => sys, :nice => nice, :idle => idle, :rest => rest}
21
+ when /cpu(\d+) (.*)$/
22
+ idx = $~[1].to_i
23
+ user, nice, sys, idle, *rest = $~[2].strip.split.map(&:to_i)
24
+ cpus[idx] = {:user => user, :sys => sys, :nice => nice, :idle => idle, :rest => rest}
25
+ end
26
+ end
27
+
28
+ {:system => cpu_total,
29
+ :cpus => cpus}
30
+ end
31
+
32
+ def checkpoint
33
+ @checkpoint2 = @checkpoint1
34
+ @checkpoint1 = self.read_stat
35
+ end
36
+
37
+ def reset
38
+ @checkpoint2 = @checkpoint1 = nil
39
+ end
40
+
41
+ def checkpoint_and_get_usage
42
+ self.checkpoint
43
+ self.get_last_usage
44
+ end
45
+
46
+ def get_usage
47
+ unless @checkpoint1
48
+ raise RuntimeError.new("Checkpointing is required")
49
+ end
50
+
51
+ self.calc_usage(self.read_stat[:system], @checkpoint1[:system])
52
+ end
53
+
54
+ def get_last_usage
55
+ unless @checkpoint1 && @checkpoint2
56
+ raise RuntimeError.new("At least two checkpoints are required")
57
+ end
58
+
59
+ self.calc_usage(@checkpoint2[:system], @checkpoint1[:system])
60
+ end
61
+
62
+ def calc_usage(stat1, stat2)
63
+ stat1_clk, stat2_clk = [stat1, stat2].map{|stat|
64
+ stat.values.inject(&:+)
65
+ }
66
+ if stat1_clk > stat2_clk
67
+ stat1, stat2, stat1_clk, stat2_clk = [stat2, stat1, stat2_clk, stat1_clk]
68
+ elsif stat1_clk == stat2_clk
69
+ raise RuntimeError.new("Same clock count. cannot calc usage.")
70
+ end
71
+ clk_diff = (stat2_clk - stat1_clk).to_f
72
+ ret = Hash.new
73
+ [:user, :sys, :nice, :idle].map{|key|
74
+ ret[key] = (stat2[key] - stat1[key]) / clk_diff
75
+ }
76
+ ret[:total] = 1.0 - ret[:idle]
77
+
78
+ ret
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'jkr/array'
4
+
5
+ module Jkr
6
+ class Cpufreq
7
+ def self.cpupath(cpu_idx = nil)
8
+ if cpu_idx
9
+ self.cpupath() + "/cpu#{cpu_idx}"
10
+ else
11
+ "/sys/devices/system/cpu"
12
+ end
13
+ end
14
+
15
+ def self.cpufreqpath(cpu_idx = 0)
16
+ cpupath(cpu_idx) + "/cpufreq"
17
+ end
18
+
19
+ def self.num_cpu()
20
+ Dir.glob(cpupath("*")).select{|file| file =~ /cpu\d+$/}.size
21
+ end
22
+
23
+ def self.available?()
24
+ (0..(self.num_cpu() - 1)).to_a.every?{|cpu_idx|
25
+ File.exists?(cpufreqpath(cpu_idx))
26
+ }
27
+ end
28
+
29
+ def self.config()
30
+ Config.get()
31
+ end
32
+
33
+ def self.available_frequency(cpu_idx = 0)
34
+ if self.available?
35
+ `cat #{cpufreqpath(cpu_idx) + "/scaling_available_frequencies"}`.strip.split.map(&:to_i).sort
36
+ else
37
+ []
38
+ end
39
+ end
40
+
41
+ class Config
42
+ attr_reader :cpuconfigs
43
+
44
+ def initialize(*args)
45
+ @cpuconfigs = \
46
+ if args.size == 0
47
+ self.current_config
48
+ elsif args.size == 1 && args.first.is_a?(Hash)
49
+ arg = args.first
50
+ if ! arg[:governor]
51
+ raise ArgumentError.new("governor must be specified.")
52
+ elsif arg[:governor] == "userspace" && ! arg[:frequency]
53
+ raise ArgumentError.new("parameter :frequency is required for userspece governor")
54
+ end
55
+
56
+ Array.new(Cpufreq.num_cpu()){|idx|
57
+ CpuConfig.new(idx, arg[:governor], arg)
58
+ }
59
+ elsif args.size == 1 && args.first.is_a?(Array) && args.first.every?{|arg| arg.is_a? CpuConfig}
60
+ args.first
61
+ elsif args.size == Cpufreq.num_cpu() && args.every?{|arg| arg.is_a? CpuConfig}
62
+ args
63
+ end
64
+ end
65
+
66
+ def self.get()
67
+ cpuconfigs = Array.new(Cpufreq.num_cpu){|cpu_idx|
68
+ CpuConfig.read_config(cpu_idx)
69
+ }
70
+ self.new(cpuconfigs)
71
+ end
72
+
73
+ def self.set(config)
74
+ config.cpuconfigs.each_with_index{|cpuconfig, idx|
75
+ CpuConfig.write_config(idx, cpuconfig)
76
+ }
77
+ end
78
+
79
+ def to_s
80
+ cpuconfig = @cpuconfigs.first
81
+
82
+ ret = cpuconfig.governor
83
+
84
+ suffix = case cpuconfig.governor
85
+ when /\Aperformance\Z/
86
+ nil
87
+ when /\Apowersave\Z/
88
+ nil
89
+ when /\Auserspace\Z/
90
+ cpuconfig.params[:frequency] / 1000000.0
91
+ when /\Aondemand\Z/
92
+ nil
93
+ end
94
+
95
+ if suffix
96
+ ret += ":freq=#{suffix}GHz"
97
+ end
98
+
99
+ ret
100
+ end
101
+
102
+ class CpuConfig
103
+ attr_accessor :governor
104
+ attr_accessor :params
105
+
106
+ # cpu_idx is just a hint for gathering information
107
+ def initialize(cpu_idx, gov, params = Hash.new)
108
+ @governor = gov.to_s
109
+ @freq = nil
110
+ @params = params
111
+
112
+ @cpu_idx = cpu_idx
113
+ @available_freqs = Cpufreq.available_frequency(cpu_idx)
114
+
115
+ case @governor
116
+ when /\Aperformance\Z/
117
+ # do nothing
118
+ when /\Apowersave\Z/
119
+ # do nothing
120
+ when /\Auserspace\Z/
121
+ if ! @freq = params[:frequency]
122
+ raise ArgumentError.new("parameter :frequency is required for userspece governor")
123
+ elsif ! @available_freqs.include?(params[:frequency])
124
+ raise ArgumentError.new("Frequency not available: #{params[:frequency]}")
125
+ end
126
+ when /\Aondemand\Z/
127
+ # TODO
128
+ end
129
+ end
130
+
131
+ def frequency
132
+ case @governor
133
+ when /\Aperformance\Z/
134
+ @available_freqs.max
135
+ when /\Apowersave\Z/
136
+ @available_freqs.min
137
+ when /\Auserspace\Z/
138
+ @freq
139
+ when /\Aondemand\Z/
140
+ `cat #{Cpufreq.cpufreqpath(@cpu_idx) + "/scaling_cur_freq"}`.strip.to_i
141
+ end
142
+ end
143
+
144
+ def frequency=(freq)
145
+ if @available_freqs.include?(freq)
146
+ @freq = freq
147
+ else
148
+ raise ArgumentError.new("Frequency not available: #{freq}")
149
+ end
150
+ end
151
+
152
+ def self.read_config(cpu_idx)
153
+ gov = `cat #{Cpufreq.cpufreqpath(cpu_idx) + "/scaling_governor"}`.strip
154
+ freq = nil
155
+
156
+ case gov
157
+ when /\Aperformance\Z/
158
+ # do nothing
159
+ when /\Aperformance\Z/
160
+ # do nothing
161
+ when /\Auserspace\Z/
162
+ freq = `cat #{Cpufreq.cpufreqpath(cpu_idx) + "/scaling_cur_freq"}`.strip.to_i
163
+ when /\Aondemand\Z/
164
+ # TODO: read parameters
165
+ end
166
+
167
+ CpuConfig.new(cpu_idx, gov, {:frequency => freq})
168
+ end
169
+
170
+ def self.write_config(cpu_idx, cpuconfig)
171
+ `echo #{cpuconfig.governor} > #{Cpufreq.cpufreqpath(cpu_idx) + "/scaling_governor"}`
172
+ case cpuconfig.governor
173
+ when /\Aperformance\Z/
174
+ # do nothing
175
+ when /\Aperformance\Z/
176
+ # do nothing
177
+ when /\Auserspace\Z/
178
+ `echo #{cpuconfig.frequency} > #{Cpufreq.cpufreqpath(cpu_idx) + "/scaling_setspeed"}`
179
+ when /\Aondemand\Z/
180
+ if cpuconfig.params[:up_threshold]
181
+ `echo #{cpuconfig.params[:up_threshold]} > #{Cpufreq.cpufreqpath(cpu_idx) + "/ondemand/up_threshold"}`
182
+ end
183
+ if cpuconfig.params[:sampling_rate]
184
+ `echo #{cpuconfig.params[:sampling_rate]} > #{Cpufreq.cpufreqpath(cpu_idx) + "/ondemand/sampling_rate"}`
185
+ end
186
+ # TODO: parameters
187
+ end
188
+ end
189
+
190
+ def to_s
191
+ case self.governor
192
+ when /\Auserspace\Z/
193
+ "#<CpuConfig: governor=#{self.governor}, frequency=#{self.frequency}>"
194
+ else
195
+ "#<CpuConfig: governor=#{self.governor}>"
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,9 @@
1
+
2
+ class ::Dir
3
+ def self.lock(dir_path)
4
+ File.open(dir_path, "r") do |f|
5
+ f.flock(File::LOCK_EX)
6
+ yield
7
+ end
8
+ end
9
+ end
@@ -1,38 +1,38 @@
1
1
 
2
2
  require 'fileutils'
3
+ require 'term/ansicolor'
3
4
 
4
- class Jkr
5
+ module Jkr
5
6
  class Env
7
+ attr_reader :env_dir
6
8
  attr_reader :jkr_dir
7
- attr_reader :working_dir
8
9
  attr_reader :jkr_result_dir
9
10
  attr_reader :jkr_plan_dir
10
11
  attr_reader :jkr_script_dir
11
-
12
+
12
13
  PLAN_DIR = "plan"
13
14
  RESULT_DIR = "result"
14
15
  SCRIPT_DIR = "script"
15
16
 
16
- def initialize(working_dir = Dir.pwd, jkr_dir = File.join(Dir.pwd, "jkr"))
17
- @jkr_dir = jkr_dir
18
- @working_dir = working_dir
17
+ def initialize(env_dir = Dir.pwd)
18
+ @env_dir = env_dir
19
+ @jkr_dir = File.join(@env_dir, "jkr")
19
20
  @jkr_plan_dir = File.join(@jkr_dir, PLAN_DIR)
20
21
  @jkr_result_dir = File.join(@jkr_dir, RESULT_DIR)
21
22
  @jkr_script_dir = File.join(@jkr_dir, SCRIPT_DIR)
22
-
23
- [@jkr_dir, @jkr_result_dir, @jkr_plan_dir, @jkr_script_dir].each do |dir_path|
23
+
24
+ unless Dir.exists?(@jkr_dir)
25
+ raise Errno::ENOENT.new(@jkr_dir)
26
+ end
27
+
28
+ [@jkr_dir,
29
+ @jkr_result_dir,
30
+ @jkr_plan_dir,
31
+ @jkr_script_dir].each do |dir_path|
24
32
  unless Dir.exists?(dir_path)
25
- FileUtils.mkdir_p(dir_path)
33
+ raise ArgumentError.new("Directory #{dir_path} not found")
26
34
  end
27
35
  end
28
36
  end
29
-
30
- def next_plan
31
- self.plans.first
32
- end
33
-
34
- def plans
35
- Dir.glob("#{@jkr_plan_dir}#{File::SEPARATOR}*.plan").sort
36
- end
37
37
  end
38
38
  end