runtime_profiler 0.3.0 → 0.4.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7839cd9d608c73c35a269e4bf7f2f52ae482e714dd99a108e9078e6628f9a86b
4
- data.tar.gz: 9eecf865436dcd07b575d96c1bfb21d1ba1c9344c9100aa955ed00a84d2e8011
3
+ metadata.gz: c65d8e5bf6fc5d9c3a429de889693927953c5194ae5a3562c76f2eed92f5ab09
4
+ data.tar.gz: 3067f05a450557a11c1a8c44d0573fae93fdef06b7daecde1b7c1ed1a6bfd6be
5
5
  SHA512:
6
- metadata.gz: eaeedf57a5c02b27e1f75778290f821a4b0ed3901220c7dce8529df44d4bf49fe8d0846a4bb3e7e73b485c96196f85d7d57a6cc3e8df93bfda8b05676526ddc1
7
- data.tar.gz: 196e71a0244d7d47c8af6585213c45373dcc8d694bac70daa86e9688877d4b02fc029bc50df87baf3ce288e64c8c7850ea717603975eb38a9da367e34998d9a5
6
+ metadata.gz: 6712090d8b730c59d6250a12391962233b759dfeca38713b7cfe5bb6d714d6e99e38bef7b47262c30720b68f543b34e227d6e8cee73d1076fe68aba476755d3b
7
+ data.tar.gz: 454623783d6444cac238637b86f45e741479eeb1295f5ac246a003ac91a5b8f7b7be8a614fff1123f7c7c3e54bf077df8f5787173bcda1f7aac984fd74627ce8
data/README.md CHANGED
@@ -1,16 +1,36 @@
1
- # runtime_profiler - Runtime Profiler for Rails Applications [![Build Status](https://wnuqui.semaphoreci.com/badges/runtime_profiler/branches/master.svg?style=shields)](https://wnuqui.semaphoreci.com/projects/runtime_profiler)
1
+ # runtime_profiler
2
2
 
3
- `runtime_profiler` instruments API endpoints or methods in your Rails application using Rails' `ActiveSupport::Notifications`. Currently, it is intended to be used during development or test.
3
+ *A runtime profiler for Rails applications.*
4
4
 
5
- It then aggregates and generates report to give you insights about specific calls in your Rails application.
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
- ## Installation
7
+ [![Build Status](https://wnuqui.semaphoreci.com/badges/runtime_profiler/branches/master.svg?style=shields)](https://wnuqui.semaphoreci.com/projects/runtime_profiler)
8
+
9
+ ## Table of contents
10
+
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
8
28
 
9
29
  Add this line to your application's Gemfile:
10
30
 
11
31
  ```ruby
32
+ # In your Gemfile
12
33
  group :development, :test do
13
- ... ...
14
34
  gem 'runtime_profiler'
15
35
  end
16
36
  ```
@@ -19,13 +39,33 @@ And then execute:
19
39
 
20
40
  $ bundle
21
41
 
22
- ## Profiling/Instrumenting
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).
23
51
 
24
- To start profiling, you can make a test that targets a particular endpoint and use `RuntimeProfiler.profile!` method in the test. The output of instrumentation will be generated under the `tmp` folder of your application.
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
62
+
63
+ You can make a test that targets a particular endpoint (or even just a method) and use `RuntimeProfiler.profile!` method in the test.
25
64
 
26
- Example of a test code wrap by `RuntimeProfiler.profile!` method:
27
65
  ```ruby
28
66
  it 'updates user' do
67
+ # Profiles runtime of PUT /users/:id endpoint and
68
+ # specifically interested with the methods of `User` model.
29
69
  RuntimeProfiler.profile!('updates user', [User]) {
30
70
  patch :update, { id: user.id, name: 'Joe' }
31
71
  }
@@ -36,25 +76,26 @@ end
36
76
 
37
77
  Run the test as usual and follow printed instructions after running.
38
78
 
39
- If you prefer writing just code snippet, then just wrap the snippet with `RuntimeProfiler.profile!` method:
79
+ Or if you prefer writing just code snippet, then just wrap the snippet with `RuntimeProfiler.profile!` method:
40
80
  ```ruby
81
+ # Profiles runtime of `UserMailer` mailer.
41
82
  RuntimeProfiler.profile!('UserMailer', [UserMailer]) {
42
83
  user = User.last
43
84
  UserMailer.with(user: user).weekly_summary.deliver_now
44
85
  }
45
86
  ```
46
87
 
47
- **Note:** The code (tests or not) where `RuntimeProfiler.profile!` is used must be **free from any mocking** since your goal is to check bottlenecks.
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.
48
89
 
49
- ## Viewing Profiling Result
90
+ ### Viewing Profiling Result
50
91
 
51
- To see profiling/instrumenting report, you can open the report in browser with JSON viewer report. Or you can run the following command:
92
+ To see profiling report, you can open the report in browser with JSON viewer report. Or you can run the following command:
52
93
 
53
94
  ```bash
54
- bundle exec runtime_profiler view ~/the-rails-app/tmp/runtime-profiling-51079-1521371428.json
95
+ bundle exec runtime_profiler view tmp/rp-124094-1608308786.json
55
96
  ```
56
97
 
57
- ### view options
98
+ #### view options
58
99
 
59
100
  Here are the command line options for `runtime_profiler view` command.
60
101
 
@@ -97,14 +138,22 @@ $ bundle exec runtime_profiler view --help
97
138
  ROUNDING is integer value. Used in rounding runtimes. Default is 4.
98
139
  ```
99
140
 
100
- ## Configurations
141
+ ### Screenshot
142
+
143
+ <p align="center">
144
+ <img src="rp_view_command.png">
145
+ </p>
146
+
147
+ ### Configurations
101
148
 
102
- All the configurable variables and their defaults are listed below. These configurations can be put in the `config/initializers` folder of your Rails application.
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`.
103
150
  ```ruby
104
151
  RuntimeProfiler.output_path = File.join(Rails.root.to_s, 'tmp')
105
- RuntimeProfiler.instrumented_constants = [User]
106
- RuntimeProfiler.instrumented_paths = %w(app lib)
107
- RuntimeProfiler.instrumented_sql_commands = %w(SELECT INSERT UPDATE DELETE)
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]
108
157
  ```
109
158
 
110
159
  ## Development
@@ -1,45 +1,48 @@
1
1
  require 'active_support'
2
2
 
3
3
  require 'runtime_profiler/profiler'
4
- require 'method_meter'
5
4
 
6
5
  module RuntimeProfiler
7
6
  include ActiveSupport::Configurable
8
7
 
9
- config_accessor :instrumented_constants do
8
+ config_accessor :profiled_constants do
10
9
  []
11
10
  end
12
11
 
13
- config_accessor :instrumented_paths do
14
- %w(app lib)
12
+ config_accessor :profiled_paths do
13
+ %w[app lib]
15
14
  end
16
15
 
17
- config_accessor :instrumented_sql_commands do
18
- %w(SELECT INSERT UPDATE DELETE)
16
+ config_accessor :profiled_sql_commands do
17
+ %w[SELECT INSERT UPDATE DELETE]
19
18
  end
20
19
 
21
20
  config_accessor :output_path do
22
- if defined?(Rails) && Rails.respond_to?(:root)
21
+ if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
23
22
  File.join(Rails.root.to_s, 'tmp')
24
23
  else
25
24
  'tmp'
26
25
  end
27
26
  end
28
27
 
28
+ config_accessor :excepted_methods do
29
+ []
30
+ end
31
+
29
32
  class << self
30
33
  def configure
31
- Rails.application.eager_load! rescue nil
34
+ begin
35
+ Rails.application.eager_load!
36
+ rescue StandardError
37
+ nil
38
+ end
32
39
  yield self if block_given?
33
40
  end
34
41
 
35
- def profile!(key, konstants)
36
- konstants = konstants.is_a?(Array) ? konstants : [konstants]
37
- profiler = Profiler.new(konstants)
38
- profiler.prepare_for_instrumentation
39
-
40
- MethodMeter.measure!(key) { yield }
41
-
42
- profiler.save_instrumentation_data
42
+ def profile!(key, constants)
43
+ constants = constants.is_a?(Array) ? constants : [constants]
44
+ profiler = Profiler.new(constants)
45
+ profiler.profile!(key) { yield }
43
46
  end
44
47
  end
45
- end
48
+ end
@@ -12,6 +12,7 @@ module RuntimeProfiler
12
12
  def call(*args)
13
13
  event = RuntimeProfiler::ProcessActionEvent.new(args: args)
14
14
  return unless event.recordable?
15
+
15
16
  add event
16
17
  end
17
18
 
@@ -26,12 +26,12 @@ module RuntimeProfiler
26
26
 
27
27
  @data[key][:sql] = event.sanitized_sql
28
28
  @data[key][:runtimes] = [
29
- [ event.total_runtime, event.trace.first ]
29
+ [event.total_runtime, event.trace.first]
30
30
  ]
31
31
  end
32
32
 
33
33
  def update(event)
34
- @data[event.key][:runtimes] << [ event.total_runtime, event.trace.first ]
34
+ @data[event.key][:runtimes] << [event.total_runtime, event.trace.first]
35
35
  end
36
36
  end
37
37
  end
@@ -49,4 +49,4 @@ module RuntimeProfiler
49
49
  run!
50
50
  end
51
51
  end
52
- end
52
+ end
@@ -1,5 +1,5 @@
1
1
  module RuntimeProfiler
2
- class InstrumentationData
2
+ class Data
3
3
  attr_reader :controller_data, :sql_data
4
4
 
5
5
  def initialize(controller_data: nil, sql_data: nil)
@@ -8,14 +8,36 @@ module RuntimeProfiler
8
8
  end
9
9
 
10
10
  def persist!
11
- instrumented_api = [
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
22
+
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 ||= [
12
32
  controller_data[:payload][:controller],
13
33
  controller_data[:payload][:action]
14
- ].join('#') if controller_data[:payload]
34
+ ].join('#')
35
+ end
15
36
 
16
- instrumentation_data = {
17
- instrumentation: {
18
- instrumented_api: instrumented_api,
37
+ def profiling_data
38
+ @profiling_data ||= {
39
+ profiling: {
40
+ profiled_api: profiled_api,
19
41
  summary: {
20
42
  db_runtime: controller_data[:db_runtime],
21
43
  view_runtime: controller_data[:view_runtime],
@@ -27,59 +49,34 @@ module RuntimeProfiler
27
49
  slowest_method: method_calls_data[:slowest_method],
28
50
  mostly_called_method: method_calls_data[:mostly_called_method]
29
51
  },
30
- instrumented_sql_calls: sql_calls_data[:instrumented_sql_calls],
31
- instrumented_methods: method_calls_data[:instrumented_methods],
32
- instrumented_at: Time.now
52
+ profiled_sql_calls: sql_calls_data[:profiled_sql_calls],
53
+ profiled_methods: method_calls_data[:profiled_methods],
54
+ profiled_at: Time.now
33
55
  }
34
56
  }
35
-
36
- FileUtils.mkdir_p(RuntimeProfiler.output_path)
37
- filename = ['runtime-profiling', Process.pid, Time.now.to_i].join('-') << '.json'
38
- output_file = File.join(RuntimeProfiler.output_path, filename)
39
-
40
- File.open(output_file, 'w') do |f|
41
- f.write JSON.dump(instrumentation_data)
42
- end
43
-
44
- puts "\n"
45
- puts '~~~~> [ Profiling RUNTIME ] Profiling now COMPLETE and JSON report is written at ' + output_file.to_s
46
- puts '~~~~> [ Profiling RUNTIME ]'
47
- puts '~~~~> [ Profiling RUNTIME ] You can do the following to view the JSON report in console:'
48
- puts '~~~~> [ Profiling RUNTIME ]'
49
- puts '~~~~> [ Profiling RUNTIME ] bundle exec runtime_profiler view ' + output_file.to_s
50
- puts '~~~~> [ Profiling RUNTIME ]'
51
- puts '~~~~> [ Profiling RUNTIME ] Or'
52
- puts '~~~~> [ Profiling RUNTIME ]'
53
- puts '~~~~> [ Profiling RUNTIME ] bundle exec runtime_profiler view --help'
54
- puts '~~~~> [ Profiling RUNTIME ]'
55
- puts '~~~~> [ Profiling RUNTIME ] for more details.'
56
57
  end
57
58
 
58
- private
59
-
60
59
  def method_calls_data
61
60
  @method_calls_data ||= begin
62
- instrumented_methods = {}
61
+ profiled_methods = {}
63
62
 
64
63
  # TODO: Group methods under a key and under an object
65
- MethodMeter.measurement.each do |measurement|
66
- measurement.each_pair do |key, data|
67
- data.each do |d|
68
- object = d[:method].split(separator = '.')
69
- object = d[:method].split(separator = '#') if object.length == 1
70
-
71
- d[:method] = separator + object.second
72
-
73
- if instrumented_methods[object.first]
74
- instrumented_methods[object.first] << d
75
- else
76
- instrumented_methods[object.first] = [d]
77
- end
64
+ MethodMeter.measurement.each_pair do |key, data|
65
+ data.each do |d|
66
+ object = d[:method].split(separator = '.')
67
+ object = d[:method].split(separator = '#') if object.length == 1
68
+
69
+ d[:method] = separator + object.second
70
+
71
+ if profiled_methods[object.first]
72
+ profiled_methods[object.first] << d
73
+ else
74
+ profiled_methods[object.first] = [d]
78
75
  end
79
76
  end
80
77
  end
81
78
 
82
- instrumented_methods = instrumented_methods.inject({}) do |hash, (key, value)|
79
+ profiled_methods = profiled_methods.inject({}) do |hash, (key, value)|
83
80
  val = value.sort { |a, b| b[:total_runtime] <=> a[:total_runtime] }
84
81
  hash[key] = val
85
82
  hash
@@ -88,7 +85,7 @@ module RuntimeProfiler
88
85
  slowest_method = {total_runtime: 0}
89
86
  mostly_called_method = {total_calls: 0}
90
87
 
91
- instrumented_methods.each do |profiled_object_name, methods|
88
+ profiled_methods.each do |profiled_object_name, methods|
92
89
  # sort using `total_runtime` in DESC order
93
90
  _methods = methods.sort { |a, b| b[:total_runtime] <=> a[:total_runtime] }
94
91
  slowest = _methods[0]
@@ -111,7 +108,7 @@ module RuntimeProfiler
111
108
  end
112
109
 
113
110
  {
114
- instrumented_methods: instrumented_methods,
111
+ profiled_methods: profiled_methods,
115
112
  slowest_method: slowest_method,
116
113
  mostly_called_method: mostly_called_method
117
114
  }
@@ -120,7 +117,7 @@ module RuntimeProfiler
120
117
 
121
118
  def sql_calls_data
122
119
  @sql_calls_data ||= begin
123
- instrumented_sql_calls = []
120
+ profiled_sql_calls = []
124
121
 
125
122
  slowest_sql = {total_runtime: 0}
126
123
  mostly_called_sql = {total_calls: 0}
@@ -141,7 +138,7 @@ module RuntimeProfiler
141
138
  slowest_sql[:source] = slowest[1]
142
139
  end
143
140
 
144
- instrumented_sql_calls << {
141
+ profiled_sql_calls << {
145
142
  sql: value[:sql],
146
143
  runtimes: runtimes,
147
144
  total_calls: total_calls,
@@ -163,9 +160,9 @@ module RuntimeProfiler
163
160
  end
164
161
 
165
162
  {
166
- instrumented_sql_calls: instrumented_sql_calls.sort { |a, b| b[:max] <=> a[:max] },
167
- total_sql_calls: instrumented_sql_calls.map { |sql_call| sql_call[:total_calls] }.reduce(:+),
168
- total_unique_sql_calls: instrumented_sql_calls.size,
163
+ profiled_sql_calls: profiled_sql_calls.sort { |a, b| b[:max] <=> a[:max] },
164
+ total_sql_calls: profiled_sql_calls.map { |sql_call| sql_call[:total_calls] }.reduce(:+),
165
+ total_unique_sql_calls: profiled_sql_calls.size,
169
166
  slowest_sql: slowest_sql,
170
167
  mostly_called_sql: mostly_called_sql
171
168
  }
@@ -30,4 +30,4 @@ module RuntimeProfiler
30
30
  @key ||= Digest::MD5.hexdigest(path.downcase)
31
31
  end
32
32
  end
33
- end
33
+ end
@@ -10,8 +10,8 @@ module RuntimeProfiler
10
10
  end
11
11
 
12
12
  def recordable?
13
- return true unless RuntimeProfiler.instrumented_sql_commands.respond_to?(:join)
14
- instrumented_sql_matcher =~ sql
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 instrumented_sql_matcher
45
- @instrumented_sql_matcher ||= /\A#{RuntimeProfiler.instrumented_sql_commands.join('|')}/i
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.instrumented_paths.join('|')})\/}
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.instrumented_paths.respond_to?(:join)
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
@@ -1,55 +1,64 @@
1
- # require 'method_profiler'
1
+ require 'method_meter'
2
2
 
3
3
  require 'runtime_profiler/callbacks/active_record'
4
4
  require 'runtime_profiler/callbacks/action_controller'
5
- require 'runtime_profiler/instrumentation_data'
5
+ require 'runtime_profiler/data'
6
6
 
7
7
  module RuntimeProfiler
8
8
  class Profiler
9
- attr_accessor :instrumented_constants
9
+ attr_accessor :profiled_constants
10
10
 
11
- def initialize(konstants)
12
- self.instrumented_constants = konstants
11
+ def initialize(constants)
12
+ self.profiled_constants = constants
13
+ prepare_for_profiling
13
14
  end
14
15
 
15
- def prepare_for_instrumentation
16
- subscribe_to_event_notifications
17
- prepare_methods_to_instrument
16
+ def profile!(key)
17
+ MethodMeter.measure!(key) { yield }
18
+ save_profiling_data
18
19
  end
19
20
 
20
- def subscribe_to_event_notifications
21
+ private
22
+
23
+ def prepare_for_profiling
24
+ subscribe_to_rails_event_notifications
25
+ prepare_methods_to_profile
26
+ end
27
+
28
+ def subscribe_to_rails_event_notifications
21
29
  @subscribers = []
22
30
 
23
31
  @active_record_callback = Callback::ActiveRecord.new
24
32
 
25
33
  @subscribers << ActiveSupport::Notifications
26
- .subscribe('sql.active_record', @active_record_callback)
34
+ .subscribe('sql.active_record', @active_record_callback)
27
35
 
28
36
  @action_controller_callback = Callback::ActionController.new
29
37
 
30
38
  @subscribers << ActiveSupport::Notifications
31
- .subscribe('process_action.action_controller', @action_controller_callback)
39
+ .subscribe('process_action.action_controller', @action_controller_callback)
32
40
  end
33
41
 
34
- def unsubscribe_to_event_notifications
42
+ def unsubscribe_to_rails_event_notifications
35
43
  @subscribers.each do |subscriber|
36
44
  ActiveSupport::Notifications.unsubscribe(subscriber)
37
45
  end
38
46
  end
39
47
 
40
- def prepare_methods_to_instrument
41
- self.instrumented_constants.flatten
42
- .each { |constant| MethodMeter.observe(constant) }
48
+ def prepare_methods_to_profile
49
+ profiled_constants
50
+ .flatten
51
+ .each { |constant| MethodMeter.observe(constant, RuntimeProfiler.excepted_methods) }
43
52
  end
44
53
 
45
- def save_instrumentation_data
46
- unsubscribe_to_event_notifications
54
+ def save_profiling_data
55
+ unsubscribe_to_rails_event_notifications
47
56
 
48
- instrumentation_data = RuntimeProfiler::InstrumentationData.new \
49
- controller_data: @action_controller_callback.controller_data,
50
- sql_data: @active_record_callback.data
57
+ profiling_data = RuntimeProfiler::Data.new \
58
+ controller_data: @action_controller_callback.controller_data,
59
+ sql_data: @active_record_callback.data
51
60
 
52
- instrumentation_data.persist!
61
+ profiling_data.persist!
53
62
  end
54
63
  end
55
- end
64
+ end
@@ -26,12 +26,12 @@ module RuntimeProfiler
26
26
  \e[1mSQL CALLS\e[22m
27
27
  Total : %s
28
28
  Total Unique : %s
29
-
29
+
30
30
  \e[1mSLOWEST\e[22m
31
31
  Total Runtime : %s ms
32
32
  SQL : %s
33
33
  Source : %s
34
-
34
+
35
35
  \e[1mMOSTLY CALLED\e[22m
36
36
  Total Calls : %s
37
37
  Total Runtime : %s ms
@@ -86,7 +86,7 @@ module RuntimeProfiler
86
86
  attr_accessor :data, :options
87
87
 
88
88
  def initialize(json_file, options)
89
- self.data = JSON.parse( File.read(json_file) )
89
+ self.data = JSON.parse(File.read(json_file))
90
90
  self.options = options
91
91
  end
92
92
 
@@ -95,12 +95,12 @@ module RuntimeProfiler
95
95
 
96
96
  if options.details == 'full'
97
97
  if only_methods?
98
- print_instrumented_methods
98
+ print_profiled_methods
99
99
  elsif only_sqls?
100
- print_instrumented_sql_calls
100
+ print_profiled_sql_calls
101
101
  else
102
- print_instrumented_methods
103
- print_instrumented_sql_calls
102
+ print_profiled_methods
103
+ print_profiled_sql_calls
104
104
  end
105
105
  end
106
106
  end
@@ -121,34 +121,34 @@ module RuntimeProfiler
121
121
 
122
122
  def print_summary
123
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
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
130
130
  puts summary
131
131
  end
132
132
 
133
- def print_instrumented_methods
134
- instrumented_methods = []
133
+ def print_profiled_methods
134
+ profiled_methods = []
135
135
 
136
- data['instrumentation']['instrumented_methods'].each do |profiled_object_name, methods|
137
- _instrumented_methods = methods.map do |method|
136
+ data['profiling']['profiled_methods'].each do |profiled_object_name, methods|
137
+ _profiled_methods = methods.map do |method|
138
138
  method['method'] = [profiled_object_name, method['method']].join
139
139
  method
140
140
  end
141
- instrumented_methods.concat(_instrumented_methods)
141
+ profiled_methods.concat(_profiled_methods)
142
142
  end
143
143
 
144
- instrumented_methods = runtime_above(instrumented_methods) if options.runtime_above.presence > 0
145
- instrumented_methods = calls_above(instrumented_methods) if options.calls_above.presence > 0
146
- instrumented_methods = sort(instrumented_methods)
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)
147
147
 
148
148
  table = Terminal::Table.new do |t|
149
149
  t.headings = ['Method', 'Total Runtime (ms)', 'Total Calls', 'Min (ms)', 'Max (ms)']
150
150
 
151
- instrumented_methods.each_with_index do |row, index|
151
+ profiled_methods.each_with_index do |row, index|
152
152
  t.add_row [
153
153
  row['method'],
154
154
  row['total_runtime'].round(rounding),
@@ -156,29 +156,28 @@ module RuntimeProfiler
156
156
  row['min'].round(rounding),
157
157
  row['max'].round(rounding)
158
158
  ]
159
- t.add_separator if index < instrumented_methods.size - 1
159
+ t.add_separator if index < profiled_methods.size - 1
160
160
  end
161
-
162
161
  end
163
162
 
164
163
  puts
165
164
  puts
166
- puts "\e[1mINSTRUMENTED METHOD(s)\e[22m"
165
+ puts "\e[1mPROFILED METHOD(s)\e[22m"
167
166
  puts
168
167
  puts table
169
168
  end
170
169
 
171
- def print_instrumented_sql_calls
172
- instrumented_sql_calls = data['instrumentation']['instrumented_sql_calls']
170
+ def print_profiled_sql_calls
171
+ profiled_sql_calls = data['profiling']['profiled_sql_calls']
173
172
 
174
- instrumented_sql_calls = runtime_above(instrumented_sql_calls) if options.runtime_above.presence > 0
175
- instrumented_sql_calls = calls_above(instrumented_sql_calls) if options.calls_above.presence > 0
176
- instrumented_sql_calls = sort(instrumented_sql_calls, false)
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)
177
176
 
178
177
  table = Terminal::Table.new do |t|
179
- t.headings = ['Count', 'Total Runtime (ms)', 'Average Runtime (ms)', 'SQL Query', 'Source']
178
+ t.headings = ['SQL Query', 'Count', 'Total Runtime (ms)', 'Average Runtime (ms)', 'Source']
180
179
 
181
- instrumented_sql_calls.each_with_index do |row, index|
180
+ profiled_sql_calls.each_with_index do |row, index|
182
181
  chopped_sql = wrap_text(row['sql'], sql_width)
183
182
  source_list = wrap_list(row['runtimes'].map { |runtime| runtime[1] }.uniq, sql_width - 15)
184
183
  average_runtime = row['average'].round(rounding)
@@ -192,29 +191,29 @@ module RuntimeProfiler
192
191
  (0...total_lines).each do |line|
193
192
  count = line == 0 ? row['total_calls'] : ''
194
193
  average = line == 0 ? average_runtime : ''
195
- total_runtime = line == 0 ? total_runtime : ''
194
+ total_runtime = line == 0 ? total_runtime : ''
196
195
  source = source_list.length > line ? source_list[line] : ''
197
196
  query = row['sql'].length > line ? chopped_sql[line] : ''
198
197
 
199
198
  t.add_row []
200
- t.add_row [count, total_runtime, average, query, source]
199
+ t.add_row [query, count, total_runtime, average, source]
201
200
  end
202
201
 
203
202
  t.add_row []
204
- t.add_separator if index < instrumented_sql_calls.size - 1
203
+ t.add_separator if index < profiled_sql_calls.size - 1
205
204
  end
206
-
207
205
  end
208
206
 
209
207
  puts
210
208
  puts
211
- puts "\e[1mINSTRUMENTED SQL(s)\e[22m"
209
+ puts "\e[1mPROFILED SQL(s)\e[22m"
212
210
  puts
213
211
  puts table
214
212
  end
215
213
 
216
214
  def wrap_text(text, width)
217
215
  return [text] if text.length <= width
216
+
218
217
  text.scan(/.{1,#{width}}/)
219
218
  end
220
219
 
@@ -231,7 +230,7 @@ module RuntimeProfiler
231
230
  end
232
231
  end
233
232
 
234
- def sort(data, methods=true)
233
+ def sort(data, methods = true)
235
234
  if methods
236
235
  data.sort_by do |d|
237
236
  if options.sort_by == 'max_runtime'
@@ -243,8 +242,8 @@ module RuntimeProfiler
243
242
  end
244
243
  end
245
244
  else
246
- options.sort_by = 'total_runtime' if options.sort_by == 'max_runtime'
247
- data.sort_by { |d| options.sort_by == 'total_runtime' ? -d['total_runtime'] : -d['total_calls'] }
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'] }
248
247
  end
249
248
  end
250
249
 
@@ -257,7 +256,7 @@ module RuntimeProfiler
257
256
  end
258
257
 
259
258
  def details_template_data
260
- summary = data['instrumentation']['summary']
259
+ summary = data['profiling']['summary']
261
260
 
262
261
  template_data = [
263
262
  summary['total_runtime'] ? summary['total_runtime'].round(rounding) : 'n/a',
@@ -1,3 +1,3 @@
1
1
  module RuntimeProfiler
2
- VERSION = '0.3.0'.freeze
2
+ VERSION = '0.4.4'.freeze
3
3
  end
Binary file
@@ -1,5 +1,4 @@
1
- # coding: utf-8
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
 
21
+ spec.add_development_dependency 'activesupport', '>= 3.0.0'
22
22
  spec.add_development_dependency 'bundler'
23
- spec.add_development_dependency 'rake', '>= 12.3.3'
24
23
  spec.add_development_dependency 'minitest', '~> 5.0'
25
- spec.add_development_dependency 'activesupport', '>= 3.0.0'
26
- spec.add_development_dependency 'pry'
27
24
  spec.add_development_dependency 'minitest-line'
28
- spec.add_runtime_dependency 'terminal-table'
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'
30
- spec.add_runtime_dependency 'hirb'
31
- spec.add_runtime_dependency 'method_meter'
32
29
  spec.add_runtime_dependency 'defined_methods'
30
+ spec.add_runtime_dependency 'hirb'
31
+ spec.add_runtime_dependency 'method_meter', '>= 0.4.3'
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.3.0
4
+ version: 0.4.4
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: 2020-12-04 00:00:00.000000000 Z
11
+ date: 2021-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
14
+ name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
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: '0'
26
+ version: 3.0.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 12.3.3
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: 12.3.3
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: activesupport
56
+ name: minitest-line
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: 3.0.0
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: 3.0.0
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: minitest-line
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: terminal-table
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: commander
126
+ name: defined_methods
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
129
  - - ">="
@@ -142,16 +156,16 @@ dependencies:
142
156
  requirements:
143
157
  - - ">="
144
158
  - !ruby/object:Gem::Version
145
- version: '0'
159
+ version: 0.4.3
146
160
  type: :runtime
147
161
  prerelease: false
148
162
  version_requirements: !ruby/object:Gem::Requirement
149
163
  requirements:
150
164
  - - ">="
151
165
  - !ruby/object:Gem::Version
152
- version: '0'
166
+ version: 0.4.3
153
167
  - !ruby/object:Gem::Dependency
154
- name: defined_methods
168
+ name: terminal-table
155
169
  requirement: !ruby/object:Gem::Requirement
156
170
  requirements:
157
171
  - - ">="
@@ -185,12 +199,13 @@ files:
185
199
  - lib/runtime_profiler/callbacks/action_controller.rb
186
200
  - lib/runtime_profiler/callbacks/active_record.rb
187
201
  - lib/runtime_profiler/cli.rb
202
+ - lib/runtime_profiler/data.rb
188
203
  - lib/runtime_profiler/events/process_action_event.rb
189
204
  - lib/runtime_profiler/events/sql_event.rb
190
- - lib/runtime_profiler/instrumentation_data.rb
191
205
  - lib/runtime_profiler/profiler.rb
192
206
  - lib/runtime_profiler/text_report.rb
193
207
  - lib/runtime_profiler/version.rb
208
+ - rp_view_command.png
194
209
  - runtime_profiler.gemspec
195
210
  homepage: http://www.github.com/wnuqui/runtime_profiler
196
211
  licenses: