stack_tracy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,24 @@
1
+ module Kernel
2
+
3
+ def stack_tracy(arg = nil, options = {})
4
+ if arg.is_a?(Hash)
5
+ options = arg
6
+ arg = nil
7
+ end
8
+ StackTracy.start options
9
+ yield
10
+ StackTracy.stop
11
+ if arg == :print
12
+ StackTracy.print
13
+ elsif arg == :dump
14
+ StackTracy.dump
15
+ elsif arg == :open
16
+ file = StackTracy.dump Dir::tmpdir
17
+ StackTracy.open file, true
18
+ elsif arg.is_a? String
19
+ StackTracy.dump arg
20
+ end
21
+ nil
22
+ end
23
+
24
+ end
@@ -0,0 +1,72 @@
1
+ # See http://stackoverflow.com/questions/9607554/ruby-invalid-byte-sequence-in-utf-8
2
+
3
+ # encoding: UTF-8
4
+
5
+ module StackTracy
6
+ class EventInfo
7
+ attr_reader :event, :file, :line, :singleton, :object, :method, :nsec
8
+
9
+ def self.to_hashes(csv)
10
+ CSV.parse(csv.force_encoding("ISO-8859-1").encode("utf-8", replace: nil), :headers => true, :col_sep => ";").collect do |row|
11
+ {
12
+ :event => row[0] , :file => row[1] , :line => row[2].to_i, :singleton => row[3] == "true", :object => row[4] , :method => row[5],
13
+ :nsec => row[6].to_f, :time => row[7].to_f, :call => row[8] , :depth => row[9].to_i , :duration => row[10].to_f
14
+ }
15
+ end
16
+ end
17
+
18
+ def call?
19
+ !!event.match(/call$/)
20
+ end
21
+
22
+ def return?
23
+ !!event.match(/return$/)
24
+ end
25
+
26
+ def matches?(arg)
27
+ case arg.class.name
28
+ when "StackTracy::EventInfo"
29
+ matches? arg.call
30
+ when "String"
31
+ if call == arg
32
+ true
33
+ else
34
+ captures = arg.match(/^([\w:]*\*?)?(\.|\#)?(\w*\*?)?$/).captures
35
+ object_match?(captures[0]) && singleton_match?(captures[1]) && method_match?(captures[2])
36
+ end
37
+ else
38
+ false
39
+ end
40
+ end
41
+
42
+ def -(other)
43
+ nsec - other.nsec if other.is_a? EventInfo
44
+ end
45
+
46
+ def to_hash(first = nil)
47
+ {
48
+ :event => event, :file => file, :line => line, :singleton => singleton, :object => object,
49
+ :method => method, :nsec => nsec, :time => (first ? self - first : nil), :call => call
50
+ }
51
+ end
52
+
53
+ def call
54
+ "#{object}#{singleton ? "." : "#"}#{method}"
55
+ end
56
+
57
+ private
58
+
59
+ def object_match?(arg)
60
+ (arg.to_s == "") || (object.name =~ /^#{arg.gsub("*", "#{"\\w*" if arg == "*"}(::\\w*)*")}$/)
61
+ end
62
+
63
+ def singleton_match?(arg)
64
+ arg.nil? || ((singleton ? "." : "#") == arg)
65
+ end
66
+
67
+ def method_match?(arg)
68
+ (arg.to_s == "") || (method =~ /^#{arg.gsub("*", "\\w*")}$/)
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,36 @@
1
+ module StackTracy
2
+ class Sinatra
3
+
4
+ def initialize(app, arg = nil, options = {}, &before_filter)
5
+ @app = app
6
+ @arg = arg
7
+ @options = options
8
+ @before_filter = before_filter if block_given?
9
+ end
10
+
11
+ def call(env)
12
+ request = ::Sinatra::Request.new env
13
+ if request.path.match /^\/tracy-?(.*)?/
14
+ return open($1)
15
+ end
16
+
17
+ if @before_filter.nil? || !!@before_filter.call(request.path, request.params)
18
+ result = nil
19
+ stack_tracy @arg || Dir::tmpdir, @options do
20
+ result = @app.call(env)
21
+ end
22
+ result
23
+ else
24
+ @app.call(env)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def open(match)
31
+ StackTracy.open match.to_s.empty? ? nil : match, (match.to_s.empty? && @arg.to_s != "dump" && !StackTracy.stack_trace.empty?)
32
+ [200, {"Content-Type" => "text/html;charset=utf-8", "Content-Length" => Rack::Utils.bytesize("").to_s}, ""]
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,7 @@
1
+ module StackTracy #:nodoc:
2
+ MAJOR = 0
3
+ MINOR = 1
4
+ TINY = 0
5
+
6
+ VERSION = [MAJOR, MINOR, TINY].join(".")
7
+ end
data/script/console ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ `cd ext/stack_tracy && ruby extconf.rb`
4
+ `cd ext/stack_tracy && make`
5
+
6
+ require "rubygems"
7
+ require "bundler"
8
+
9
+ Bundler.require :gem_default, :gem_development
10
+
11
+ puts "Loading development environment (StackTracy #{StackTracy::VERSION})"
12
+
13
+ Pry.start
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.authors = ["Paul Engel"]
5
+ gem.email = ["paul.engel@holder.nl"]
6
+ gem.summary = %q{Investigate and detect slow methods within the stack trace of your Ruby (optionally Sinatra) application}
7
+ gem.description = %q{Investigate and detect slow methods within the stack trace of your Ruby (optionally Sinatra) application}
8
+ gem.homepage = "https://github.com/archan937/stack_tracy"
9
+
10
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
11
+ gem.extensions = ["ext/stack_tracy/extconf.rb"]
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "stack_tracy"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = "0.1.0"
17
+
18
+ gem.add_dependency "rich_support", "~> 0.1.2"
19
+ gem.add_dependency "launchy", "2.1.0"
20
+ end
@@ -0,0 +1,7 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+
4
+ require "minitest/unit"
5
+ require "minitest/autorun"
6
+
7
+ Bundler.require :gem_default, :gem_test
@@ -0,0 +1,13 @@
1
+ require File.expand_path("../../test_helper", __FILE__)
2
+
3
+ module Unit
4
+ class TestKernel < MiniTest::Unit::TestCase
5
+
6
+ describe Kernel do
7
+ it "should respond to stack_tracy and behave as expected" do
8
+ assert Kernel.respond_to?(:stack_tracy)
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,261 @@
1
+ require File.expand_path("../../test_helper", __FILE__)
2
+
3
+ module Unit
4
+ class TestTracy < MiniTest::Unit::TestCase
5
+
6
+ describe StackTracy do
7
+ after do
8
+ StackTracy.config do |c|
9
+ c.only = nil
10
+ c.exclude = nil
11
+ end
12
+ end
13
+
14
+ it "should respond to methods as expected" do
15
+ assert StackTracy.respond_to?(:config)
16
+ assert StackTracy.respond_to?(:start)
17
+ assert StackTracy.respond_to?(:stop)
18
+ assert StackTracy.respond_to?(:stack_trace)
19
+ assert StackTracy.respond_to?(:select)
20
+ assert StackTracy.respond_to?(:print)
21
+ assert StackTracy.respond_to?(:dump)
22
+ assert StackTracy.respond_to?(:open)
23
+ end
24
+
25
+ it "should be configurable" do
26
+ StackTracy.config do |c|
27
+ assert_equal true, c.is_a?(Struct)
28
+ assert_equal [:dump_dir, :only, :exclude], c.members
29
+ c.only = "Kernel"
30
+ c.exclude = ["IO", "String"]
31
+ end
32
+
33
+ assert_equal({:only => "Kernel", :exclude => ["IO", "String"]}, StackTracy.send(:merge_options))
34
+ assert_equal({:only => "Object", :exclude => ["IO", "String"]}, StackTracy.send(:merge_options, {:only => "Object"}))
35
+ assert_equal({:only => "Object", :exclude => nil}, StackTracy.send(:merge_options, {:only => "Object", :exclude => nil}))
36
+ assert_equal({:only => "Paul", :exclude => "Foo"}, StackTracy.send(:merge_options, {"only" => "Paul", "exclude" => "Foo"}))
37
+ assert_equal({:only => nil, :exclude => nil}, StackTracy.send(:merge_options, {:only => nil, :exclude => nil}))
38
+
39
+ assert_equal(
40
+ "Array BasicObject Enumerable Fixnum Float Foo Hash IO Integer Kernel Module Mutex Numeric Object Rational String Symbol Thread Time",
41
+ StackTracy.send(:mod_names, [:core, "Foo"])
42
+ )
43
+
44
+ assert_equal(
45
+ "ActiveRecord::Base",
46
+ StackTracy.send(:mod_names, :active_record)
47
+ )
48
+
49
+ assert_equal(
50
+ "DataMapper::Resource",
51
+ StackTracy.send(:mod_names, :data_mapper)
52
+ )
53
+ end
54
+
55
+ it "should have the expected stack trace" do
56
+ stack_tracy do
57
+ puts "testing"
58
+ end
59
+ file, line = __FILE__, __LINE__ - 2
60
+
61
+ assert_equal [
62
+ {:event => "c-call" , :file => file, :line => line, :singleton => false, :object => Kernel, :method => "puts" , :call => "Kernel#puts"},
63
+ {:event => "c-call" , :file => file, :line => line, :singleton => false, :object => IO , :method => "puts" , :call => "IO#puts" },
64
+ {:event => "c-call" , :file => file, :line => line, :singleton => false, :object => IO , :method => "write", :call => "IO#write" },
65
+ {:event => "c-return", :file => file, :line => line, :singleton => false, :object => IO , :method => "write", :call => "IO#write" },
66
+ {:event => "c-call" , :file => file, :line => line, :singleton => false, :object => IO , :method => "write", :call => "IO#write" },
67
+ {:event => "c-return", :file => file, :line => line, :singleton => false, :object => IO , :method => "write", :call => "IO#write" },
68
+ {:event => "c-return", :file => file, :line => line, :singleton => false, :object => IO , :method => "puts" , :call => "IO#puts" },
69
+ {:event => "c-return", :file => file, :line => line, :singleton => false, :object => Kernel, :method => "puts" , :call => "Kernel#puts"}
70
+ ], StackTracy.stack_trace.collect{ |event_info|
71
+ event_info.to_hash.tap do |hash|
72
+ assert hash.delete(:nsec)
73
+ hash.delete(:time)
74
+ end
75
+ }
76
+
77
+ assert StackTracy.stack_trace.first.call?
78
+ assert !StackTracy.stack_trace.last.call?
79
+
80
+ assert !StackTracy.stack_trace.first.return?
81
+ assert StackTracy.stack_trace.last.return?
82
+
83
+ StackTracy.config do |c|
84
+ c.exclude = ["IO"]
85
+ end
86
+ stack_tracy do
87
+ puts "testing"
88
+ end
89
+ file, line = __FILE__, __LINE__ - 2
90
+
91
+ assert_equal [
92
+ {:event => "c-call" , :file => file, :line => line, :singleton => false, :object => Kernel, :method => "puts" , :call => "Kernel#puts"},
93
+ {:event => "c-return", :file => file, :line => line, :singleton => false, :object => Kernel, :method => "puts" , :call => "Kernel#puts"}
94
+ ], StackTracy.stack_trace.collect{ |event_info|
95
+ event_info.to_hash.tap do |hash|
96
+ assert hash.delete(:nsec)
97
+ hash.delete(:time)
98
+ end
99
+ }
100
+
101
+ StackTracy.config do |c|
102
+ c.exclude = :core
103
+ end
104
+ stack_tracy do
105
+ puts "testing"
106
+ end
107
+
108
+ assert_equal true, StackTracy.stack_trace.empty?
109
+ end
110
+
111
+ it "should return a printable version of the stack trace" do
112
+ stack_tracy do
113
+ puts "testing"
114
+ end
115
+ file, line = __FILE__, __LINE__ - 2
116
+
117
+ assert_equal [
118
+ {:event => "c-call" , :file => file, :line => line, :singleton => false, :object => Kernel, :method => "puts" , :call => "Kernel#puts", :depth => 0},
119
+ {:event => "c-call" , :file => file, :line => line, :singleton => false, :object => IO , :method => "puts" , :call => "IO#puts" , :depth => 1},
120
+ {:event => "c-call" , :file => file, :line => line, :singleton => false, :object => IO , :method => "write", :call => "IO#write" , :depth => 2},
121
+ {:event => "c-call" , :file => file, :line => line, :singleton => false, :object => IO , :method => "write", :call => "IO#write" , :depth => 2}
122
+ ], StackTracy.select.collect{ |event_info|
123
+ event_info.to_hash.tap do |hash|
124
+ assert hash.delete(:nsec)
125
+ assert hash.delete(:duration)
126
+ hash.delete(:time)
127
+ end
128
+ }
129
+ end
130
+
131
+ it "should filter methods as expected" do
132
+ stack_tracy do
133
+ puts "testing"
134
+ end
135
+ file, line = __FILE__, __LINE__ - 2
136
+
137
+ assert_equal [
138
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => Kernel, :method => "puts" , :call => "Kernel#puts", :depth => 0},
139
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO , :method => "puts" , :call => "IO#puts" , :depth => 1},
140
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO , :method => "write", :call => "IO#write" , :depth => 2},
141
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO , :method => "write", :call => "IO#write" , :depth => 2}
142
+ ], StackTracy.select("*").collect{ |event_info|
143
+ event_info.to_hash.tap do |hash|
144
+ assert hash.delete(:nsec)
145
+ assert hash.delete(:duration)
146
+ hash.delete(:time)
147
+ end
148
+ }
149
+
150
+ assert_equal [
151
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => Kernel, :method => "puts", :call => "Kernel#puts", :depth => 0}
152
+ ], StackTracy.select("Kernel").collect{ |event_info|
153
+ event_info.to_hash.tap do |hash|
154
+ assert hash.delete(:nsec)
155
+ assert hash.delete(:duration)
156
+ hash.delete(:time)
157
+ end
158
+ }
159
+
160
+ assert_equal [
161
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO, :method => "puts" , :call => "IO#puts" , :depth => 0},
162
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO, :method => "write", :call => "IO#write", :depth => 1},
163
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO, :method => "write", :call => "IO#write", :depth => 1}
164
+ ], StackTracy.select("IO").collect{ |event_info|
165
+ event_info.to_hash.tap do |hash|
166
+ assert hash.delete(:nsec)
167
+ assert hash.delete(:duration)
168
+ hash.delete(:time)
169
+ end
170
+ }
171
+
172
+ assert_equal [
173
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO, :method => "puts" , :call => "IO#puts" , :depth => 0},
174
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO, :method => "write", :call => "IO#write", :depth => 1},
175
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO, :method => "write", :call => "IO#write", :depth => 1}
176
+ ], StackTracy.select("IO*").collect{ |event_info|
177
+ event_info.to_hash.tap do |hash|
178
+ assert hash.delete(:nsec)
179
+ assert hash.delete(:duration)
180
+ hash.delete(:time)
181
+ end
182
+ }
183
+
184
+ assert_equal [
185
+ ], StackTracy.select("I*").collect{ |event_info|
186
+ event_info.to_hash
187
+ }
188
+
189
+ assert_equal [
190
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO, :method => "puts" , :call => "IO#puts" , :depth => 0},
191
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO, :method => "write", :call => "IO#write", :depth => 1},
192
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO, :method => "write", :call => "IO#write", :depth => 1}
193
+ ], StackTracy.select("IO#").collect{ |event_info|
194
+ event_info.to_hash.tap do |hash|
195
+ assert hash.delete(:nsec)
196
+ assert hash.delete(:duration)
197
+ hash.delete(:time)
198
+ end
199
+ }
200
+
201
+ assert_equal [
202
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO, :method => "write", :call => "IO#write", :depth => 0},
203
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO, :method => "write", :call => "IO#write", :depth => 0}
204
+ ], StackTracy.select("IO#w*").collect{ |event_info|
205
+ event_info.to_hash.tap do |hash|
206
+ assert hash.delete(:nsec)
207
+ assert hash.delete(:duration)
208
+ hash.delete(:time)
209
+ end
210
+ }
211
+
212
+ assert_equal [
213
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO, :method => "write", :call => "IO#write", :depth => 0},
214
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO, :method => "write", :call => "IO#write", :depth => 0}
215
+ ], StackTracy.select("IO#write").collect{ |event_info|
216
+ event_info.to_hash.tap do |hash|
217
+ assert hash.delete(:nsec)
218
+ assert hash.delete(:duration)
219
+ hash.delete(:time)
220
+ end
221
+ }
222
+
223
+ assert_equal [
224
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => Kernel, :method => "puts", :call => "Kernel#puts", :depth => 0},
225
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO , :method => "puts", :call => "IO#puts" , :depth => 1}
226
+ ], StackTracy.select("*#puts").collect{ |event_info|
227
+ event_info.to_hash.tap do |hash|
228
+ assert hash.delete(:nsec)
229
+ assert hash.delete(:duration)
230
+ hash.delete(:time)
231
+ end
232
+ }
233
+
234
+ assert_equal [
235
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => Kernel, :method => "puts" , :call => "Kernel#puts", :depth => 0},
236
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO , :method => "write", :call => "IO#write" , :depth => 1},
237
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO , :method => "write", :call => "IO#write" , :depth => 1}
238
+ ], StackTracy.select(%w(Kernel #write)).collect{ |event_info|
239
+ event_info.to_hash.tap do |hash|
240
+ assert hash.delete(:nsec)
241
+ assert hash.delete(:duration)
242
+ hash.delete(:time)
243
+ end
244
+ }
245
+
246
+ assert_equal [
247
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => Kernel, :method => "puts" , :call => "Kernel#puts", :depth => 0},
248
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO , :method => "write", :call => "IO#write" , :depth => 1},
249
+ {:event => "c-call", :file => file, :line => line, :singleton => false, :object => IO , :method => "write", :call => "IO#write" , :depth => 1}
250
+ ], StackTracy.select("Kernel IO#write").collect{ |event_info|
251
+ event_info.to_hash.tap do |hash|
252
+ assert hash.delete(:nsec)
253
+ assert hash.delete(:duration)
254
+ hash.delete(:time)
255
+ end
256
+ }
257
+ end
258
+ end
259
+
260
+ end
261
+ end