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.
- 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
|