strace_log 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 591335f397f9c5892b35533fef68ae0ee892ee44
4
+ data.tar.gz: 4a7aee2c5f1277020cac75899c13dc33e5ed087c
5
+ SHA512:
6
+ metadata.gz: 70cb159cdc830926381dad06fa186c03fa50640e18dd492b1d60447715d493561029e872f2b1dbed62fb3469f20f6ffdc72c7726a61af2a9daa31f029993bd44
7
+ data.tar.gz: 2296435aa1b62347d13fbad77d71d0071aebd28f23d026025194f6cbea2519fbccd7d3eae6da29c0f6262491e88cc902bf025ae8a3c78a173d57b10ab4316f97
data/.gitignore ADDED
@@ -0,0 +1,28 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .config
19
+ *~
20
+ */*~
21
+ */*/*~
22
+ *.bak
23
+ */*.bak
24
+ */*/*.bak
25
+ .#*
26
+ */.#*
27
+ */*/.#*
28
+ rhosts
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in strace_log.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # StraceLog
2
+
3
+ Parse logs generated by Strace (system call tracer) and obtain statistics.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'strace_log'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install strace_log
20
+
21
+ ## Usage
22
+
23
+ Run command with strace:
24
+
25
+ $ strace -ttt -T -o strace.log command
26
+
27
+ Print statistics of strace log:
28
+
29
+ $ strace-stat strace.log
30
+
31
+ ## Classes
32
+
33
+ * StraceLog::ParsedCall
34
+ * StraceLog::IOCounter
35
+ * StraceLog::Stat
36
+
37
+ ## Contributing
38
+
39
+ 1. Fork it ( https://github.com/[my-github-username]/strace_log/fork )
40
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
41
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
42
+ 4. Push to the branch (`git push origin my-new-feature`)
43
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/strace-stat ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "strace_log"
4
+
5
+ stat = StraceLog::Stat.new
6
+ stat.parse(ARGF)
7
+ stat.print
data/lib/strace_log.rb ADDED
@@ -0,0 +1,266 @@
1
+ require 'strscan'
2
+
3
+ module StraceLog
4
+
5
+ VERSION = "0.1.0"
6
+
7
+ class ParsedCall
8
+ ESCAPES = [ /x[\da-f][\da-f]/i, /n/, /t/, /r/, /\\/, /"/, /\d+/]
9
+
10
+ def initialize(line)
11
+ if /^---.*---$/ =~ line
12
+ @mesg = line
13
+ else
14
+ s = StringScanner.new(line)
15
+ s.scan(/^(?:(\d\d:\d\d:\d\d|\d+)(?:\.(\d+))? )?([\w\d]+)\(/)
16
+ @time = s[1]
17
+ @usec = s[2]
18
+ @func = s[3]
19
+ @args = scan_items(s,/\s*\)\s*/)
20
+ s.scan(/\s*= ([^=<>\s]+)(?:\s+([^<>]+))?(?: <([\d.]+)>)?$/)
21
+ @ret = s[1]
22
+ @mesg = s[2]
23
+ @elap = s[3]
24
+ end
25
+ end
26
+ attr_reader :time, :usec, :func, :args, :ret , :mesg, :elap
27
+
28
+ def scan_items(s,close)
29
+ args = []
30
+ i = 0
31
+ while !s.scan(close)
32
+ x = scan_string(s) || scan_bracket(s) ||
33
+ scan_brace(s) || scan_method(s) || scan_other(s)
34
+ if x.nil?
35
+ raise "match error: args=#{args.inspect} post_match=#{s.post_match}"
36
+ end
37
+ (args[i] ||= "") << x
38
+ if s.scan(/\s*,\s*/)
39
+ i += 1
40
+ end
41
+ end
42
+ args
43
+ end
44
+
45
+ def scan_string(s)
46
+ return nil if s.scan(/\s*"/).nil?
47
+ arg = ""
48
+ while !s.scan(/"/)
49
+ if s.scan(/\\/)
50
+ ESCAPES.each do |re|
51
+ if x = s.scan(re)
52
+ arg << eval('"\\'+x+'"')
53
+ break
54
+ end
55
+ end
56
+ elsif x = s.scan(/[^\\"]+/)
57
+ arg << x
58
+ end
59
+ end
60
+ if x = s.scan(/\.+/)
61
+ arg << x
62
+ end
63
+ arg
64
+ end
65
+
66
+ def scan_bracket(s)
67
+ s.scan(/\s*\[\s*/) && '['+scan_items(s,/\s*\]\s*/).join(',')+']'
68
+ end
69
+
70
+ def scan_brace(s)
71
+ s.scan(/\s*{\s*/) && '{'+scan_items(s,/\s*}\s*/).join(',')+'}'
72
+ end
73
+
74
+ def scan_method(s)
75
+ if s.scan(/([^"\\,{}()\[\]]+)\(/)
76
+ meth = s[1]
77
+ meth+'('+scan_items(s,/\s*\)\s*/).join(',')+')'
78
+ end
79
+ end
80
+
81
+ def scan_other(s)
82
+ s.scan(/[^"\\,{}()\[\]]+/)
83
+ end
84
+ end
85
+
86
+
87
+ class IOCounter
88
+ def initialize(path)
89
+ @path = path
90
+ @ok = {}
91
+ @fail = {}
92
+ @size = {}
93
+ @time = {}
94
+ @rename = []
95
+ end
96
+ attr_reader :path, :ok, :fail, :size, :time, :rename
97
+
98
+ def add(h,func,c)
99
+ h[func] = (h[func] || 0) + c
100
+ end
101
+
102
+ def count(fc)
103
+ if fc.ret == "-1"
104
+ add(@fail, fc.func, 1)
105
+ else
106
+ add(@ok, fc.func, 1)
107
+ end
108
+ add(@time, fc.func, fc.elap.to_f) if fc.elap
109
+ end
110
+
111
+ def count_size(fc)
112
+ sz = fc.ret.to_i
113
+ if sz >= 0
114
+ add(@size, fc.func, sz)
115
+ end
116
+ count(fc)
117
+ end
118
+
119
+ def rename_as(newpath)
120
+ @rename << @path
121
+ @path = newpath
122
+ end
123
+
124
+ def print
125
+ Kernel.print @path+":\n"
126
+ if !@ok.empty?
127
+ keys = @ok.keys.sort
128
+ Kernel.print " ok={"+keys.map{|k| "#{k}:#{@ok[k]}"}.join(", ")+"}\n"
129
+ end
130
+ if !@fail.empty?
131
+ keys = @fail.keys.sort
132
+ Kernel.print " fail={"+keys.map{|k| "#{k}:#{@fail[k]}"}.join(", ")+"}\n"
133
+ end
134
+ if !@size.empty?
135
+ keys = @size.keys.sort
136
+ Kernel.print " size={"+keys.map{|k| "#{k}:#{@size[k]}"}.join(", ")+"}\n"
137
+ end
138
+ if !@time.empty?
139
+ keys = @time.keys.sort
140
+ Kernel.print " time={"+keys.map{|k| "#{k}:#{@time[k]}"}.join(", ")+"}\n"
141
+ end
142
+ if !@rename.empty?
143
+ Kernel.print " rename={#{@rename.join(', ')}}\n"
144
+ end
145
+ puts
146
+ end
147
+ end
148
+
149
+
150
+ class Stat
151
+
152
+ def initialize
153
+ @stat = {}
154
+ @count = {}
155
+ @spent = {}
156
+ end
157
+
158
+ attr_reader :stat, :count, :spent
159
+
160
+ def parse(a)
161
+ @fd2path = ["stdin", "stdout", "stderr"]
162
+ a.each do |line|
163
+ stat_call( ParsedCall.new(line) )
164
+ end
165
+ end
166
+
167
+ def stat_call(pc)
168
+ m = pc.func
169
+ return if m.nil?
170
+ @count[m] = (@count[m] || 0) + 1
171
+ @spent[m] = (@spent[m] || 0) + pc.elap.to_f if pc.elap
172
+
173
+ case m
174
+
175
+ when /^(open|execve|l?stat|(read|un)?link|getc?wd|access|mkdir|mknod|chmod|chown)$/
176
+ path = pc.args[0]
177
+ count_path(path,pc)
178
+ if m=="open" && pc.ret != "-1"
179
+ fd = pc.ret.to_i
180
+ @fd2path[fd] = path
181
+ end
182
+
183
+ when /^(readv?|writev?)$/
184
+ path = @fd2path[pc.args[0].to_i]
185
+ count_size(path,pc)
186
+
187
+ when /^(fstat|fchmod|[fl]chown|lseek|ioctl|fcntl|getdents|sendto|recvmsg|close)$/
188
+ fd = pc.args[0].to_i
189
+ count_fd(fd,pc)
190
+ if m=="close" && pc.ret != "-1"
191
+ @fd2path[fd] = nil
192
+ end
193
+
194
+ when /^rename$/
195
+ path = pc.args[0]
196
+ count_path(path,pc)
197
+ rename(pc)
198
+
199
+ when /^dup[23]?$/
200
+ fd = pc.args[0].to_i
201
+ count_fd(fd,pc)
202
+ if pc.ret != "-1"
203
+ fd = pc.ret.to_i
204
+ @fd2path[fd] = path
205
+ end
206
+
207
+ when /^mmap$/
208
+ fd = pc.args[4].to_i
209
+ if fd >= 0
210
+ count_fd(fd,pc)
211
+ end
212
+
213
+ when /^connect$/
214
+ fd = pc.args[0].to_i
215
+ @fd2path[fd] = path = pc.args[1]
216
+ count_path(path,pc)
217
+
218
+ end
219
+ end
220
+
221
+ def count_fd(fd,pc)
222
+ path = @fd2path[fd]
223
+ count_path(path,pc)
224
+ end
225
+
226
+ def count_path(path,pc)
227
+ io_counter(path).count(pc)
228
+ end
229
+
230
+ def count_size(path,pc)
231
+ io_counter(path).count_size(pc)
232
+ end
233
+
234
+ def io_counter(path)
235
+ @stat[path] ||= IOCounter.new(path)
236
+ end
237
+
238
+ def rename(pc)
239
+ if pc.ret != "-1"
240
+ oldpath = pc.args[0]
241
+ newpath = pc.args[1]
242
+ ioc = @stat[newpath] = @stat[oldpath]
243
+ ioc.rename_as(newpath)
244
+ @stat.delete(oldpath)
245
+ end
246
+ end
247
+
248
+ def print
249
+ Kernel.print "count={\n"
250
+ @count.each do |m,c|
251
+ Kernel.print " #{m}: #{c},\n"
252
+ end
253
+ Kernel.print "}\n\n"
254
+ Kernel.print "time={\n"
255
+ @spent.each do |m,t|
256
+ Kernel.print " #{m}: #{t},\n"
257
+ end
258
+ Kernel.print "}\n\n"
259
+ files = @stat.keys.select{|x| x.class==String}.sort
260
+ files.each do |fn|
261
+ @stat[fn].print
262
+ end
263
+ end
264
+
265
+ end
266
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'strace_log'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "strace_log"
8
+ spec.version = StraceLog::VERSION
9
+ spec.authors = ["Masahiro TANAKA"]
10
+ spec.email = ["masa16.tanaka@gmail.com"]
11
+
12
+ spec.summary = %q{Parse strace's log}
13
+ spec.description = %q{Parse strace's log, summary of system call}
14
+ spec.homepage = ""
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "bin"
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.9"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: strace_log
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Masahiro TANAKA
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: Parse strace's log, summary of system call
42
+ email:
43
+ - masa16.tanaka@gmail.com
44
+ executables:
45
+ - strace-stat
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - ".travis.yml"
51
+ - Gemfile
52
+ - README.md
53
+ - Rakefile
54
+ - bin/strace-stat
55
+ - lib/strace_log.rb
56
+ - strace_log.gemspec
57
+ homepage: ''
58
+ licenses: []
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 2.4.5
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: Parse strace's log
80
+ test_files: []
81
+ has_rdoc: