jkr 0.0.1 → 0.1.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.
@@ -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