runtime_profiler 0.1.4 → 0.4.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/.semaphore/semaphore.yml +18 -0
- data/README.md +117 -19
- data/lib/runtime_profiler.rb +18 -22
- data/lib/runtime_profiler/callbacks/action_controller.rb +1 -0
- data/lib/runtime_profiler/callbacks/active_record.rb +2 -2
- data/lib/runtime_profiler/cli.rb +17 -12
- data/lib/runtime_profiler/{instrumentation_data.rb → data.rb} +46 -43
- data/lib/runtime_profiler/events/process_action_event.rb +1 -1
- data/lib/runtime_profiler/events/sql_event.rb +6 -6
- data/lib/runtime_profiler/profiler.rb +17 -17
- data/lib/runtime_profiler/text_report.rb +158 -51
- data/lib/runtime_profiler/version.rb +1 -1
- data/rp_view_command.png +0 -0
- data/runtime_profiler.gemspec +8 -8
- metadata +34 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 10844b957c031772b37b68bfe5f2c83fc98c25c40c709eea8e6920badd400b04
|
4
|
+
data.tar.gz: 52b61fe38de36b0f97fa16bbe742e3baa5e4444f22db6fa64e577deddeba491a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 289e5c785b16a2fb16ff17af9c1d7dbb02d765cf6990bac5ad1c7f4f6005042ff8589a80b79f54b7473705b5323adfb93b49cd31f4fa3e3934d9df15dbb31a6c
|
7
|
+
data.tar.gz: 5b783916251fafdf991f7a3cca98336bc329ce96ff4b1efcb4f215dc2924439c3dabe797f66502956b4169bb72bc60512f3a7b7b04740463a65cb5c60a803353
|
@@ -0,0 +1,18 @@
|
|
1
|
+
version: v1.0
|
2
|
+
name: Ruby
|
3
|
+
agent:
|
4
|
+
machine:
|
5
|
+
type: e1-standard-2
|
6
|
+
os_image: ubuntu1804
|
7
|
+
blocks:
|
8
|
+
- name: test
|
9
|
+
task:
|
10
|
+
jobs:
|
11
|
+
- name: test
|
12
|
+
commands:
|
13
|
+
- checkout
|
14
|
+
- sem-version ruby 2.6.5
|
15
|
+
- cache restore
|
16
|
+
- bundle install --path vendor/bundle
|
17
|
+
- cache store
|
18
|
+
- bundle exec rake
|
data/README.md
CHANGED
@@ -1,20 +1,36 @@
|
|
1
|
-
# runtime_profiler
|
1
|
+
# runtime_profiler
|
2
2
|
|
3
|
-
|
3
|
+
*A runtime profiler for Rails applications.*
|
4
4
|
|
5
|
-
|
5
|
+
Check which part of your Rails application is causing slow response time. **runtime_profiler** gives you an easy way to find performance problems by profiling an endpoint or a method in your Rails application.
|
6
6
|
|
7
|
-
|
7
|
+
[](https://wnuqui.semaphoreci.com/projects/runtime_profiler)
|
8
8
|
|
9
|
-
|
9
|
+
## Table of contents
|
10
10
|
|
11
|
-
|
11
|
+
- [Getting Started](#getting-started)
|
12
|
+
- [Installing](#installing)
|
13
|
+
- [Profiling](#profiling)
|
14
|
+
- [Structure](#structure)
|
15
|
+
- [Examples](#examples)
|
16
|
+
- [Viewing Profiling Result](#viewing-profiling-result)
|
17
|
+
- [view Options](#view-options)
|
18
|
+
- [Screenshot](#screenshot)
|
19
|
+
- [Configurations](#configurations)
|
20
|
+
- [Development](#development)
|
21
|
+
- [Contributing](#contributing)
|
22
|
+
- [Acknowledgement](#acknowledgement)
|
23
|
+
- [License](#license)
|
24
|
+
|
25
|
+
## Getting Started
|
26
|
+
|
27
|
+
### Installing
|
12
28
|
|
13
29
|
Add this line to your application's Gemfile:
|
14
30
|
|
15
31
|
```ruby
|
32
|
+
# In your Gemfile
|
16
33
|
group :development, :test do
|
17
|
-
... ...
|
18
34
|
gem 'runtime_profiler'
|
19
35
|
end
|
20
36
|
```
|
@@ -23,13 +39,33 @@ And then execute:
|
|
23
39
|
|
24
40
|
$ bundle
|
25
41
|
|
26
|
-
|
42
|
+
Or install it yourself as:
|
43
|
+
|
44
|
+
$ gem install runtime_profiler
|
45
|
+
|
46
|
+
### Profiling
|
47
|
+
|
48
|
+
#### Structure
|
49
|
+
|
50
|
+
To profile a specific class (model, controller, etc), all you need to do is to wrap a line where the target class (or instance) is calling a method (entry point of profiling).
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
# Profiles runtime of `ClassToProfile` class.
|
54
|
+
RuntimeProfiler.profile!('description', [ClassToProfile]) {
|
55
|
+
# one line where `ClassToProfile` (or its instance) is calling a method
|
56
|
+
}
|
57
|
+
```
|
58
|
+
|
59
|
+
Since the second argument of `.profile!` accepts array of classes, then you can provide all target classes that you want to profile.
|
60
|
+
|
61
|
+
#### Examples
|
27
62
|
|
28
|
-
|
63
|
+
You can make a test that targets a particular endpoint (or even just a method) and use `RuntimeProfiler.profile!` method in the test.
|
29
64
|
|
30
|
-
Example:
|
31
65
|
```ruby
|
32
66
|
it 'updates user' do
|
67
|
+
# Profiles runtime of PUT /users/:id endpoint and
|
68
|
+
# specifically interested with the methods of `User` model.
|
33
69
|
RuntimeProfiler.profile!('updates user', [User]) {
|
34
70
|
patch :update, { id: user.id, name: 'Joe' }
|
35
71
|
}
|
@@ -38,24 +74,86 @@ it 'updates user' do
|
|
38
74
|
end
|
39
75
|
```
|
40
76
|
|
41
|
-
Run
|
77
|
+
Run the test as usual and follow printed instructions after running.
|
78
|
+
|
79
|
+
Or if you prefer writing just code snippet, then just wrap the snippet with `RuntimeProfiler.profile!` method:
|
80
|
+
```ruby
|
81
|
+
# Profiles runtime of `UserMailer` mailer.
|
82
|
+
RuntimeProfiler.profile!('UserMailer', [UserMailer]) {
|
83
|
+
user = User.last
|
84
|
+
UserMailer.with(user: user).weekly_summary.deliver_now
|
85
|
+
}
|
86
|
+
```
|
87
|
+
|
88
|
+
**Note:** The code (test or not) where `RuntimeProfiler.profile!` is used must be **free from any mocking/stubbing** since the goal is to check performance bottlenecks.
|
89
|
+
|
90
|
+
### Viewing Profiling Result
|
91
|
+
|
92
|
+
To see profiling report, you can open the report in browser with JSON viewer report. Or you can run the following command:
|
93
|
+
|
94
|
+
```bash
|
95
|
+
bundle exec runtime_profiler view tmp/rp-124094-1608308786.json
|
96
|
+
```
|
42
97
|
|
43
|
-
|
98
|
+
#### view options
|
44
99
|
|
45
|
-
|
100
|
+
Here are the command line options for `runtime_profiler view` command.
|
46
101
|
|
47
102
|
```bash
|
48
|
-
bundle exec runtime_profiler view
|
103
|
+
$ bundle exec runtime_profiler view --help
|
104
|
+
|
105
|
+
NAME:
|
106
|
+
|
107
|
+
view
|
108
|
+
|
109
|
+
SYNOPSIS:
|
110
|
+
|
111
|
+
runtime_profiler view <profile.report.json> [options]
|
112
|
+
|
113
|
+
DESCRIPTION:
|
114
|
+
|
115
|
+
Display report in console given the JSON report file
|
116
|
+
|
117
|
+
OPTIONS:
|
118
|
+
|
119
|
+
--sort-by COLUMN
|
120
|
+
Sort by COLUMN. COLUMN can be "max_runtime", total_calls" or "total_runtime". Default is "max_runtime".
|
121
|
+
|
122
|
+
--details TYPE
|
123
|
+
TYPE can be "full" or "summary". Default is "summary"
|
124
|
+
|
125
|
+
--only-sqls
|
126
|
+
Show only SQL queries. Default is false.
|
127
|
+
|
128
|
+
--only-methods
|
129
|
+
Show only methods. Default is false.
|
130
|
+
|
131
|
+
--runtime-above RUNTIME
|
132
|
+
RUNTIME is integer or float value in ms.
|
133
|
+
|
134
|
+
--calls-above CALLS
|
135
|
+
CALLS is integer value.
|
136
|
+
|
137
|
+
--rounding ROUNDING
|
138
|
+
ROUNDING is integer value. Used in rounding runtimes. Default is 4.
|
49
139
|
```
|
50
140
|
|
51
|
-
|
141
|
+
### Screenshot
|
142
|
+
|
143
|
+
<p align="center">
|
144
|
+
<img src="rp_view_command.png">
|
145
|
+
</p>
|
146
|
+
|
147
|
+
### Configurations
|
52
148
|
|
53
|
-
All the configurable variables and their defaults are listed below
|
149
|
+
All the configurable variables and their defaults are listed below. There is no one correct place where to put these configurations. It can be inside `config/initializers` folder of your Rails application. Or if you are using test to profile, it can be in the last part of `spec/spec_helper.rb`.
|
54
150
|
```ruby
|
55
151
|
RuntimeProfiler.output_path = File.join(Rails.root.to_s, 'tmp')
|
56
|
-
RuntimeProfiler.
|
57
|
-
RuntimeProfiler.
|
58
|
-
RuntimeProfiler.
|
152
|
+
RuntimeProfiler.profiled_constants = [User]
|
153
|
+
RuntimeProfiler.profiled_paths = %w(app lib)
|
154
|
+
RuntimeProfiler.profiled_sql_commands = %w(SELECT INSERT UPDATE DELETE)
|
155
|
+
# Useful when you want to exclude in profiling specific method(s) from framework/library being used
|
156
|
+
RuntimeProfiler.excepted_methods = [:attribute_type_decorations, :_validators, :defined_enums]
|
59
157
|
```
|
60
158
|
|
61
159
|
## Development
|
data/lib/runtime_profiler.rb
CHANGED
@@ -6,52 +6,48 @@ require 'method_meter'
|
|
6
6
|
module RuntimeProfiler
|
7
7
|
include ActiveSupport::Configurable
|
8
8
|
|
9
|
-
config_accessor :
|
9
|
+
config_accessor :profiled_constants do
|
10
10
|
[]
|
11
11
|
end
|
12
12
|
|
13
|
-
config_accessor :
|
14
|
-
%w
|
13
|
+
config_accessor :profiled_paths do
|
14
|
+
%w[app lib]
|
15
15
|
end
|
16
16
|
|
17
|
-
config_accessor :
|
18
|
-
%w
|
17
|
+
config_accessor :profiled_sql_commands do
|
18
|
+
%w[SELECT INSERT UPDATE DELETE]
|
19
19
|
end
|
20
20
|
|
21
21
|
config_accessor :output_path do
|
22
|
-
if defined?(Rails) && Rails.respond_to?(:root)
|
22
|
+
if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
|
23
23
|
File.join(Rails.root.to_s, 'tmp')
|
24
24
|
else
|
25
25
|
'tmp'
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
config_accessor :excepted_methods do
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
|
29
33
|
class << self
|
30
34
|
def configure
|
31
|
-
|
35
|
+
begin
|
36
|
+
Rails.application.eager_load!
|
37
|
+
rescue StandardError
|
38
|
+
nil
|
39
|
+
end
|
32
40
|
yield self if block_given?
|
33
41
|
end
|
34
42
|
|
35
43
|
def profile!(key, konstants)
|
36
44
|
konstants = konstants.is_a?(Array) ? konstants : [konstants]
|
37
45
|
profiler = Profiler.new(konstants)
|
38
|
-
profiler.
|
46
|
+
profiler.prepare_for_profiling
|
39
47
|
|
40
48
|
MethodMeter.measure!(key) { yield }
|
41
49
|
|
42
|
-
profiler.
|
43
|
-
end
|
44
|
-
|
45
|
-
def runtime(label='for the block')
|
46
|
-
result = nil
|
47
|
-
|
48
|
-
elapsed_time = Benchmark.realtime { result = yield }
|
49
|
-
|
50
|
-
puts
|
51
|
-
puts '~~~~> ELAPSED TIME (%s): %s' % [label, elapsed_time * 1000]
|
52
|
-
puts
|
53
|
-
|
54
|
-
result
|
50
|
+
profiler.save_profiling_data
|
55
51
|
end
|
56
52
|
end
|
57
|
-
end
|
53
|
+
end
|
@@ -26,12 +26,12 @@ module RuntimeProfiler
|
|
26
26
|
|
27
27
|
@data[key][:sql] = event.sanitized_sql
|
28
28
|
@data[key][:runtimes] = [
|
29
|
-
[
|
29
|
+
[event.total_runtime, event.trace.first]
|
30
30
|
]
|
31
31
|
end
|
32
32
|
|
33
33
|
def update(event)
|
34
|
-
@data[event.key][:runtimes] << [
|
34
|
+
@data[event.key][:runtimes] << [event.total_runtime, event.trace.first]
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
data/lib/runtime_profiler/cli.rb
CHANGED
@@ -16,32 +16,37 @@ module RuntimeProfiler
|
|
16
16
|
c.syntax = 'runtime_profiler view <profile.report.json> [options]'
|
17
17
|
c.description = 'Display report in console given the JSON report file'
|
18
18
|
|
19
|
-
c.option '--sort-by COLUMN', String,
|
20
|
-
c.option '--details TYPE', String,
|
21
|
-
c.option '--
|
22
|
-
c.option '--only-
|
23
|
-
c.option '--
|
24
|
-
c.option '--calls-above CALLS',
|
19
|
+
c.option '--sort-by COLUMN', String, 'Sort by COLUMN. COLUMN can be "max_runtime", total_calls" or "total_runtime". Default is "max_runtime".'
|
20
|
+
c.option '--details TYPE', String, 'TYPE can be "full" or "summary". Default is "summary"'
|
21
|
+
c.option '--only-sqls', String, 'Show only SQL queries. Default is false.'
|
22
|
+
c.option '--only-methods', String, 'Show only methods. Default is false.'
|
23
|
+
c.option '--runtime-above RUNTIME', Float, 'RUNTIME is integer or float value in ms.'
|
24
|
+
c.option '--calls-above CALLS', Integer, 'CALLS is integer value.'
|
25
|
+
c.option '--rounding ROUNDING', Integer, 'ROUNDING is integer value. Used in rounding runtimes. Default is 4.'
|
25
26
|
|
26
27
|
c.action do |args, options|
|
27
28
|
default_options = {
|
28
|
-
sort_by: '
|
29
|
+
sort_by: 'max_runtime',
|
29
30
|
details: 'summary',
|
30
31
|
runtime_above: 0,
|
31
32
|
only_sqls: false,
|
32
33
|
only_methods: false,
|
33
|
-
calls_above:
|
34
|
+
calls_above: 0,
|
35
|
+
rounding: 4
|
34
36
|
}
|
35
37
|
|
36
38
|
options.default default_options
|
37
39
|
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
if args.first.nil?
|
41
|
+
say 'You need to supply <profile.report.json> as first argument of view.'
|
42
|
+
else
|
43
|
+
report = RuntimeProfiler::TextReport.new(args.first, options)
|
44
|
+
report.print
|
45
|
+
end
|
41
46
|
end
|
42
47
|
end
|
43
48
|
|
44
49
|
run!
|
45
50
|
end
|
46
51
|
end
|
47
|
-
end
|
52
|
+
end
|
@@ -1,18 +1,43 @@
|
|
1
1
|
module RuntimeProfiler
|
2
|
-
class
|
2
|
+
class Data
|
3
3
|
attr_reader :controller_data, :sql_data
|
4
4
|
|
5
5
|
def initialize(controller_data: nil, sql_data: nil)
|
6
|
-
@controller_data = controller_data
|
6
|
+
@controller_data = controller_data || {}
|
7
7
|
@sql_data = sql_data
|
8
8
|
end
|
9
9
|
|
10
10
|
def persist!
|
11
|
-
|
11
|
+
File.open(output_file, 'w') do |f|
|
12
|
+
f.write JSON.dump(profiling_data)
|
13
|
+
end
|
14
|
+
|
15
|
+
puts "\n"
|
16
|
+
puts 'Profiling data written at ' + output_file.to_s
|
17
|
+
puts 'You can view profiling data via: bundle exec runtime_profiler view ' + output_file.to_s
|
18
|
+
puts "\n"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
12
22
|
|
13
|
-
|
14
|
-
|
15
|
-
|
23
|
+
def output_file
|
24
|
+
FileUtils.mkdir_p(RuntimeProfiler.output_path)
|
25
|
+
filename = ['rp', Process.pid, Time.now.to_i].join('-') + '.json'
|
26
|
+
File.join(RuntimeProfiler.output_path, filename)
|
27
|
+
end
|
28
|
+
|
29
|
+
def profiled_api
|
30
|
+
return unless controller_data[:payload]
|
31
|
+
@profiled_api ||= [
|
32
|
+
controller_data[:payload][:controller],
|
33
|
+
controller_data[:payload][:action]
|
34
|
+
].join('#')
|
35
|
+
end
|
36
|
+
|
37
|
+
def profiling_data
|
38
|
+
@profiling_data ||= {
|
39
|
+
profiling: {
|
40
|
+
profiled_api: profiled_api,
|
16
41
|
summary: {
|
17
42
|
db_runtime: controller_data[:db_runtime],
|
18
43
|
view_runtime: controller_data[:view_runtime],
|
@@ -24,38 +49,16 @@ module RuntimeProfiler
|
|
24
49
|
slowest_method: method_calls_data[:slowest_method],
|
25
50
|
mostly_called_method: method_calls_data[:mostly_called_method]
|
26
51
|
},
|
27
|
-
|
28
|
-
|
29
|
-
|
52
|
+
profiled_sql_calls: sql_calls_data[:profiled_sql_calls],
|
53
|
+
profiled_methods: method_calls_data[:profiled_methods],
|
54
|
+
profiled_at: Time.now
|
30
55
|
}
|
31
56
|
}
|
32
|
-
|
33
|
-
FileUtils.mkdir_p(RuntimeProfiler.output_path)
|
34
|
-
filename = ['runtime-profiling', Process.pid, Time.now.to_i].join('-') << '.json'
|
35
|
-
output_file = File.join(RuntimeProfiler.output_path, filename)
|
36
|
-
|
37
|
-
File.open(output_file, 'w') do |f|
|
38
|
-
f.write JSON.dump(instrumentation_data)
|
39
|
-
end
|
40
|
-
|
41
|
-
puts '~~~~> [ Profiling RUNTIME ] Profiling now COMPLETE and JSON report written at ' + output_file.to_s
|
42
|
-
puts '~~~~> [ Profiling RUNTIME ]'
|
43
|
-
puts '~~~~> [ Profiling RUNTIME ] You can do the following to view the JSON report in console:'
|
44
|
-
puts '~~~~> [ Profiling RUNTIME ]'
|
45
|
-
puts '~~~~> [ Profiling RUNTIME ] bundle exec runtime_profiler view ' + output_file.to_s
|
46
|
-
puts '~~~~> [ Profiling RUNTIME ]'
|
47
|
-
puts '~~~~> [ Profiling RUNTIME ] Or'
|
48
|
-
puts '~~~~> [ Profiling RUNTIME ]'
|
49
|
-
puts '~~~~> [ Profiling RUNTIME ] bundle exec runtime_profiler view --help'
|
50
|
-
puts '~~~~> [ Profiling RUNTIME ]'
|
51
|
-
puts '~~~~> [ Profiling RUNTIME ] for more details.'
|
52
57
|
end
|
53
58
|
|
54
|
-
private
|
55
|
-
|
56
59
|
def method_calls_data
|
57
60
|
@method_calls_data ||= begin
|
58
|
-
|
61
|
+
profiled_methods = {}
|
59
62
|
|
60
63
|
# TODO: Group methods under a key and under an object
|
61
64
|
MethodMeter.measurement.each do |measurement|
|
@@ -66,16 +69,16 @@ module RuntimeProfiler
|
|
66
69
|
|
67
70
|
d[:method] = separator + object.second
|
68
71
|
|
69
|
-
if
|
70
|
-
|
72
|
+
if profiled_methods[object.first]
|
73
|
+
profiled_methods[object.first] << d
|
71
74
|
else
|
72
|
-
|
75
|
+
profiled_methods[object.first] = [d]
|
73
76
|
end
|
74
77
|
end
|
75
78
|
end
|
76
79
|
end
|
77
80
|
|
78
|
-
|
81
|
+
profiled_methods = profiled_methods.inject({}) do |hash, (key, value)|
|
79
82
|
val = value.sort { |a, b| b[:total_runtime] <=> a[:total_runtime] }
|
80
83
|
hash[key] = val
|
81
84
|
hash
|
@@ -84,7 +87,7 @@ module RuntimeProfiler
|
|
84
87
|
slowest_method = {total_runtime: 0}
|
85
88
|
mostly_called_method = {total_calls: 0}
|
86
89
|
|
87
|
-
|
90
|
+
profiled_methods.each do |profiled_object_name, methods|
|
88
91
|
# sort using `total_runtime` in DESC order
|
89
92
|
_methods = methods.sort { |a, b| b[:total_runtime] <=> a[:total_runtime] }
|
90
93
|
slowest = _methods[0]
|
@@ -107,7 +110,7 @@ module RuntimeProfiler
|
|
107
110
|
end
|
108
111
|
|
109
112
|
{
|
110
|
-
|
113
|
+
profiled_methods: profiled_methods,
|
111
114
|
slowest_method: slowest_method,
|
112
115
|
mostly_called_method: mostly_called_method
|
113
116
|
}
|
@@ -116,7 +119,7 @@ module RuntimeProfiler
|
|
116
119
|
|
117
120
|
def sql_calls_data
|
118
121
|
@sql_calls_data ||= begin
|
119
|
-
|
122
|
+
profiled_sql_calls = []
|
120
123
|
|
121
124
|
slowest_sql = {total_runtime: 0}
|
122
125
|
mostly_called_sql = {total_calls: 0}
|
@@ -137,7 +140,7 @@ module RuntimeProfiler
|
|
137
140
|
slowest_sql[:source] = slowest[1]
|
138
141
|
end
|
139
142
|
|
140
|
-
|
143
|
+
profiled_sql_calls << {
|
141
144
|
sql: value[:sql],
|
142
145
|
runtimes: runtimes,
|
143
146
|
total_calls: total_calls,
|
@@ -159,9 +162,9 @@ module RuntimeProfiler
|
|
159
162
|
end
|
160
163
|
|
161
164
|
{
|
162
|
-
|
163
|
-
total_sql_calls:
|
164
|
-
total_unique_sql_calls:
|
165
|
+
profiled_sql_calls: profiled_sql_calls.sort { |a, b| b[:max] <=> a[:max] },
|
166
|
+
total_sql_calls: profiled_sql_calls.map { |sql_call| sql_call[:total_calls] }.reduce(:+),
|
167
|
+
total_unique_sql_calls: profiled_sql_calls.size,
|
165
168
|
slowest_sql: slowest_sql,
|
166
169
|
mostly_called_sql: mostly_called_sql
|
167
170
|
}
|
@@ -10,8 +10,8 @@ module RuntimeProfiler
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def recordable?
|
13
|
-
return true unless RuntimeProfiler.
|
14
|
-
|
13
|
+
return true unless RuntimeProfiler.profiled_sql_commands.respond_to?(:join)
|
14
|
+
profiled_sql_matcher =~ sql
|
15
15
|
end
|
16
16
|
|
17
17
|
def total_runtime
|
@@ -41,12 +41,12 @@ module RuntimeProfiler
|
|
41
41
|
@sql ||= @payload[:sql].dup
|
42
42
|
end
|
43
43
|
|
44
|
-
def
|
45
|
-
@
|
44
|
+
def profiled_sql_matcher
|
45
|
+
@profiled_sql_matcher ||= /\A#{RuntimeProfiler.profiled_sql_commands.join('|')}/i
|
46
46
|
end
|
47
47
|
|
48
48
|
def trace_path_matcher
|
49
|
-
@trace_path_matcher ||= %r{^(#{RuntimeProfiler.
|
49
|
+
@trace_path_matcher ||= %r{^(#{RuntimeProfiler.profiled_paths.join('|')})\/}
|
50
50
|
end
|
51
51
|
|
52
52
|
def sanitize_trace!(trace)
|
@@ -59,7 +59,7 @@ module RuntimeProfiler
|
|
59
59
|
|
60
60
|
Rails.backtrace_cleaner.remove_silencers!
|
61
61
|
|
62
|
-
if RuntimeProfiler.
|
62
|
+
if RuntimeProfiler.profiled_paths.respond_to?(:join)
|
63
63
|
Rails.backtrace_cleaner.add_silencer do |line|
|
64
64
|
line !~ trace_path_matcher
|
65
65
|
end
|
@@ -2,19 +2,19 @@
|
|
2
2
|
|
3
3
|
require 'runtime_profiler/callbacks/active_record'
|
4
4
|
require 'runtime_profiler/callbacks/action_controller'
|
5
|
-
require 'runtime_profiler/
|
5
|
+
require 'runtime_profiler/data'
|
6
6
|
|
7
7
|
module RuntimeProfiler
|
8
8
|
class Profiler
|
9
|
-
attr_accessor :
|
9
|
+
attr_accessor :profiled_constants
|
10
10
|
|
11
|
-
def initialize(
|
12
|
-
self.
|
11
|
+
def initialize(constants)
|
12
|
+
self.profiled_constants = constants
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
15
|
+
def prepare_for_profiling
|
16
16
|
subscribe_to_event_notifications
|
17
|
-
|
17
|
+
prepare_methods_to_profile
|
18
18
|
end
|
19
19
|
|
20
20
|
def subscribe_to_event_notifications
|
@@ -23,12 +23,12 @@ module RuntimeProfiler
|
|
23
23
|
@active_record_callback = Callback::ActiveRecord.new
|
24
24
|
|
25
25
|
@subscribers << ActiveSupport::Notifications
|
26
|
-
|
26
|
+
.subscribe('sql.active_record', @active_record_callback)
|
27
27
|
|
28
28
|
@action_controller_callback = Callback::ActionController.new
|
29
29
|
|
30
30
|
@subscribers << ActiveSupport::Notifications
|
31
|
-
|
31
|
+
.subscribe('process_action.action_controller', @action_controller_callback)
|
32
32
|
end
|
33
33
|
|
34
34
|
def unsubscribe_to_event_notifications
|
@@ -37,19 +37,19 @@ module RuntimeProfiler
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
40
|
+
def prepare_methods_to_profile
|
41
|
+
profiled_constants.flatten
|
42
|
+
.each { |constant| MethodMeter.observe(constant, RuntimeProfiler.excepted_methods) }
|
43
43
|
end
|
44
44
|
|
45
|
-
def
|
45
|
+
def save_profiling_data
|
46
46
|
unsubscribe_to_event_notifications
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
profiling_data = RuntimeProfiler::Data.new \
|
49
|
+
controller_data: @action_controller_callback.controller_data,
|
50
|
+
sql_data: @active_record_callback.data
|
51
51
|
|
52
|
-
|
52
|
+
profiling_data.persist!
|
53
53
|
end
|
54
54
|
end
|
55
|
-
end
|
55
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'hirb'
|
2
2
|
require 'terminal-table'
|
3
3
|
require 'active_support/core_ext/string'
|
4
|
+
require 'pry'
|
4
5
|
|
5
6
|
module RuntimeProfiler
|
6
7
|
class TextReport
|
@@ -8,29 +9,72 @@ module RuntimeProfiler
|
|
8
9
|
DURATION_WIDTH = 22
|
9
10
|
TOTAL_RUNTIME_WIDTH = 20
|
10
11
|
|
11
|
-
|
12
|
+
FULL_DETAILS_TEMPLATE = <<-EOT.strip_heredoc
|
12
13
|
|
13
14
|
\e[1mPROFILING REPORT\e[22m
|
14
15
|
----------------
|
15
16
|
|
16
|
-
\e[
|
17
|
+
\e[1mAPI RUNTIME\e[22m
|
17
18
|
Total Runtime : %s ms
|
18
19
|
Database Runtime : %s ms
|
19
20
|
View Runtime : %s ms
|
20
21
|
|
21
|
-
\e[
|
22
|
+
\e[1mMETHOD CALLS\e[22m
|
22
23
|
SLOWEST : %s (%s ms)
|
23
24
|
MOSTLY CALLED : %s (%s number of calls in %s ms)
|
24
25
|
|
25
26
|
\e[1mSQL CALLS\e[22m
|
26
27
|
Total : %s
|
27
28
|
Total Unique : %s
|
28
|
-
|
29
|
+
|
30
|
+
\e[1mSLOWEST\e[22m
|
31
|
+
Total Runtime : %s ms
|
32
|
+
SQL : %s
|
33
|
+
Source : %s
|
34
|
+
|
35
|
+
\e[1mMOSTLY CALLED\e[22m
|
36
|
+
Total Calls : %s
|
37
|
+
Total Runtime : %s ms
|
38
|
+
SQL : %s
|
39
|
+
Sources : %s
|
40
|
+
|
41
|
+
EOT
|
42
|
+
|
43
|
+
METHODS_DETAILS_TEMPLATE = <<-EOT.strip_heredoc
|
44
|
+
|
45
|
+
\e[1mPROFILING REPORT\e[22m
|
46
|
+
----------------
|
47
|
+
|
48
|
+
\e[1mAPI RUNTIME\e[22m
|
49
|
+
Total Runtime : %s ms
|
50
|
+
Database Runtime : %s ms
|
51
|
+
View Runtime : %s ms
|
52
|
+
|
53
|
+
\e[1mMETHOD CALLS\e[22m
|
54
|
+
SLOWEST : %s (%s ms)
|
55
|
+
MOSTLY CALLED : %s (%s number of calls in %s ms)
|
56
|
+
|
57
|
+
EOT
|
58
|
+
|
59
|
+
SQLS_DETAILS_TEMPLATE = <<-EOT.strip_heredoc
|
60
|
+
|
61
|
+
\e[1mPROFILING REPORT\e[22m
|
62
|
+
----------------
|
63
|
+
|
64
|
+
\e[1mAPI RUNTIME\e[22m
|
65
|
+
Total Runtime : %s ms
|
66
|
+
Database Runtime : %s ms
|
67
|
+
View Runtime : %s ms
|
68
|
+
|
69
|
+
\e[1mSQL CALLS\e[22m
|
70
|
+
Total : %s
|
71
|
+
Total Unique : %s
|
72
|
+
|
29
73
|
\e[1mSLOWEST\e[22m
|
30
74
|
Total Runtime : %s ms
|
31
75
|
SQL : %s
|
32
76
|
Source : %s
|
33
|
-
|
77
|
+
|
34
78
|
\e[1mMOSTLY CALLED\e[22m
|
35
79
|
Total Calls : %s
|
36
80
|
Total Runtime : %s ms
|
@@ -42,68 +86,102 @@ module RuntimeProfiler
|
|
42
86
|
attr_accessor :data, :options
|
43
87
|
|
44
88
|
def initialize(json_file, options)
|
45
|
-
self.data = JSON.parse(
|
46
|
-
self.options
|
89
|
+
self.data = JSON.parse(File.read(json_file))
|
90
|
+
self.options = options
|
47
91
|
end
|
48
92
|
|
49
93
|
def print
|
50
94
|
print_summary
|
51
95
|
|
52
|
-
if
|
53
|
-
|
54
|
-
|
96
|
+
if options.details == 'full'
|
97
|
+
if only_methods?
|
98
|
+
print_profiled_methods
|
99
|
+
elsif only_sqls?
|
100
|
+
print_profiled_sql_calls
|
101
|
+
else
|
102
|
+
print_profiled_methods
|
103
|
+
print_profiled_sql_calls
|
104
|
+
end
|
55
105
|
end
|
56
106
|
end
|
57
107
|
|
58
108
|
private
|
59
109
|
|
110
|
+
def only_methods?
|
111
|
+
options.only_methods.present? && options.only_sqls.blank?
|
112
|
+
end
|
113
|
+
|
114
|
+
def only_sqls?
|
115
|
+
options.only_sqls.present? && options.only_methods.blank?
|
116
|
+
end
|
117
|
+
|
118
|
+
def rounding
|
119
|
+
options.rounding
|
120
|
+
end
|
121
|
+
|
60
122
|
def print_summary
|
61
|
-
summary =
|
123
|
+
summary = if only_methods?
|
124
|
+
METHODS_DETAILS_TEMPLATE % details_template_data
|
125
|
+
elsif only_sqls?
|
126
|
+
SQLS_DETAILS_TEMPLATE % details_template_data
|
127
|
+
else
|
128
|
+
FULL_DETAILS_TEMPLATE % details_template_data
|
129
|
+
end
|
62
130
|
puts summary
|
63
131
|
end
|
64
132
|
|
65
|
-
def
|
66
|
-
|
133
|
+
def print_profiled_methods
|
134
|
+
profiled_methods = []
|
67
135
|
|
68
|
-
|
69
|
-
|
136
|
+
data['profiling']['profiled_methods'].each do |profiled_object_name, methods|
|
137
|
+
_profiled_methods = methods.map do |method|
|
138
|
+
method['method'] = [profiled_object_name, method['method']].join
|
139
|
+
method
|
140
|
+
end
|
141
|
+
profiled_methods.concat(_profiled_methods)
|
70
142
|
end
|
71
143
|
|
72
|
-
|
144
|
+
profiled_methods = runtime_above(profiled_methods) if options.runtime_above.presence > 0
|
145
|
+
profiled_methods = calls_above(profiled_methods) if options.calls_above.presence > 0
|
146
|
+
profiled_methods = sort(profiled_methods)
|
73
147
|
|
74
148
|
table = Terminal::Table.new do |t|
|
75
|
-
t.headings = ['Method', 'Total Runtime (ms)', 'Total Calls', 'Min', 'Max']
|
149
|
+
t.headings = ['Method', 'Total Runtime (ms)', 'Total Calls', 'Min (ms)', 'Max (ms)']
|
76
150
|
|
77
|
-
|
151
|
+
profiled_methods.each_with_index do |row, index|
|
78
152
|
t.add_row [
|
79
153
|
row['method'],
|
80
|
-
row['total_runtime'],
|
154
|
+
row['total_runtime'].round(rounding),
|
81
155
|
row['total_calls'],
|
82
|
-
row['min'],
|
83
|
-
row['max']
|
156
|
+
row['min'].round(rounding),
|
157
|
+
row['max'].round(rounding)
|
84
158
|
]
|
85
|
-
t.add_separator if index <
|
159
|
+
t.add_separator if index < profiled_methods.size - 1
|
86
160
|
end
|
87
|
-
|
88
161
|
end
|
89
162
|
|
90
163
|
puts
|
91
164
|
puts
|
92
|
-
puts "\e[
|
165
|
+
puts "\e[1mPROFILED METHOD(s)\e[22m"
|
93
166
|
puts
|
94
167
|
puts table
|
95
168
|
end
|
96
169
|
|
97
|
-
def
|
98
|
-
|
170
|
+
def print_profiled_sql_calls
|
171
|
+
profiled_sql_calls = data['profiling']['profiled_sql_calls']
|
172
|
+
|
173
|
+
profiled_sql_calls = runtime_above(profiled_sql_calls) if options.runtime_above.presence > 0
|
174
|
+
profiled_sql_calls = calls_above(profiled_sql_calls) if options.calls_above.presence > 0
|
175
|
+
profiled_sql_calls = sort(profiled_sql_calls, false)
|
99
176
|
|
100
177
|
table = Terminal::Table.new do |t|
|
101
|
-
t.headings = ['Count', 'Total Runtime (ms)', 'Average Runtime (ms)', '
|
178
|
+
t.headings = ['SQL Query', 'Count', 'Total Runtime (ms)', 'Average Runtime (ms)', 'Source']
|
102
179
|
|
103
|
-
|
180
|
+
profiled_sql_calls.each_with_index do |row, index|
|
104
181
|
chopped_sql = wrap_text(row['sql'], sql_width)
|
105
182
|
source_list = wrap_list(row['runtimes'].map { |runtime| runtime[1] }.uniq, sql_width - 15)
|
106
|
-
average_runtime = row['average'].round(
|
183
|
+
average_runtime = row['average'].round(rounding)
|
184
|
+
total_runtime = row['total_runtime'].round(rounding)
|
107
185
|
total_lines = if chopped_sql.length >= source_list.length
|
108
186
|
chopped_sql.length
|
109
187
|
else
|
@@ -113,29 +191,29 @@ module RuntimeProfiler
|
|
113
191
|
(0...total_lines).each do |line|
|
114
192
|
count = line == 0 ? row['total_calls'] : ''
|
115
193
|
average = line == 0 ? average_runtime : ''
|
116
|
-
total_runtime = line == 0 ?
|
194
|
+
total_runtime = line == 0 ? total_runtime : ''
|
117
195
|
source = source_list.length > line ? source_list[line] : ''
|
118
196
|
query = row['sql'].length > line ? chopped_sql[line] : ''
|
119
197
|
|
120
198
|
t.add_row []
|
121
|
-
t.add_row [count, total_runtime, average,
|
199
|
+
t.add_row [query, count, total_runtime, average, source]
|
122
200
|
end
|
123
|
-
|
201
|
+
|
124
202
|
t.add_row []
|
125
|
-
t.add_separator if index <
|
203
|
+
t.add_separator if index < profiled_sql_calls.size - 1
|
126
204
|
end
|
127
|
-
|
128
205
|
end
|
129
206
|
|
130
207
|
puts
|
131
208
|
puts
|
132
|
-
puts "\e[
|
209
|
+
puts "\e[1mPROFILED SQL(s)\e[22m"
|
133
210
|
puts
|
134
211
|
puts table
|
135
212
|
end
|
136
213
|
|
137
214
|
def wrap_text(text, width)
|
138
215
|
return [text] if text.length <= width
|
216
|
+
|
139
217
|
text.scan(/.{1,#{width}}/)
|
140
218
|
end
|
141
219
|
|
@@ -152,43 +230,72 @@ module RuntimeProfiler
|
|
152
230
|
end
|
153
231
|
end
|
154
232
|
|
155
|
-
def sort(data)
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
233
|
+
def sort(data, methods = true)
|
234
|
+
if methods
|
235
|
+
data.sort_by do |d|
|
236
|
+
if options.sort_by == 'max_runtime'
|
237
|
+
-d['max']
|
238
|
+
elsif options.sort_by == 'total_runtime'
|
239
|
+
-d['total_runtime']
|
240
|
+
elsif options.sort_by == 'total_calls'
|
241
|
+
-d['total_calls']
|
242
|
+
end
|
161
243
|
end
|
244
|
+
else
|
245
|
+
options.sort_by = 'total_runtime' if options.sort_by == 'max_runtime'
|
246
|
+
data.sort_by { |d| options.sort_by == 'total_runtime' ? -d['total_runtime'] : -d['total_calls'] }
|
162
247
|
end
|
163
248
|
end
|
164
249
|
|
165
|
-
def
|
166
|
-
|
250
|
+
def runtime_above(data)
|
251
|
+
data.select { |d| d['total_runtime'] > options.runtime_above }
|
252
|
+
end
|
253
|
+
|
254
|
+
def calls_above(data)
|
255
|
+
data.select { |d| d['total_calls'] > options.calls_above }
|
256
|
+
end
|
257
|
+
|
258
|
+
def details_template_data
|
259
|
+
summary = data['profiling']['summary']
|
167
260
|
|
168
|
-
[
|
169
|
-
summary['total_runtime'].round(
|
170
|
-
summary['db_runtime'].round(
|
171
|
-
summary['view_runtime'].round(
|
261
|
+
template_data = [
|
262
|
+
summary['total_runtime'] ? summary['total_runtime'].round(rounding) : 'n/a',
|
263
|
+
summary['db_runtime'] ? summary['db_runtime'].round(rounding) : 'n/a',
|
264
|
+
summary['view_runtime'] ? summary['view_runtime'].round(rounding) : 'n/a'
|
265
|
+
]
|
172
266
|
|
267
|
+
methods_data = [
|
173
268
|
summary['slowest_method']['method'],
|
174
|
-
summary['slowest_method']['total_runtime'].round(
|
269
|
+
summary['slowest_method']['total_runtime'].round(rounding),
|
175
270
|
|
176
271
|
summary['mostly_called_method']['method'],
|
177
272
|
summary['mostly_called_method']['total_calls'],
|
178
|
-
summary['mostly_called_method']['total_runtime'].round(
|
273
|
+
summary['mostly_called_method']['total_runtime'].round(rounding)
|
274
|
+
]
|
179
275
|
|
276
|
+
sqls_data = [
|
180
277
|
summary['total_sql_calls'],
|
181
278
|
summary['total_unique_sql_calls'],
|
182
279
|
|
183
|
-
summary['slowest_sql']['total_runtime'].round(
|
280
|
+
summary['slowest_sql']['total_runtime'].round(rounding),
|
184
281
|
summary['slowest_sql']['sql'],
|
185
282
|
summary['slowest_sql']['source'],
|
186
283
|
|
187
284
|
summary['mostly_called_sql']['total_calls'],
|
188
|
-
summary['mostly_called_sql']['total_runtime'].round(
|
285
|
+
summary['mostly_called_sql']['total_runtime'].round(rounding),
|
189
286
|
summary['mostly_called_sql']['sql'],
|
190
287
|
summary['mostly_called_sql']['runtimes'].map { |runtime| runtime[1] }.uniq
|
191
288
|
]
|
289
|
+
|
290
|
+
if only_methods?
|
291
|
+
template_data.concat(methods_data)
|
292
|
+
elsif only_sqls?
|
293
|
+
template_data.concat(sqls_data)
|
294
|
+
else
|
295
|
+
template_data
|
296
|
+
.concat(methods_data)
|
297
|
+
.concat(sqls_data)
|
298
|
+
end
|
192
299
|
end
|
193
300
|
end
|
194
301
|
end
|
data/rp_view_command.png
ADDED
Binary file
|
data/runtime_profiler.gemspec
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require 'runtime_profiler/version'
|
5
4
|
|
@@ -19,15 +18,16 @@ Gem::Specification.new do |spec|
|
|
19
18
|
spec.executables = ['runtime_profiler']
|
20
19
|
spec.require_paths = ['lib']
|
21
20
|
|
22
|
-
spec.add_development_dependency 'bundler', '~> 1.12'
|
23
|
-
spec.add_development_dependency 'rake', '>= 12.3.3'
|
24
|
-
spec.add_development_dependency 'minitest', '~> 5.0'
|
25
21
|
spec.add_development_dependency 'activesupport', '>= 3.0.0'
|
26
|
-
spec.add_development_dependency '
|
22
|
+
spec.add_development_dependency 'bundler'
|
23
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
27
24
|
spec.add_development_dependency 'minitest-line'
|
28
|
-
spec.
|
25
|
+
spec.add_development_dependency 'pry'
|
26
|
+
spec.add_development_dependency 'rake', '>= 12.3.3'
|
27
|
+
spec.add_development_dependency 'rubocop'
|
29
28
|
spec.add_runtime_dependency 'commander'
|
29
|
+
spec.add_runtime_dependency 'defined_methods'
|
30
30
|
spec.add_runtime_dependency 'hirb'
|
31
31
|
spec.add_runtime_dependency 'method_meter'
|
32
|
-
spec.add_runtime_dependency '
|
32
|
+
spec.add_runtime_dependency 'terminal-table'
|
33
33
|
end
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: runtime_profiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wilfrido T. Nuqui Jr.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 3.0.0
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 3.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,19 +53,19 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '5.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: minitest-line
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: pry
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,7 +81,21 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 12.3.3
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 12.3.3
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
86
100
|
requirements:
|
87
101
|
- - ">="
|
@@ -95,7 +109,7 @@ dependencies:
|
|
95
109
|
- !ruby/object:Gem::Version
|
96
110
|
version: '0'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
112
|
+
name: commander
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
100
114
|
requirements:
|
101
115
|
- - ">="
|
@@ -109,7 +123,7 @@ dependencies:
|
|
109
123
|
- !ruby/object:Gem::Version
|
110
124
|
version: '0'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
126
|
+
name: defined_methods
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
114
128
|
requirements:
|
115
129
|
- - ">="
|
@@ -151,7 +165,7 @@ dependencies:
|
|
151
165
|
- !ruby/object:Gem::Version
|
152
166
|
version: '0'
|
153
167
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
168
|
+
name: terminal-table
|
155
169
|
requirement: !ruby/object:Gem::Requirement
|
156
170
|
requirements:
|
157
171
|
- - ">="
|
@@ -173,6 +187,7 @@ extensions: []
|
|
173
187
|
extra_rdoc_files: []
|
174
188
|
files:
|
175
189
|
- ".gitignore"
|
190
|
+
- ".semaphore/semaphore.yml"
|
176
191
|
- Gemfile
|
177
192
|
- LICENSE.txt
|
178
193
|
- README.md
|
@@ -184,12 +199,13 @@ files:
|
|
184
199
|
- lib/runtime_profiler/callbacks/action_controller.rb
|
185
200
|
- lib/runtime_profiler/callbacks/active_record.rb
|
186
201
|
- lib/runtime_profiler/cli.rb
|
202
|
+
- lib/runtime_profiler/data.rb
|
187
203
|
- lib/runtime_profiler/events/process_action_event.rb
|
188
204
|
- lib/runtime_profiler/events/sql_event.rb
|
189
|
-
- lib/runtime_profiler/instrumentation_data.rb
|
190
205
|
- lib/runtime_profiler/profiler.rb
|
191
206
|
- lib/runtime_profiler/text_report.rb
|
192
207
|
- lib/runtime_profiler/version.rb
|
208
|
+
- rp_view_command.png
|
193
209
|
- runtime_profiler.gemspec
|
194
210
|
homepage: http://www.github.com/wnuqui/runtime_profiler
|
195
211
|
licenses:
|