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 +7 -0
- data/.gitignore +28 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/README.md +43 -0
- data/Rakefile +1 -0
- data/bin/strace-stat +7 -0
- data/lib/strace_log.rb +266 -0
- data/strace_log.gemspec +23 -0
- metadata +81 -0
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
data/Gemfile
ADDED
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
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
|
data/strace_log.gemspec
ADDED
@@ -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:
|