stack_tracy 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.
@@ -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