yuslow 0.0.2 → 0.0.3
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.
- 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
|