strace_log 0.1.3 → 0.2.1

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