strace_log 0.1.3 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +59 -88
  3. data/bin/strace-stat +14 -2
  4. data/lib/strace_log.rb +132 -126
  5. metadata +3 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 60912730fd9e97f6be5618218c74dbe050c5e01f
4
- data.tar.gz: b3e0abf9c2c8b62ef9b223fce27cd02f3fe32e6f
2
+ SHA256:
3
+ metadata.gz: bf0ec0832d7b894a0b0bef334b4e7614313db881e9f2b1fc0e867943f2974d5a
4
+ data.tar.gz: 3f95e1df8f7d617df9783fbd9bd23de39ef4bbcd972c51de082bf81aae3390b8
5
5
  SHA512:
6
- metadata.gz: 950e8007b438cf96b95ab82c545e73df6ea695b1091fa044c988dbc096f98fe26d187f9916fc4df6bdc41571441813c517a173ea126bee20afe535949851ee31
7
- data.tar.gz: 535ef872af158736e7431b964b7a9a932b7bb0e6e7237a56df080211c1d5a53424abd0012e35c0a9c1fda5b67722ff4754d88042b12a040bd7b0cacff66a6b2d
6
+ metadata.gz: 4b01896fcb02a1ff979a5a4486450c81a935fd24759bfae2c47fc1d4e0509be344d7882805c88b7950c224c3ddf438aed7cb7fc6fb02c4ca29716f4a3098729b
7
+ data.tar.gz: 03272c4affd1a38c0a58ed141c6b0d88e4dc67139e0c4b6be162f800e2298b25c41b71553b02301bcb54dbc763f769c804ddaa0bbc372abdb13217248de818a5
data/README.md CHANGED
@@ -25,104 +25,75 @@ Or install it yourself as:
25
25
 
26
26
  Run command with strace:
27
27
 
28
- $ strace -ttt -T -o strace.log command
28
+ $ strace -T -o strace.log command
29
29
 
30
- Print statistics of strace log:
30
+ Output statistics of strace log by mount point to CSV file:
31
31
 
32
- $ strace-stat strace.log
32
+ $ strace-stat -s -o strace.csv strace.log
33
+
34
+ Option for `strace-stat`:
35
+
36
+ Usage: strace-stat [options] [FILE]
37
+ FILE filename of strace log (default: stdin)
38
+ -o, --output OUTFILE output CSV filename
39
+ -s, --stat output statistics by mount point (default: statistics by each path)
40
+ -t, --table TABLEFILE filename of mounted file system table (default:/etc/mtab)
41
+ -c, --column TABLECOLUMN column number of mount point in mtab (default:2)
33
42
 
34
43
  ## Classes
35
44
 
36
45
  * StraceLog::ParsedCall
37
- * StraceLog::IOCounter
38
46
  * StraceLog::Stat
39
47
 
40
48
  ## Example
41
49
 
42
- $ strace -ttt -T -o strace.log dd if=/dev/zero of=tmpfile count=10000
43
-
44
- $ strace-stat strace.log
45
- count={
46
- execve: 1,
47
- brk: 3,
48
- mmap: 16,
49
- access: 1,
50
- open: 42,
51
- stat: 19,
52
- read: 10005,
53
- fstat: 6,
54
- mprotect: 7,
55
- close: 11,
56
- arch_prctl: 1,
57
- set_tid_address: 1,
58
- set_robust_list: 1,
59
- futex: 2,
60
- rt_sigaction: 6,
61
- rt_sigprocmask: 1,
62
- getrlimit: 1,
63
- dup2: 2,
64
- lseek: 1,
65
- clock_gettime: 2,
66
- write: 10003,
67
- munmap: 1,
68
- exit_group: 1,
69
- }
70
-
71
- time={
72
- execve: 0.000479,
73
- brk: 0.000376,
74
- mmap: 0.001751,
75
- access: 0.000157,
76
- open: 0.004224,
77
- stat: 0.001602,
78
- read: 1.176440,
79
- fstat: 0.000697,
80
- mprotect: 0.000647,
81
- close: 0.006732,
82
- arch_prctl: 0.000101,
83
- set_tid_address: 0.000127,
84
- set_robust_list: 0.000111,
85
- futex: 0.000232,
86
- rt_sigaction: 0.000718,
87
- rt_sigprocmask: 0.000116,
88
- getrlimit: 0.000091,
89
- dup2: 0.000213,
90
- lseek: 0.000112,
91
- clock_gettime: 0.000343,
92
- write: 1.229899,
93
- munmap: 0.000110,
94
- }
95
-
96
- /bin/dd:
97
- ok={execve:1}
98
- time={execve:0.000479}
99
-
100
- /dev/zero:
101
- ok={close:2, dup2:1, lseek:1, open:1, read:10000}
102
- size={read:5120000}
103
- time={close:0.000224, dup2:0.000112, lseek:0.000112, open:0.000119, read:1.176062}
104
-
105
- /etc/ld.so.preload:
106
- fail={access:1}
107
- time={access:0.000157}
108
-
109
- ...
110
-
111
- /usr/share/locale/locale.alias:
112
- ok={close:1, fstat:1, open:1, read:2}
113
- size={read:2512}
114
- time={close:0.000202, fstat:0.000216, open:0.000124, read:0.000221}
115
-
116
- stderr:
117
- ok={close:1, write:3}
118
- size={write:90}
119
- time={close:0.000022, write:0.000243}
120
-
121
- tmpfile:
122
- ok={close:2, dup2:1, open:1, write:10000}
123
- size={write:5120000}
124
- time={close:0.005866, dup2:0.000101, open:0.000154, write:1.229656}
125
-
50
+ $ strace -T -o strace.log dd if=/dev/zero of=tmpfile count=10000
51
+ $ strace-stat -s -o strace.csv strace.log
52
+ $ cat strace.csv
53
+ path,syscall,calls,errors,time,size
54
+ *,execve,1,0,0.009673,
55
+ *,brk,4,0,0.000057,
56
+ *,mmap,10,0,0.000285,
57
+ *,access,1,1,0.000021,
58
+ *,open,39,32,0.001220,
59
+ *,stat,27,21,0.000470,
60
+ *,read,10003,0,0.123271,5123334
61
+ *,fstat,5,0,0.000147,
62
+ *,mprotect,4,0,0.000075,
63
+ *,close,10,0,0.000311,
64
+ *,arch_prctl,1,0,0.000012,
65
+ *,rt_sigaction,4,0,0.000050,
66
+ *,dup2,2,0,0.000037,
67
+ *,lseek,1,0,0.000006,
68
+ *,write,10003,0,0.246277,5120136
69
+ *,munmap,1,0,0.000049,
70
+ *,exit_group,1,0,0.000000,
71
+ /,execve,1,0,0.009673,
72
+ /,access,1,1,0.000021,
73
+ /,open,29,24,0.000885,
74
+ /,stat,19,14,0.000346,
75
+ /,read,3,0,0.000101,3334
76
+ /,fstat,5,0,0.000147,
77
+ /,mmap,5,0,0.000181,
78
+ /,close,5,0,0.000167,
79
+ /home,open,9,8,0.000251,
80
+ /home,stat,8,7,0.000124,
81
+ /home,dup2,1,0,0.000030,
82
+ /home,close,2,0,0.000054,
83
+ /home,write,10000,0,0.246116,5120000
84
+ /dev,open,1,0,0.000084,
85
+ /dev,dup2,1,0,0.000007,
86
+ /dev,close,2,0,0.000049,
87
+ /dev,lseek,1,0,0.000006,
88
+ /dev,read,10000,0,0.123170,5120000
89
+ stderr,write,3,0,0.000161,136
90
+ stderr,close,1,0,0.000041,
91
+
92
+ (path=* means total sum for each system call)
93
+
94
+ Same example using pipe:
95
+
96
+ $ strace -T -s 0 -o '|strace-stat -s -o strace.csv' dd if=/dev/zero of=tmpfile count=10000
126
97
 
127
98
  ## Contributing
128
99
 
@@ -1,7 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "strace_log"
4
+ require 'optparse'
4
5
 
5
- stat = StraceLog::Stat.new
6
+ params = {}
7
+ outfile = nil
8
+
9
+ opt = OptionParser.new
10
+ opt.on('-o OUTFILE','--output','output CSV filename') {|v| outfile = v}
11
+ opt.on('-s','--stat','output statistics by mount point (default: statistics by each path)') {|v| params[:sum] = true}
12
+ opt.on('-t TABLEFILE','--table','filename of mounted file system table (default:/etc/mtab)') {|v| params[:table] = v}
13
+ opt.on('-c TABLECOLUMN','--column','column number of mount point in mtab (default:2)') {|v| params[:column] = v.to_i}
14
+ opt.banner += ' [STRACE_LOG_FILE]'
15
+ opt.parse!(ARGV)
16
+
17
+ stat = StraceLog::Stat.new(**params)
6
18
  stat.parse(ARGF)
7
- stat.print
19
+ stat.write(outfile)
@@ -1,13 +1,16 @@
1
1
  require 'strscan'
2
+ require 'pathname'
3
+ require 'csv'
2
4
 
3
5
  module StraceLog
4
6
 
5
- VERSION = "0.1.3"
7
+ VERSION = "0.2.1"
6
8
 
7
9
  class ParsedCall
8
10
  ESCAPES = [ /x[\da-f][\da-f]/i, /n/, /t/, /r/, /\\/, /"/, /\d+/]
9
11
 
10
12
  def initialize(line)
13
+ @size = nil
11
14
  if /^(?:(\d\d:\d\d:\d\d|\d+)(?:\.(\d+))? )?[+-]{3}.*[+-]{3}$/ =~ line
12
15
  @mesg = line
13
16
  else
@@ -17,13 +20,13 @@ module StraceLog
17
20
  @usec = s[2]
18
21
  @func = s[3]
19
22
  @args = scan_items(s,/\s*\)\s*/)
20
- s.scan(/\s*= ([^=<>\s]+)(?:\s+([^<>]+))?(?: <([\d.]+)>)?$/)
23
+ s.scan(/\s*= ([^=<>\s]+(?:<[^<>]*>)?)(?:\s+([^<>]+))?(?: <([\d.]+)>)?$/)
21
24
  @ret = s[1]
22
25
  @mesg = s[2]
23
26
  @elap = s[3]
24
27
  end
25
28
  end
26
- attr_reader :time, :usec, :func, :args, :ret , :mesg, :elap
29
+ attr_reader :time, :usec, :func, :args, :ret , :mesg, :elap, :size
27
30
 
28
31
  def scan_items(s,close)
29
32
  args = []
@@ -81,81 +84,77 @@ module StraceLog
81
84
  def scan_other(s)
82
85
  s.scan(/[^"\\,{}()\[\]]+/)
83
86
  end
84
- end
85
-
86
87
 
87
- class IOCounter
88
- def initialize(path)
89
- @path = path
90
- @ok = {}
91
- @fail = {}
92
- @size = {}
93
- @time = {}
94
- @rename = []
88
+ def set_size
89
+ sz = @ret.to_i
90
+ if sz >= 0
91
+ @size = sz
92
+ end
95
93
  end
96
- attr_reader :path, :ok, :fail, :size, :time, :rename
94
+ end
97
95
 
98
- def add(h,func,c)
99
- h[func] = (h[func] || 0) + c
100
- end
101
96
 
102
- def count(fc)
103
- if fc.ret == "-1"
104
- add(@fail, fc.func, 1)
105
- else
106
- add(@ok, fc.func, 1)
97
+ class Stat
98
+
99
+ class Counter
100
+ def initialize
101
+ @calls = 0
102
+ @errors = 0
103
+ @time = 0
104
+ @size = nil
107
105
  end
108
- add(@time, fc.func, fc.elap.to_f) if fc.elap
109
- end
106
+ attr_reader :calls, :errors, :size, :time
110
107
 
111
- def count_size(fc)
112
- sz = fc.ret.to_i
113
- if sz >= 0
114
- add(@size, fc.func, sz)
108
+ def count(call)
109
+ @calls += 1
110
+ @errors += 1 if call.ret == "-1"
111
+ @time += call.elap.to_f if call.elap
112
+ @size = (@size||0) + call.size if call.size
115
113
  end
116
- count(fc)
117
- end
118
114
 
119
- def rename_as(newpath)
120
- @rename << @path
121
- @path = newpath
115
+ def to_a
116
+ [@calls,@errors,"%.6f"%@time,@size]
117
+ end
122
118
  end
123
119
 
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"
120
+ class CallCounter
121
+ def initialize(path)
122
+ @path = path
123
+ @rename = []
124
+ @counter = {}
129
125
  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"
126
+ attr_reader :path, :rename
127
+
128
+ def count(call)
129
+ c = (@counter[call.func] ||= Counter.new)
130
+ c.count(call)
137
131
  end
138
- if !@time.empty?
139
- keys = @time.keys.sort
140
- Kernel.print " time={"+keys.map{|k| "%s:%.6f"%[k,@time[k]]}.join(", ")+"}\n"
132
+
133
+ def rename_as(newpath)
134
+ @rename << @path
135
+ @path = newpath
141
136
  end
142
- if !@rename.empty?
143
- Kernel.print " rename={#{@rename.join(', ')}}\n"
137
+
138
+ def each
139
+ @counter.keys.map do |func|
140
+ yield [@path,func,*@counter[func].to_a]
141
+ end
144
142
  end
145
- puts
146
143
  end
147
- end
148
144
 
149
-
150
- class Stat
151
-
152
- def initialize
145
+ def initialize(sum:false,table:'/etc/mtab',column:2)
146
+ @sum = sum
153
147
  @stat = {}
154
- @count = {}
155
- @spent = {}
148
+ @total = CallCounter.new('*')
149
+ if @sum
150
+ a = open(table,'r').each_line.map do |line|
151
+ line.split(/\s+/)[column-1]
152
+ end.sort.reverse
153
+ @paths = Hash[ a.map{|x| [x, /^#{x.sub(/\/$/,'')}($|\/)/] } ]
154
+ end
156
155
  end
157
156
 
158
- attr_reader :stat, :count, :spent
157
+ attr_reader :stat, :total
159
158
 
160
159
  def parse(a)
161
160
  @fd2path = ["stdin", "stdout", "stderr"]
@@ -164,101 +163,108 @@ module StraceLog
164
163
  end
165
164
  end
166
165
 
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
166
+ def get_fd(arg)
167
+ if /([-\d]+)(<.*>)?/ =~ arg
168
+ x = $1.to_i
169
+ return x
170
+ else
171
+ raise
172
+ end
173
+ end
174
+
175
+ def stat_call(call)
176
+ path = nil
177
+
178
+ case call.func
172
179
 
173
- case m
180
+ when /^(execve|l?stat|(read|un)?link|getc?wd|access|mkdir|mknod|chmod|chown)$/
181
+ path = call.args[0]
174
182
 
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
183
+ when /^open$/
184
+ path = call.args[0]
185
+ if call.ret != "-1"
186
+ fd = call.ret.to_i
180
187
  @fd2path[fd] = path
181
188
  end
182
189
 
183
- when /^(readv?|writev?)$/
184
- path = @fd2path[pc.args[0].to_i]
185
- count_size(path,pc)
190
+ when /^connect$/
191
+ fd = get_fd(call.args[0])
192
+ @fd2path[fd] = path = "socket"
186
193
 
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"
194
+ when /^close$/
195
+ fd = get_fd(call.args[0])
196
+ path = @fd2path[fd]
197
+ if call.ret != "-1"
191
198
  @fd2path[fd] = nil
192
199
  end
193
200
 
201
+ when /^(readv?|writev?)$/
202
+ call.set_size
203
+ fd = get_fd(call.args[0])
204
+ path = @fd2path[fd]
205
+
206
+ when /^(fstat|fchmod|[fl]chown|lseek|ioctl|fcntl|getdents|sendto|recvmsg)$/
207
+ fd = get_fd(call.args[0])
208
+ path = @fd2path[fd]
209
+
194
210
  when /^rename$/
195
- path = pc.args[0]
196
- count_path(path,pc)
197
- rename(pc)
211
+ rename(call)
212
+ path = call.args[1]
198
213
 
199
214
  when /^dup[23]?$/
200
- fd = pc.args[0].to_i
201
- count_fd(fd,pc)
202
- if pc.ret != "-1"
203
- fd2 = pc.ret.to_i
215
+ fd = get_fd(call.args[0])
216
+ path = @fd2path[fd]
217
+ if call.ret != "-1"
218
+ fd2 = call.ret.to_i
204
219
  @fd2path[fd2] = @fd2path[fd]
205
220
  end
206
221
 
207
222
  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)
223
+ fd = get_fd(call.args[4])
224
+ path = @fd2path[fd] if fd >= 0
217
225
 
226
+ when NilClass
227
+ return
218
228
  end
219
- end
220
229
 
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
230
+ @total.count(call)
229
231
 
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)
232
+ if path
233
+ if @sum
234
+ realpath = File.exist?(path) ? Pathname.new(path).realdirpath.to_s : path
235
+ @paths.each do |mp,re|
236
+ if re =~ realpath
237
+ (@stat[mp] ||= CallCounter.new(mp)).count(call)
238
+ return
239
+ end
240
+ end
241
+ end
242
+ (@stat[path] ||= CallCounter.new(path)).count(call)
243
+ end
236
244
  end
237
245
 
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]
246
+ def rename(call)
247
+ if call.ret != "-1"
248
+ oldpath = call.args[0]
249
+ newpath = call.args[1]
250
+ ioc = @stat[newpath] = (@stat[oldpath] ||= CallCounter.new(oldpath))
243
251
  ioc.rename_as(newpath)
244
252
  @stat.delete(oldpath)
245
253
  end
246
254
  end
247
255
 
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.printf " %s: %.6f,\n",m,t
256
+ def write(file=nil)
257
+ block = proc do |w|
258
+ w << ["path","syscall","calls","errors","time","size"]
259
+ @total.each{|item| w << item}
260
+ @stat.each do |path,cntr|
261
+ cntr.each{|item| w << item}
262
+ end
257
263
  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
264
+ if file
265
+ CSV.open(file,'w',&block)
266
+ else
267
+ CSV.instance(&block)
262
268
  end
263
269
  end
264
270
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strace_log
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masahiro TANAKA
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-21 00:00:00.000000000 Z
11
+ date: 2019-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -72,10 +72,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
72
  - !ruby/object:Gem::Version
73
73
  version: '0'
74
74
  requirements: []
75
- rubyforge_project:
76
- rubygems_version: 2.4.5
75
+ rubygems_version: 3.0.1
77
76
  signing_key:
78
77
  specification_version: 4
79
78
  summary: Parse strace log
80
79
  test_files: []
81
- has_rdoc: