yuslow 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +44 -0
- data/lib/yuslow/investigation.rb +72 -0
- data/lib/yuslow/operation.rb +39 -0
- data/lib/yuslow/stdout_printer.rb +60 -0
- metadata +5 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5246c2535d0277df7d0117c0abdb1a9ce9eeb49c434d0df1db0bfa85f71fbdd5
|
4
|
+
data.tar.gz: e2408ca24e820cd5d91cfa10b441bbbf8246e7476f7aab48f657bc6cd82fe1b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a89ddd6dd6063d40a3614904226728771d15f746a29d326e2b01cc96b2f6895f01e0366c375b6dccb6862a1492cebb94386e4e0a176bf88266d8f61c0c26b482
|
7
|
+
data.tar.gz: 9c0c568bcd841ac8b0e78927b00fa7cc73d1bdf4cb0bc2a6e140679de948d5e853210798137158c6403583f1fa07d6cf7a1a1172418284dbbc108e4126538ec9
|
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# Yuslow
|
2
|
+
Yuslow `[waɪ ju sləʊ]` is a lightweight profiler for Ruby designed to debug slow parts of your system.
|
3
|
+
|
4
|
+
## Install
|
5
|
+
|
6
|
+
```
|
7
|
+
gem install yuslow
|
8
|
+
```
|
9
|
+
|
10
|
+
## Usage
|
11
|
+
Require the gem in the file which you would like to investigate.
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
require 'yuslow'
|
15
|
+
```
|
16
|
+
|
17
|
+
then add
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
Yuslow.run do
|
21
|
+
a_very_slow_method
|
22
|
+
end
|
23
|
+
```
|
24
|
+
|
25
|
+
or
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
investigation = Yuslow.investigation
|
29
|
+
|
30
|
+
investigation.start
|
31
|
+
a_very_slow_method
|
32
|
+
investigation.finish
|
33
|
+
```
|
34
|
+
|
35
|
+
to your code. Execute the code and you will see output in $stdout with the following tree structure. It will help you understand which branch and method of your code has poor performance.
|
36
|
+
|
37
|
+
```
|
38
|
+
Thread[1]:
|
39
|
+
Object#very_slow elapsed 1000 ms
|
40
|
+
Object#not_my_fault elapsed 1000 ms
|
41
|
+
Object#not_me elapsed 0 ms
|
42
|
+
Object#root_cause elapsed 1000 ms
|
43
|
+
Kernel#sleep elapsed 1000 ms
|
44
|
+
```
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Yuslow
|
2
|
+
class Investigation
|
3
|
+
def initialize(debug: false, printer: nil)
|
4
|
+
@printer = printer
|
5
|
+
@debug = debug
|
6
|
+
@tracing = false
|
7
|
+
@trace = nil
|
8
|
+
@indent = 0
|
9
|
+
@execution = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def start
|
13
|
+
@trace = TracePoint.new(:call, :return, :c_call, :c_return) do |trace_point|
|
14
|
+
thread_id = Thread.current.object_id
|
15
|
+
@execution[thread_id] ||= {root: nil, current: nil}
|
16
|
+
|
17
|
+
if trace_point.defined_class == self.class && trace_point.callee_id == :start
|
18
|
+
@tracing = true
|
19
|
+
elsif trace_point.defined_class == self.class && trace_point.callee_id == :finish
|
20
|
+
@tracing = false
|
21
|
+
elsif @tracing
|
22
|
+
if %i(call c_call).include? trace_point.event
|
23
|
+
if @execution[thread_id][:current]
|
24
|
+
@execution[thread_id][:current] =
|
25
|
+
@execution[thread_id][:current].fork object: trace_point.defined_class,
|
26
|
+
method: trace_point.callee_id
|
27
|
+
else
|
28
|
+
operation = Operation.new object: trace_point.defined_class, method: trace_point.callee_id
|
29
|
+
@execution[thread_id][:root] = operation
|
30
|
+
@execution[thread_id][:current] = operation
|
31
|
+
end
|
32
|
+
|
33
|
+
debug trace_point, @indent
|
34
|
+
@indent += 1
|
35
|
+
|
36
|
+
elsif %i(return c_return).include? trace_point.event
|
37
|
+
@indent -= 1
|
38
|
+
debug trace_point, @indent
|
39
|
+
|
40
|
+
@execution[thread_id][:current]&.complete
|
41
|
+
@execution[thread_id][:current] = @execution[thread_id][:current]&.parent
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
@trace.enable
|
47
|
+
end
|
48
|
+
|
49
|
+
def finish
|
50
|
+
@trace.disable
|
51
|
+
|
52
|
+
print_result @printer if @printer
|
53
|
+
end
|
54
|
+
|
55
|
+
def debug(trace_point, indent)
|
56
|
+
return unless @debug
|
57
|
+
|
58
|
+
puts (' ' * indent) + "#{trace_point.event} #{trace_point.defined_class}##{trace_point.callee_id}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def print_result(printer)
|
62
|
+
return unless printer
|
63
|
+
|
64
|
+
operations =
|
65
|
+
@execution.keys.map do |thread_id|
|
66
|
+
@execution[thread_id][:root]
|
67
|
+
end
|
68
|
+
|
69
|
+
printer.execute operations
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Operation
|
2
|
+
attr_reader :parent, :children
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def start(object, method)
|
6
|
+
new(object, method)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(object:, method:, parent: nil, started_at: Time.now, completed_at: nil, children: [])
|
11
|
+
@object = object
|
12
|
+
@method = method
|
13
|
+
@started_at = started_at
|
14
|
+
@completed_at = completed_at
|
15
|
+
@parent = parent
|
16
|
+
@children = children
|
17
|
+
end
|
18
|
+
|
19
|
+
def fork(object:, method:)
|
20
|
+
operation = self.class.new object: object, method: method, parent: self
|
21
|
+
@children << operation
|
22
|
+
|
23
|
+
operation
|
24
|
+
end
|
25
|
+
|
26
|
+
def identifier
|
27
|
+
"#{@object}##{@method}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def complete
|
31
|
+
raise 'This operation is already completed' if @completed_at
|
32
|
+
|
33
|
+
@completed_at = Time.now
|
34
|
+
end
|
35
|
+
|
36
|
+
def elapsed
|
37
|
+
((@completed_at - @started_at) * 1000).round if @completed_at
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Yuslow
|
2
|
+
module StdoutPrinter
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def execute(operations, colorize = true)
|
6
|
+
output = []
|
7
|
+
|
8
|
+
operations.each_with_index.map do |operation, index|
|
9
|
+
output << "Thread[#{index + 1}]:"
|
10
|
+
output << stringify_operation(operation, 1, colorize)
|
11
|
+
output << nil
|
12
|
+
end
|
13
|
+
|
14
|
+
print output.join "\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
def stringify_operation(operation, indent, colorize)
|
18
|
+
output = []
|
19
|
+
indentation = ' ' * indent
|
20
|
+
|
21
|
+
if operation.elapsed
|
22
|
+
identifier = operation.identifier
|
23
|
+
identifier = colorize(identifier, :green) if colorize
|
24
|
+
|
25
|
+
elapsed = operation.elapsed
|
26
|
+
elapsed = colorize(elapsed, :white) if colorize
|
27
|
+
|
28
|
+
output << "#{indentation}#{identifier} elapsed #{elapsed} ms"
|
29
|
+
else
|
30
|
+
text = "#{operation.identifier} did not finish"
|
31
|
+
colorize(text, :red) if colorize
|
32
|
+
|
33
|
+
output << "#{indentation}#{text}"
|
34
|
+
end
|
35
|
+
|
36
|
+
operation.children.each do |child_operation|
|
37
|
+
output << stringify_operation(child_operation, indent + 1, colorize)
|
38
|
+
end
|
39
|
+
|
40
|
+
output.join "\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
def colorize(text, color)
|
44
|
+
code =
|
45
|
+
case color
|
46
|
+
when :black then '30'
|
47
|
+
when :red then '31'
|
48
|
+
when :green then '32'
|
49
|
+
when :white then '1;37'
|
50
|
+
when :bg_black then '40'
|
51
|
+
when :bg_red then '41'
|
52
|
+
when :bg_green then '42'
|
53
|
+
else
|
54
|
+
0
|
55
|
+
end
|
56
|
+
|
57
|
+
"\e[#{code}m#{text}\e[0m"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yuslow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Deyan Dobrinov
|
@@ -31,7 +31,11 @@ executables: []
|
|
31
31
|
extensions: []
|
32
32
|
extra_rdoc_files: []
|
33
33
|
files:
|
34
|
+
- README.md
|
34
35
|
- lib/yuslow.rb
|
36
|
+
- lib/yuslow/investigation.rb
|
37
|
+
- lib/yuslow/operation.rb
|
38
|
+
- lib/yuslow/stdout_printer.rb
|
35
39
|
homepage: https://github.com/dobrinov/yuslow
|
36
40
|
licenses:
|
37
41
|
- MIT
|