jkr 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +81 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +674 -0
- data/{README.txt → README.rdoc} +0 -0
- data/Rakefile +4 -5
- data/bin/console +7 -0
- data/etc/example.plan +32 -0
- data/etc/zsh-comp.sh +82 -0
- data/exe/jkr +6 -0
- data/jkr.gemspec +31 -0
- data/lib/jkr.rb +13 -4
- data/lib/jkr/analysis.rb +5 -14
- data/lib/jkr/analytics.rb +75 -0
- data/lib/jkr/array.rb +47 -0
- data/lib/jkr/blktrace.rb +131 -0
- data/lib/jkr/cli.rb +110 -0
- data/lib/jkr/cpu_usage.rb +81 -0
- data/lib/jkr/cpufreq.rb +201 -0
- data/lib/jkr/dirlock.rb +9 -0
- data/lib/jkr/env.rb +17 -17
- data/lib/jkr/error.rb +5 -0
- data/lib/jkr/numeric.rb +28 -0
- data/lib/jkr/plan.rb +317 -26
- data/lib/jkr/planfinder.rb +40 -0
- data/lib/jkr/plot.rb +626 -0
- data/lib/jkr/stat.rb +2 -0
- data/lib/jkr/stat/kmeans-1d.rb +94 -0
- data/lib/jkr/su_cmd +163 -0
- data/lib/jkr/sysinfo.rb +34 -0
- data/lib/jkr/trial.rb +91 -16
- data/lib/jkr/userutils.rb +300 -22
- data/lib/jkr/utils.rb +38 -314
- data/lib/jkr/version.rb +3 -0
- data/sample-jkr.plan +52 -0
- metadata +171 -63
- data/bin/jkr +0 -224
- data/test/test_jkr.rb +0 -8
data/{README.txt → README.rdoc}
RENAMED
File without changes
|
data/Rakefile
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
# -*- ruby -*-
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
-
require '
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
require 'bundler/gem_tasks'
|
5
6
|
|
6
|
-
|
7
|
-
developer('Yuto HAYAMIZU', 'y.hayamizu@gmail.com')
|
7
|
+
RSpec::Core::RakeTask.new("spec")
|
8
8
|
|
9
|
-
|
10
|
-
end
|
9
|
+
task :default => [:spec]
|
11
10
|
|
12
11
|
# vim: syntax=ruby
|
data/bin/console
ADDED
data/etc/example.plan
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- mode: ruby -*-
|
2
|
+
|
3
|
+
title "example"
|
4
|
+
description "this is an example."
|
5
|
+
short_desc ""
|
6
|
+
|
7
|
+
def_parameters do
|
8
|
+
constant :foo => 123
|
9
|
+
variable :trial_no => [1,2,3]
|
10
|
+
end
|
11
|
+
|
12
|
+
def_prep do |plan|
|
13
|
+
end
|
14
|
+
|
15
|
+
# This is optional routine for presenting estimated execution time for users.
|
16
|
+
# Return estimated execution time in seconds.
|
17
|
+
exec_time_estimate do |plan|
|
18
|
+
# 30 seconds for each trial
|
19
|
+
30 * plan.vars[:trial_no].size
|
20
|
+
end
|
21
|
+
|
22
|
+
def_routine do |plan, params|
|
23
|
+
# store various information with metastore
|
24
|
+
# metastore data is saved in "metastore.msh" with Marshal.dump for each trial
|
25
|
+
plan.metastore[:foo] = "bar"
|
26
|
+
end
|
27
|
+
|
28
|
+
def_cleanup do |plan|
|
29
|
+
end
|
30
|
+
|
31
|
+
def_analysis do |plan|
|
32
|
+
end
|
data/etc/zsh-comp.sh
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#compdef jkr
|
2
|
+
|
3
|
+
# Subcommand completion function
|
4
|
+
__jkr-execute-cmd() {
|
5
|
+
local curcontext context state line
|
6
|
+
local env_dir
|
7
|
+
|
8
|
+
integer ret=1
|
9
|
+
_arguments \
|
10
|
+
-C -S \
|
11
|
+
'--debug[enable debug mode]' \
|
12
|
+
{-C,--directory}':Jkr directory:_directories' \
|
13
|
+
'(-): :->jkr_plans' \
|
14
|
+
&& return
|
15
|
+
|
16
|
+
case $state in
|
17
|
+
(jkr_plans)
|
18
|
+
if [[ -n ${opt_args[(I)-C|--directory]} ]]; then
|
19
|
+
env_dir=${opt_args[${opt_args[(I)-C|--directory]}]}
|
20
|
+
else
|
21
|
+
env_dir=""
|
22
|
+
fi
|
23
|
+
|
24
|
+
__jkr_plans $env_dir && ret=0
|
25
|
+
;;
|
26
|
+
esac
|
27
|
+
return $ret
|
28
|
+
}
|
29
|
+
|
30
|
+
__jkr-list-cmd() {
|
31
|
+
_arguments \
|
32
|
+
-C -S \
|
33
|
+
'--debug[enable debug mode]' \
|
34
|
+
{-C,--directory}':Jkr directory:_directories' \
|
35
|
+
&& return
|
36
|
+
}
|
37
|
+
|
38
|
+
__jkr_plans() {
|
39
|
+
local old_IFS="$IFS" # save IFS
|
40
|
+
IFS=$'\n'
|
41
|
+
if [[ -n $1 ]]; then
|
42
|
+
_values "Available plans" $(JKR_ZSHCOMP_HELPER=y jkr list --directory=$1)
|
43
|
+
else
|
44
|
+
_values "Available plans" $(JKR_ZSHCOMP_HELPER=y jkr list)
|
45
|
+
fi
|
46
|
+
IFS="$old_IFS" # restore IFS
|
47
|
+
}
|
48
|
+
|
49
|
+
# Main completion function
|
50
|
+
_jkr() {
|
51
|
+
local curcontext context state line
|
52
|
+
declare -A opt_args
|
53
|
+
integer ret=1
|
54
|
+
|
55
|
+
_arguments \
|
56
|
+
-C -S \
|
57
|
+
'(-): :->subcmds' \
|
58
|
+
'(-)*:: :->option-or-argument' \
|
59
|
+
&& return
|
60
|
+
|
61
|
+
case $state in
|
62
|
+
(subcmds)
|
63
|
+
__jkr_subcmds && ret=0
|
64
|
+
;;
|
65
|
+
(option-or-argument)
|
66
|
+
if (( $+functions[__jkr-$words[1]-cmd] )); then
|
67
|
+
_call_function ret __jkr-$words[1]-cmd
|
68
|
+
else
|
69
|
+
_message 'no completion'
|
70
|
+
fi
|
71
|
+
;;
|
72
|
+
esac
|
73
|
+
|
74
|
+
return $ret
|
75
|
+
}
|
76
|
+
|
77
|
+
__jkr_subcmds() {
|
78
|
+
_values 'Jkr command' \
|
79
|
+
'execute[Execute a plan]' \
|
80
|
+
'list[List jkr plans]' \
|
81
|
+
'analyze[Analyze a result]'
|
82
|
+
}
|
data/exe/jkr
ADDED
data/jkr.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'jkr/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "jkr"
|
8
|
+
spec.version = Jkr::VERSION
|
9
|
+
spec.authors = ["Yuto Hayamizu"]
|
10
|
+
spec.email = ["y.hayamizu@gmail.com"]
|
11
|
+
spec.licenses = ["GPL-3.0"]
|
12
|
+
|
13
|
+
spec.summary = %q{Script execution manager for experimental measurements.}
|
14
|
+
spec.description = %q{Jkr is a script execution manager for experimental measurements.}
|
15
|
+
spec.homepage = "https://github.com/as110-tkl/jkr"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency 'thor'
|
23
|
+
spec.add_dependency 'term-ansicolor'
|
24
|
+
|
25
|
+
spec.add_development_dependency 'pry'
|
26
|
+
spec.add_development_dependency 'bundler'
|
27
|
+
spec.add_development_dependency 'guard'
|
28
|
+
spec.add_development_dependency 'guard-rspec'
|
29
|
+
spec.add_development_dependency 'rspec'
|
30
|
+
spec.add_development_dependency "rake"
|
31
|
+
end
|
data/lib/jkr.rb
CHANGED
@@ -1,9 +1,18 @@
|
|
1
1
|
|
2
|
+
require 'jkr/array'
|
3
|
+
require 'jkr/numeric'
|
2
4
|
require 'jkr/env'
|
5
|
+
require 'jkr/error'
|
3
6
|
require 'jkr/plan'
|
7
|
+
require 'jkr/planfinder'
|
4
8
|
require 'jkr/trial'
|
5
9
|
require 'jkr/analysis'
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
+
require 'jkr/cpufreq'
|
11
|
+
require 'jkr/cpu_usage'
|
12
|
+
require 'jkr/sysinfo'
|
13
|
+
require 'jkr/analytics'
|
14
|
+
require 'jkr/plot'
|
15
|
+
require 'jkr/stat'
|
16
|
+
require 'jkr/userutils'
|
17
|
+
require 'jkr/dirlock'
|
18
|
+
require 'jkr/cli'
|
data/lib/jkr/analysis.rb
CHANGED
@@ -1,27 +1,18 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
module Jkr
|
3
3
|
class Analysis
|
4
4
|
def self.analyze(env, resultset_num)
|
5
|
-
resultset_num = sprintf "%
|
5
|
+
resultset_num = sprintf "%05d", resultset_num.to_i
|
6
6
|
resultset_dir = Dir.glob(File.join(env.jkr_result_dir, resultset_num)+"*")
|
7
7
|
if resultset_dir.size != 1
|
8
8
|
raise RuntimeError.new "cannot specify resultset dir (#{resultset_dir.join(" ")})"
|
9
9
|
end
|
10
10
|
resultset_dir = resultset_dir.first
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
raise RuntimeError.new "cannot find plan file"
|
15
|
-
elsif plan_files.size > 1
|
16
|
-
raise RuntimeError.new "there are two or more plan files"
|
17
|
-
end
|
18
|
-
plan_file_path = plan_files.first
|
19
|
-
|
20
|
-
plan = Jkr::Plan.new(env, plan_file_path)
|
12
|
+
plan = Plan.create_by_result_id(env, resultset_num)
|
13
|
+
plan.resultset_dir = File.dirname(plan.file_path)
|
21
14
|
|
22
|
-
|
23
|
-
plan.analysis.call(plan)
|
24
|
-
Jkr::AnalysisUtils.undef_analysis_utils(plan)
|
15
|
+
plan.do_analysis()
|
25
16
|
end
|
26
17
|
end
|
27
18
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
|
2
|
+
module Jkr
|
3
|
+
class Analytics
|
4
|
+
class << self
|
5
|
+
def normalize_param(key, value = nil)
|
6
|
+
if value.is_a? TrueClass
|
7
|
+
return key.to_s + "_true"
|
8
|
+
elsif value.is_a? FalseClass
|
9
|
+
return key.to_s + "_false"
|
10
|
+
end
|
11
|
+
|
12
|
+
value
|
13
|
+
end
|
14
|
+
|
15
|
+
def each_group(results, variable, opt = {})
|
16
|
+
# extract parameters given as variables
|
17
|
+
param_keys = results.first[:params].keys.select do |param_key|
|
18
|
+
values = results.map do |result|
|
19
|
+
normalize_param(param_key, result[:params][param_key])
|
20
|
+
end
|
21
|
+
values.all?{|val| ! val.nil?} && values.sort.uniq.size > 1
|
22
|
+
end
|
23
|
+
|
24
|
+
unless param_keys.include?(variable)
|
25
|
+
raise ArgumentError.new("Invalid variable: #{variable.inspect}")
|
26
|
+
end
|
27
|
+
[:start_time, :end_time].each do |obsolete_key|
|
28
|
+
if param_keys.include?(obsolete_key)
|
29
|
+
$stderr.puts("#{obsolete_key} should not be included in result[:params]")
|
30
|
+
param_keys.delete(obsolete_key)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
param_keys.delete(:trial)
|
34
|
+
param_keys.delete(variable)
|
35
|
+
if opt[:except]
|
36
|
+
opt[:except].each do |key|
|
37
|
+
param_keys.delete(key)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
results.group_by do |result|
|
42
|
+
param_keys.map{|key| normalize_param(key, result[:params][key])}
|
43
|
+
end.sort_by do |group_param, group|
|
44
|
+
group_param
|
45
|
+
end.each do |group_param, group|
|
46
|
+
group = group.sort_by{|result| result[:params][variable]}
|
47
|
+
yield(group_param, group)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def hton(str)
|
52
|
+
if str.is_a? Numeric
|
53
|
+
return str
|
54
|
+
end
|
55
|
+
|
56
|
+
units = {
|
57
|
+
'k' => 1024, 'K' => 1024,
|
58
|
+
'm' => 1024*1024, 'M' => 1024*1024,
|
59
|
+
'g' => 1024*1024*1024, 'G' => 1024*1024*1024,
|
60
|
+
}
|
61
|
+
|
62
|
+
if str =~ /(\d+)([kKmMgG]?)/
|
63
|
+
num = $1.to_i
|
64
|
+
unit = $2
|
65
|
+
if unit.size > 0
|
66
|
+
num *= units[unit]
|
67
|
+
end
|
68
|
+
else
|
69
|
+
raise ArgumentError.new("#{str} is not a valid number expression")
|
70
|
+
end
|
71
|
+
num
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/jkr/array.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
class Array
|
3
|
+
def sum()
|
4
|
+
self.inject(&:+)
|
5
|
+
end
|
6
|
+
|
7
|
+
def avg()
|
8
|
+
if self.empty?
|
9
|
+
nil
|
10
|
+
else
|
11
|
+
self.sum.to_f / self.size
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def stdev()
|
16
|
+
avg = self.avg
|
17
|
+
var = self.map{|val| (val - avg) ** 2}.sum
|
18
|
+
if self.size > 1
|
19
|
+
var /= self.size - 1
|
20
|
+
end
|
21
|
+
Math.sqrt(var)
|
22
|
+
end
|
23
|
+
|
24
|
+
def sterr()
|
25
|
+
self.stdev / Math.sqrt(self.size)
|
26
|
+
end
|
27
|
+
|
28
|
+
def every?(&block)
|
29
|
+
ret = true
|
30
|
+
self.each do |elem|
|
31
|
+
unless block.call(elem)
|
32
|
+
ret = false
|
33
|
+
break
|
34
|
+
end
|
35
|
+
end
|
36
|
+
ret
|
37
|
+
end
|
38
|
+
|
39
|
+
def group_by()
|
40
|
+
ret = Hash.new{ Array.new }
|
41
|
+
self.each do |elem|
|
42
|
+
ret[yield(elem)] += [elem]
|
43
|
+
end
|
44
|
+
|
45
|
+
ret
|
46
|
+
end
|
47
|
+
end
|
data/lib/jkr/blktrace.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
|
2
|
+
module Jkr
|
3
|
+
class Blktrace
|
4
|
+
class << self
|
5
|
+
def open(input_basename)
|
6
|
+
self.new(input_basename)
|
7
|
+
end
|
8
|
+
|
9
|
+
def each(input_basename, &block)
|
10
|
+
self.new(input_basename).each(&block)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(input_basename)
|
15
|
+
input_basename = Pathname.new(input_basename).relative_path_from(Pathname.new(`pwd`.strip)).to_s
|
16
|
+
if Dir.glob(input_basename + ".blktrace.*").size > 0
|
17
|
+
@input_basename = input_basename
|
18
|
+
puts "multi file: #{input_basename}"
|
19
|
+
else
|
20
|
+
if File.exists?(input_basename)
|
21
|
+
@input_singlefile = input_basename
|
22
|
+
puts "single file: #{input_basename}"
|
23
|
+
else
|
24
|
+
raise ArgumentError.new("No such blktrace data: #{input_basename}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def raw_each(option = {}, &block)
|
30
|
+
if option[:limit]
|
31
|
+
limit = "| head -n #{option[:limit]}"
|
32
|
+
else
|
33
|
+
limit = ""
|
34
|
+
end
|
35
|
+
if @input_basename
|
36
|
+
cmd = "blkparse -f \"bt\\t%c\\t%s\\t%T.%t\\t%a\\t%d\\t%S\\t%n\\n\" -i #{@input_basename} #{limit} | grep '^bt'|sort -k4,4"
|
37
|
+
elsif @input_singlefile
|
38
|
+
cmd = "cat #{@input_singlefile}|blkparse -f \"bt\\t%c\\t%s\\t%T.%t\\t%a\\t%d\\t%S\\t%n\\n\" -i - #{limit} | grep '^bt'|sort -k4,4"
|
39
|
+
end
|
40
|
+
IO.popen(cmd, "r") do |io|
|
41
|
+
while line = io.gets
|
42
|
+
_, cpu, seqno, time, action, rwbs, pos_sec, sz_sec = line.split("\t")
|
43
|
+
cpu = cpu.to_i
|
44
|
+
seqno = seqno.to_i
|
45
|
+
time = time.to_f
|
46
|
+
pos_sec = pos_sec.to_i
|
47
|
+
sz_sec = sz_sec.to_i
|
48
|
+
record = [cpu, seqno, time, action, rwbs, pos_sec, sz_sec]
|
49
|
+
|
50
|
+
block.call(record)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def each(option = {}, &block)
|
56
|
+
self.map(option, &block)
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
def map(option = {}, &block)
|
61
|
+
if ! option.include?(:cache)
|
62
|
+
option[:cache] = true
|
63
|
+
end
|
64
|
+
if option[:limit]
|
65
|
+
limit = "| head -n #{option[:limit]}"
|
66
|
+
else
|
67
|
+
limit = ""
|
68
|
+
end
|
69
|
+
|
70
|
+
if @input_basename
|
71
|
+
cache_file_path = @input_basename + ".cache"
|
72
|
+
else
|
73
|
+
cache_file_path = @input_singlefile + ".cache"
|
74
|
+
end
|
75
|
+
|
76
|
+
if option[:cache] && File.exists?(cache_file_path)
|
77
|
+
records = Marshal.load(File.open(cache_file_path))
|
78
|
+
else
|
79
|
+
records = []
|
80
|
+
issues = []
|
81
|
+
if @input_basename
|
82
|
+
cmd = "blkparse -f \"bt\\t%c\\t%s\\t%T.%t\\t%a\\t%d\\t%S\\t%n\\n\" -i #{@input_basename} #{limit} | grep '^bt'|sort -k4,4"
|
83
|
+
elsif
|
84
|
+
cmd = "cat #{@input_singlefile}|blkparse -f \"bt\\t%c\\t%s\\t%T.%t\\t%a\\t%d\\t%S\\t%n\\n\" -i - #{limit} | grep '^bt'|sort -k4,4"
|
85
|
+
end
|
86
|
+
IO.popen(cmd, "r") do |io|
|
87
|
+
while line = io.gets
|
88
|
+
_, cpu, seqno, time, action, rwbs, pos_sec, sz_sec = line.split("\t")
|
89
|
+
cpu = cpu.to_i
|
90
|
+
seqno = seqno.to_i
|
91
|
+
time = time.to_f
|
92
|
+
pos_sec = pos_sec.to_i
|
93
|
+
sz_sec = sz_sec.to_i
|
94
|
+
|
95
|
+
record = [cpu, seqno, time, action, rwbs, pos_sec, sz_sec]
|
96
|
+
if action == "D"
|
97
|
+
issues.push(record)
|
98
|
+
elsif action == "C"
|
99
|
+
# check pos and sz
|
100
|
+
del_idx = nil
|
101
|
+
issues.each_with_index do |rec, idx|
|
102
|
+
if pos_sec == rec[5] && sz_sec == rec[6]
|
103
|
+
del_idx = idx
|
104
|
+
rt = time - rec[2]
|
105
|
+
record.push(rt) # append response time
|
106
|
+
records.push(record)
|
107
|
+
break
|
108
|
+
end
|
109
|
+
end
|
110
|
+
if del_idx.nil?
|
111
|
+
puts("Unmatched complete record: #{record.inspect}")
|
112
|
+
# raise StandardError.new("Unmatched complete record: #{record.inspect}")
|
113
|
+
next
|
114
|
+
end
|
115
|
+
issues.delete_at(del_idx)
|
116
|
+
else
|
117
|
+
raise NotImplementedError.new("Action #{action} handler is not implemented.")
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
File.open(cache_file_path, "w") do |file|
|
122
|
+
Marshal.dump(records, file)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
records.map do |*record|
|
127
|
+
block.call(*record)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|