tcpsnitch_analyzer 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 50b9dac312c23a4ec6e10f497b675078c0d6e7cf
4
+ data.tar.gz: 86e2119b21841973b78cadca2965d89377500ee4
5
+ SHA512:
6
+ metadata.gz: b8446c031fe67e81e5cbd72ab6b0bd3dcd2bc827d023684bc2f3b59da73e6e7566d6d415938d417cd71fdf18319940ed0236a526988291134bb063aa811d5e8b
7
+ data.tar.gz: 58b7ef0d840080ad862399c4a55ec8b1b01d15ad3c33fe7ef94e0aeaf916618a299cc03d590f6234b2ed7244e7f9417fb5baa16b7311566d45b745e30bbac974
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'tcpsnitch_analyzer'
4
+
5
+ # Parse options
6
+ options = TcpsnitchAnalyzer::OptParser.parse(ARGV)
7
+
8
+ # Check files
9
+ files = ARGV
10
+
11
+ if files.empty? then
12
+ TcpsnitchAnalyzer::error("missing argument, must provide at least 1 file")
13
+ exit 1
14
+ end
15
+
16
+ if options.analysis_type == TcpsnitchAnalyzer::TimeSerieStat and
17
+ files.size > 1 then
18
+ TcpsnitchAnalyzer::error("invalid argument: time serie analysis has no "\
19
+ "meaning on multiple JSON traces")
20
+ exit 1
21
+ end
22
+
23
+ # Start processing
24
+ TcpsnitchAnalyzer.process_files(options, files)
@@ -0,0 +1,101 @@
1
+ require 'oj'
2
+
3
+ require 'tcpsnitch_analyzer/opt_parser'
4
+ require 'tcpsnitch_analyzer/descriptive_stat'
5
+ require 'tcpsnitch_analyzer/proportion_stat'
6
+ require 'tcpsnitch_analyzer/time_serie_stat'
7
+
8
+ module TcpsnitchAnalyzer
9
+ EXECUTABLE = 'tcpsnitch_analyzer'
10
+ VERSION = '0.0.0'
11
+
12
+ # Configure Oj
13
+ Oj.default_options = { symbol_keys: true }
14
+
15
+ class << self
16
+ def process_files(options, files)
17
+ # We DO NOT want to read the entire JSON files into memory.
18
+ # We DO NOT want to build a Ruby object for the entire JSON array.
19
+ #
20
+ # Instead, we want to the file line by line, where each line consists of
21
+ # a single event. We then instantiate each event individually and discard
22
+ # them as we consume the file, thus giving O(1) memory consumption.
23
+ matched_data = false
24
+
25
+ files.each do |file|
26
+ # IO.each should not read entire file in memory. To verify?
27
+ File.open(file).each_with_index do |line, index|
28
+ next if index == 0 # First line is opening '[' of JSON Array
29
+ next if line.eql? "]" # Last line is closing ']' of JSON Array
30
+
31
+ # Parse JSON object
32
+ begin
33
+ hash = Oj.load(line.chomp(",\n")) # Remove ',\n' after JSON object
34
+ rescue Exception => e
35
+ error(e)
36
+ end
37
+
38
+ # Skip if filter does not match
39
+ next unless filter(hash, options.event_filter)
40
+ matched_data = true
41
+
42
+ # Extract value
43
+ begin
44
+ val = node_val(hash, options.node_path)
45
+ rescue Exception => e
46
+ error("invalid -n argument: '#{options.node_path}'")
47
+ end
48
+
49
+ # Compute on value
50
+ if options.analysis_type == TimeSerieStat
51
+ options.analysis_type.add_point(node_val(hash, 'timestamp'), val)
52
+ else
53
+ options.analysis_type.add_val(val)
54
+ end
55
+ end
56
+ end
57
+
58
+ # Output results
59
+ puts_options_header(options)
60
+ if matched_data
61
+ options.analysis_type.print(options)
62
+ else
63
+ puts "No data point matching criterias."
64
+ end
65
+ end
66
+
67
+ def error(msg)
68
+ puts "#{EXECUTABLE}: #{msg}."
69
+ puts "Try '#{EXECUTABLE} -h' for more information."
70
+ exit 1
71
+ end
72
+
73
+ def filter(hash, filter)
74
+ if filter then
75
+ hash[:type] == filter ? hash : nil
76
+ else
77
+ hash
78
+ end
79
+ end
80
+
81
+ def val_for(hash, keys)
82
+ keys.reduce(hash) { |h, key| h[key] }
83
+ end
84
+
85
+ def keys_from_path(path)
86
+ path.split('.').collect(&:to_sym)
87
+ end
88
+
89
+ def node_val(hash, path)
90
+ val_for(hash, keys_from_path(path))
91
+ end
92
+
93
+ def puts_options_header(options)
94
+ puts "JSON node:".ljust(15) + "#{options.node_path}"
95
+ event_filter = options.event_filter ? options.event_filter : "/"
96
+ puts "Type filter:".ljust(15) + event_filter
97
+ puts ""
98
+ end
99
+
100
+ end # class << self
101
+ end # module
@@ -0,0 +1,53 @@
1
+ require 'descriptive_statistics'
2
+ require 'gnuplot'
3
+
4
+ module TcpsnitchAnalyzer
5
+ class DescriptiveStat
6
+ @@data = []
7
+
8
+ def self.add_val(val)
9
+ if val.is_a? Integer
10
+ @@data.push(val)
11
+ else
12
+ if val.is_a? Hash
13
+ TcpsnitchAnalyzer.error("invalid value for descriptive statistic: "\
14
+ "non-terminal node")
15
+ else
16
+ TcpsnitchAnalyzer.error("invalid value for descriptive statistic: '#{val}'")
17
+ end
18
+ end
19
+ end
20
+
21
+ def self.print(options)
22
+ puts "Descriptive statistics:"
23
+ @@data.descriptive_statistics.each do |key, value|
24
+ puts "#{key}".ljust(20) + "#{value}"
25
+ end
26
+
27
+ # Only plot CDF is we have a range
28
+ return unless @@data.range > 0
29
+
30
+ x = @@data.sort
31
+ n = x.size
32
+ y = x.map do |el|
33
+ ((x.rindex { |v| v <= el } || -1.0) + 1.0) / n * 100.0
34
+ end
35
+
36
+ Gnuplot.open do |gp|
37
+ Gnuplot::Plot.new(gp) do |plot|
38
+ plot.xrange "[#{@@data.min}:#{@@data.max}]; set logscale x"
39
+ plot.title "CDF for #{options.node_path} (#{options.event_filter} events)"
40
+ plot.xlabel "Value"
41
+ plot.ylabel "Normal CDF"
42
+ plot.data << Gnuplot::DataSet.new([x,y]) do |ds|
43
+ ds.with = "lines"
44
+ ds.linewidth = 4
45
+ ds.title = options.node_path.split('.').last
46
+ end
47
+ end
48
+ end # Gnuplot.open
49
+ end # def self.print
50
+
51
+ end # class
52
+ end # module
53
+
@@ -0,0 +1,65 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+
4
+ module TcpsnitchAnalyzer
5
+ class OptParser
6
+ def self.parse(args)
7
+ options = OpenStruct.new
8
+ options.analysis_type = ProportionStat
9
+ options.event_filter = nil
10
+ options.node_path = "type"
11
+
12
+ begin
13
+ OptionParser.new do |opts|
14
+ opts.banner = "Usage: #{EXECUTABLE} [-h] [options] file...\n"
15
+ opts.separator ""
16
+ opts.separator "Analyze tcpsnitch JSON traces."
17
+ opts.separator ""
18
+ opts.separator "Options:"
19
+
20
+ opts.on("-a", "--analysis [TYPE]", "TYPE of statistic analysis:
21
+ descriptive (d), proportion (p) or timeserie (t).") do |type|
22
+ TcpsnitchAnalyzer.error("missing -a argument") unless type
23
+
24
+ case type.downcase
25
+ when /^d.*/
26
+ options.analysis_type = DescriptiveStat
27
+ when /^p.*/
28
+ options.analysis_type = ProportionStat
29
+ when /^t.*/
30
+ options.analysis_type = TimeSerieStat
31
+ else
32
+ TcpsnitchAnalyzer.error("invalid -a argument: '#{type}'")
33
+ end
34
+ end
35
+
36
+ opts.on("-e", "--event [FILTER]", "filter on events of type FILTER") do |ev|
37
+ TcpsnitchAnalyzer.error("missing -e argument") unless ev
38
+ options.event_filter = ev
39
+ end
40
+
41
+ opts.on_tail("-h", "--help", "show this help text") do
42
+ puts opts
43
+ exit
44
+ end
45
+
46
+ opts.on("-n", "--node [PATH]", "compute on node at PATH") do |node|
47
+ TcpsnitchAnalyzer.error("missing -n argument") unless node
48
+ options.node_path = node
49
+ end
50
+
51
+ opts.on_tail("--version", "show version") do
52
+ puts VERSION
53
+ exit
54
+ end
55
+
56
+ end.parse!(args) # OptionParser
57
+ rescue OptionParser::ParseError => e
58
+ TcpsnitchAnalyzer.error(e)
59
+ end
60
+
61
+ options
62
+ end # def self.parse
63
+
64
+ end # class
65
+ end # module
@@ -0,0 +1,22 @@
1
+ module TcpsnitchAnalyzer
2
+ class ProportionStat
3
+ @@count = 0
4
+ @@hash = Hash.new(0)
5
+
6
+ def self.add_val(val)
7
+ @@count += 1
8
+ @@hash[val] += 1
9
+ end
10
+
11
+ def self.print(options)
12
+ puts "Proportion statistics:"
13
+
14
+ @@hash.sort_by { |val, count| -count }.each do |val, count|
15
+ pc = ((count.to_f/@@count) * 100).round(2)
16
+ puts "#{pc}%".ljust(7) + "(#{count})".ljust(10) + "#{val}"
17
+ end
18
+ puts "100%".ljust(7) + "(#{@@count})"
19
+ end
20
+
21
+ end # class
22
+ end # module
@@ -0,0 +1,32 @@
1
+ module TcpsnitchAnalyzer
2
+ class TimeSerieStat
3
+ @@x = []
4
+ @@y = []
5
+
6
+ def self.add_point(timestamp, y)
7
+ usec = timestamp[:sec] * 1000000 + timestamp[:usec]
8
+ @@min ||= usec
9
+ @@x.push(usec-@@min)
10
+ @@y.push(y)
11
+ end
12
+
13
+ def self.print(options)
14
+ puts "Time serie plot"
15
+
16
+ Gnuplot.open do |gp|
17
+ Gnuplot::Plot.new(gp) do |plot|
18
+ plot.title "Time serie: #{options.node_path}(t)"
19
+ plot.xlabel "Micro seconds"
20
+ plot.ylabel options.node_path.split('.').last.capitalize
21
+
22
+ plot.data << Gnuplot::DataSet.new([@@x,@@y]) do |ds|
23
+ ds.with = "lines"
24
+ ds.linewidth = 4
25
+ ds.notitle
26
+ end
27
+ end
28
+ end # Gnuplot.open
29
+ end # def self.print
30
+
31
+ end # class
32
+ end # module
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tcpsnitch_analyzer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Gregory Vander Schueren
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-01-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: oj
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.18'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.18'
27
+ - !ruby/object:Gem::Dependency
28
+ name: descriptive_statistics
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.5'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: gnuplot
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.6'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.6'
55
+ description: Tool to analyze tcpsnitch traces. Longer description to come.
56
+ email: gregory.vanderschueren@gmail.com
57
+ executables:
58
+ - tcpsnitch_analyzer
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - bin/tcpsnitch_analyzer
63
+ - lib/tcpsnitch_analyzer.rb
64
+ - lib/tcpsnitch_analyzer/descriptive_stat.rb
65
+ - lib/tcpsnitch_analyzer/opt_parser.rb
66
+ - lib/tcpsnitch_analyzer/proportion_stat.rb
67
+ - lib/tcpsnitch_analyzer/time_serie_stat.rb
68
+ homepage: http://rubygems.org/gems/tcpsnitch_analyzer
69
+ licenses:
70
+ - GPL-3.0
71
+ metadata: {}
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubyforge_project:
88
+ rubygems_version: 2.6.8
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: Tool to analyze tcpsnitch traces
92
+ test_files: []