runtime_profiler 0.3.0 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
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: