piko-sharp-kit 0.0.1

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.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/piko-sharp-kit.gemspec +11 -0
  3. data/rack-mini-profiler-4.0.1/CHANGELOG.md +424 -0
  4. data/rack-mini-profiler-4.0.1/README.md +542 -0
  5. data/rack-mini-profiler-4.0.1/lib/enable_rails_patches.rb +5 -0
  6. data/rack-mini-profiler-4.0.1/lib/generators/rack_mini_profiler/USAGE +9 -0
  7. data/rack-mini-profiler-4.0.1/lib/generators/rack_mini_profiler/install_generator.rb +13 -0
  8. data/rack-mini-profiler-4.0.1/lib/generators/rack_mini_profiler/templates/rack_mini_profiler.rb +8 -0
  9. data/rack-mini-profiler-4.0.1/lib/generators/rack_profiler/USAGE +2 -0
  10. data/rack-mini-profiler-4.0.1/lib/generators/rack_profiler/install_generator.rb +16 -0
  11. data/rack-mini-profiler-4.0.1/lib/html/dot.1.1.2.min.js +2 -0
  12. data/rack-mini-profiler-4.0.1/lib/html/includes.css +474 -0
  13. data/rack-mini-profiler-4.0.1/lib/html/includes.js +1520 -0
  14. data/rack-mini-profiler-4.0.1/lib/html/includes.scss +698 -0
  15. data/rack-mini-profiler-4.0.1/lib/html/includes.tmpl +333 -0
  16. data/rack-mini-profiler-4.0.1/lib/html/pretty-print.js +810 -0
  17. data/rack-mini-profiler-4.0.1/lib/html/profile_handler.js +1 -0
  18. data/rack-mini-profiler-4.0.1/lib/html/rack-mini-profiler.css +3 -0
  19. data/rack-mini-profiler-4.0.1/lib/html/rack-mini-profiler.js +2 -0
  20. data/rack-mini-profiler-4.0.1/lib/html/share.html +10 -0
  21. data/rack-mini-profiler-4.0.1/lib/html/speedscope/LICENSE +21 -0
  22. data/rack-mini-profiler-4.0.1/lib/html/speedscope/README.md +3 -0
  23. data/rack-mini-profiler-4.0.1/lib/html/speedscope/demangle-cpp.1768f4cc.js +4 -0
  24. data/rack-mini-profiler-4.0.1/lib/html/speedscope/favicon-16x16.f74b3187.png +0 -0
  25. data/rack-mini-profiler-4.0.1/lib/html/speedscope/favicon-32x32.bc503437.png +0 -0
  26. data/rack-mini-profiler-4.0.1/lib/html/speedscope/file-format-schema.json +324 -0
  27. data/rack-mini-profiler-4.0.1/lib/html/speedscope/fonts/source-code-pro-regular.css +8 -0
  28. data/rack-mini-profiler-4.0.1/lib/html/speedscope/fonts/source-code-pro-v13-regular.woff +0 -0
  29. data/rack-mini-profiler-4.0.1/lib/html/speedscope/fonts/source-code-pro-v13-regular.woff2 +0 -0
  30. data/rack-mini-profiler-4.0.1/lib/html/speedscope/import.8ae8aa3d.js +123 -0
  31. data/rack-mini-profiler-4.0.1/lib/html/speedscope/index.html +2 -0
  32. data/rack-mini-profiler-4.0.1/lib/html/speedscope/release.txt +3 -0
  33. data/rack-mini-profiler-4.0.1/lib/html/speedscope/reset.8c46b7a1.css +2 -0
  34. data/rack-mini-profiler-4.0.1/lib/html/speedscope/source-map.438fa06b.js +24 -0
  35. data/rack-mini-profiler-4.0.1/lib/html/speedscope/speedscope.f27db165.js +193 -0
  36. data/rack-mini-profiler-4.0.1/lib/html/vendor.js +848 -0
  37. data/rack-mini-profiler-4.0.1/lib/mini_profiler/actions.rb +157 -0
  38. data/rack-mini-profiler-4.0.1/lib/mini_profiler/asset_version.rb +6 -0
  39. data/rack-mini-profiler-4.0.1/lib/mini_profiler/client_settings.rb +127 -0
  40. data/rack-mini-profiler-4.0.1/lib/mini_profiler/config.rb +146 -0
  41. data/rack-mini-profiler-4.0.1/lib/mini_profiler/context.rb +14 -0
  42. data/rack-mini-profiler-4.0.1/lib/mini_profiler/gc_profiler.rb +158 -0
  43. data/rack-mini-profiler-4.0.1/lib/mini_profiler/profiling_methods.rb +167 -0
  44. data/rack-mini-profiler-4.0.1/lib/mini_profiler/snapshots_transporter.rb +111 -0
  45. data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/abstract_store.rb +97 -0
  46. data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/file_store.rb +179 -0
  47. data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/memcache_store.rb +105 -0
  48. data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/memory_store.rb +233 -0
  49. data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/redis_store.rb +353 -0
  50. data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage.rb +7 -0
  51. data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/base.rb +39 -0
  52. data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/client.rb +90 -0
  53. data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/custom.rb +25 -0
  54. data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/page.rb +146 -0
  55. data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/request.rb +186 -0
  56. data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/sql.rb +89 -0
  57. data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct.rb +8 -0
  58. data/rack-mini-profiler-4.0.1/lib/mini_profiler/version.rb +8 -0
  59. data/rack-mini-profiler-4.0.1/lib/mini_profiler/views.rb +198 -0
  60. data/rack-mini-profiler-4.0.1/lib/mini_profiler.rb +670 -0
  61. data/rack-mini-profiler-4.0.1/lib/mini_profiler_rails/railtie.rb +234 -0
  62. data/rack-mini-profiler-4.0.1/lib/mini_profiler_rails/railtie_methods.rb +61 -0
  63. data/rack-mini-profiler-4.0.1/lib/patches/db/activerecord.rb +44 -0
  64. data/rack-mini-profiler-4.0.1/lib/patches/db/mongo.rb +18 -0
  65. data/rack-mini-profiler-4.0.1/lib/patches/db/moped.rb +14 -0
  66. data/rack-mini-profiler-4.0.1/lib/patches/db/mysql2/alias_method.rb +30 -0
  67. data/rack-mini-profiler-4.0.1/lib/patches/db/mysql2/prepend.rb +34 -0
  68. data/rack-mini-profiler-4.0.1/lib/patches/db/mysql2.rb +7 -0
  69. data/rack-mini-profiler-4.0.1/lib/patches/db/neo4j.rb +16 -0
  70. data/rack-mini-profiler-4.0.1/lib/patches/db/nobrainer.rb +31 -0
  71. data/rack-mini-profiler-4.0.1/lib/patches/db/oracle_enhanced.rb +70 -0
  72. data/rack-mini-profiler-4.0.1/lib/patches/db/pg/alias_method.rb +121 -0
  73. data/rack-mini-profiler-4.0.1/lib/patches/db/pg/prepend.rb +115 -0
  74. data/rack-mini-profiler-4.0.1/lib/patches/db/pg.rb +7 -0
  75. data/rack-mini-profiler-4.0.1/lib/patches/db/plucky.rb +49 -0
  76. data/rack-mini-profiler-4.0.1/lib/patches/db/riak.rb +105 -0
  77. data/rack-mini-profiler-4.0.1/lib/patches/db/rsolr.rb +26 -0
  78. data/rack-mini-profiler-4.0.1/lib/patches/db/sequel.rb +12 -0
  79. data/rack-mini-profiler-4.0.1/lib/patches/net_patches.rb +27 -0
  80. data/rack-mini-profiler-4.0.1/lib/patches/sql_patches.rb +74 -0
  81. data/rack-mini-profiler-4.0.1/lib/prepend_mysql2_patch.rb +5 -0
  82. data/rack-mini-profiler-4.0.1/lib/prepend_net_http_patch.rb +5 -0
  83. data/rack-mini-profiler-4.0.1/lib/prepend_pg_patch.rb +5 -0
  84. data/rack-mini-profiler-4.0.1/lib/rack-mini-profiler.rb +13 -0
  85. data/rack-mini-profiler-4.0.1/rack-mini-profiler.gemspec +49 -0
  86. metadata +124 -0
@@ -0,0 +1,670 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+ require 'json'
5
+ require 'erb'
6
+
7
+ require 'mini_profiler/timer_struct'
8
+ require 'mini_profiler/storage'
9
+ require 'mini_profiler/config'
10
+ require 'mini_profiler/profiling_methods'
11
+ require 'mini_profiler/context'
12
+ require 'mini_profiler/client_settings'
13
+ require 'mini_profiler/gc_profiler'
14
+ require 'mini_profiler/snapshots_transporter'
15
+ require 'mini_profiler/views'
16
+ require 'mini_profiler/actions'
17
+
18
+ module Rack
19
+ class MiniProfiler
20
+ include Actions
21
+ include Views
22
+
23
+ class << self
24
+ include Rack::MiniProfiler::ProfilingMethods
25
+ attr_accessor :subscribe_sql_active_record
26
+
27
+ def patch_rails?
28
+ !!defined?(Rack::MINI_PROFILER_ENABLE_RAILS_PATCHES)
29
+ end
30
+
31
+ def generate_id
32
+ rand(36**20).to_s(36)
33
+ end
34
+
35
+ def reset_config
36
+ @config = Config.default
37
+ end
38
+
39
+ # So we can change the configuration if we want
40
+ def config
41
+ @config ||= Config.default
42
+ end
43
+
44
+ def current
45
+ Thread.current[:mini_profiler_private]
46
+ end
47
+
48
+ def current=(c)
49
+ # we use TLS cause we need access to this from sql blocks and code blocks that have no access to env
50
+ Thread.current[:mini_profiler_snapshot_custom_fields] = nil
51
+ Thread.current[:mp_ongoing_snapshot] = nil
52
+ Thread.current[:mini_profiler_private] = c
53
+ end
54
+
55
+ def add_snapshot_custom_field(key, value)
56
+ thread_var_key = :mini_profiler_snapshot_custom_fields
57
+ Thread.current[thread_var_key] ||= {}
58
+ Thread.current[thread_var_key][key] = value
59
+ end
60
+
61
+ def get_snapshot_custom_fields
62
+ Thread.current[:mini_profiler_snapshot_custom_fields]
63
+ end
64
+
65
+ # discard existing results, don't track this request
66
+ def discard_results
67
+ self.current.discard = true if current
68
+ end
69
+
70
+ def create_current(env = {}, options = {})
71
+ # profiling the request
72
+ context = Context.new
73
+ context.inject_js = config.auto_inject && (!env['HTTP_X_REQUESTED_WITH'].eql? 'XMLHttpRequest')
74
+ context.page_struct = TimerStruct::Page.new(env)
75
+ context.current_timer = context.page_struct[:root]
76
+ self.current = context
77
+ end
78
+
79
+ def authorize_request
80
+ Thread.current[:mp_authorized] = true
81
+ end
82
+
83
+ def deauthorize_request
84
+ Thread.current[:mp_authorized] = nil
85
+ end
86
+
87
+ def request_authorized?
88
+ Thread.current[:mp_authorized]
89
+ end
90
+
91
+ def advanced_tools_message
92
+ <<~TEXT
93
+ This feature is disabled by default, to enable set the enable_advanced_debugging_tools option to true in Mini Profiler config.
94
+ TEXT
95
+ end
96
+
97
+ def binds_to_params(binds)
98
+ return if binds.nil? || config.max_sql_param_length == 0
99
+ # map ActiveRecord::Relation::QueryAttribute to [name, value]
100
+ params = binds.map { |c| c.kind_of?(Array) ? [c.first, c.last] : [c.name, c.value] }
101
+ if (skip = config.skip_sql_param_names)
102
+ params.map { |(n, v)| n =~ skip ? [n, nil] : [n, v] }
103
+ else
104
+ params
105
+ end
106
+ end
107
+
108
+ def snapshots_transporter?
109
+ !!config.snapshots_transport_destination_url &&
110
+ !!config.snapshots_transport_auth_key
111
+ end
112
+
113
+ def redact_sql_queries?
114
+ Thread.current[:mp_ongoing_snapshot] == true &&
115
+ Rack::MiniProfiler.config.snapshots_redact_sql_queries
116
+ end
117
+ end
118
+
119
+ #
120
+ # options:
121
+ # :auto_inject - should script be automatically injected on every html page (not xhr)
122
+ def initialize(app, config = nil)
123
+ MiniProfiler.config.merge!(config)
124
+ @config = MiniProfiler.config
125
+ @app = app
126
+ @config.base_url_path += "/" unless @config.base_url_path.end_with? "/"
127
+ unless @config.storage_instance
128
+ @config.storage_instance = @config.storage.new(@config.storage_options)
129
+ end
130
+ @storage = @config.storage_instance
131
+ end
132
+
133
+ def user(env)
134
+ @config.user_provider.call(env)
135
+ end
136
+
137
+ def current
138
+ MiniProfiler.current
139
+ end
140
+
141
+ def current=(c)
142
+ MiniProfiler.current = c
143
+ end
144
+
145
+ def config
146
+ @config
147
+ end
148
+
149
+ def advanced_debugging_enabled?
150
+ config.enable_advanced_debugging_tools
151
+ end
152
+
153
+ def tool_disabled_message(client_settings)
154
+ client_settings.handle_cookie(text_result(Rack::MiniProfiler.advanced_tools_message))
155
+ end
156
+
157
+ def call(env)
158
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
159
+ client_settings = ClientSettings.new(env, @storage, start)
160
+ MiniProfiler.deauthorize_request if @config.authorization_mode == :allow_authorized
161
+
162
+ status = headers = body = nil
163
+ path = env['PATH_INFO'].sub('//', '/')
164
+
165
+ # Someone (e.g. Rails engine) could change the SCRIPT_NAME so we save it
166
+ env['RACK_MINI_PROFILER_ORIGINAL_SCRIPT_NAME'] = ENV['PASSENGER_BASE_URI'] || env['SCRIPT_NAME']
167
+
168
+ skip_it = matches_action?('skip', env) || (
169
+ @config.skip_paths &&
170
+ @config.skip_paths.any? do |p|
171
+ if p.instance_of?(String)
172
+ path.start_with?(p)
173
+ elsif p.instance_of?(Regexp)
174
+ p.match?(path)
175
+ end
176
+ end
177
+ )
178
+ if skip_it
179
+ return client_settings.handle_cookie(@app.call(env))
180
+ end
181
+
182
+ skip_it = (@config.pre_authorize_cb && !@config.pre_authorize_cb.call(env))
183
+
184
+ if skip_it || (
185
+ @config.authorization_mode == :allow_authorized &&
186
+ !client_settings.has_valid_cookie?
187
+ )
188
+ if take_snapshot?(path)
189
+ return client_settings.handle_cookie(take_snapshot(env, start))
190
+ else
191
+ return client_settings.handle_cookie(@app.call(env))
192
+ end
193
+ end
194
+
195
+ # handle all /mini-profiler requests here
196
+ if path.start_with? @config.base_url_path
197
+ file_name = path.sub(@config.base_url_path, '')
198
+
199
+ case file_name
200
+ when 'results'
201
+ return serve_results(env)
202
+ when 'snapshots'
203
+ self.current = nil
204
+ return serve_snapshot(env)
205
+ when 'flamegraph'
206
+ return serve_flamegraph(env)
207
+ end
208
+
209
+ return client_settings.handle_cookie(serve_file(env, file_name: file_name))
210
+ end
211
+
212
+ has_disable_cookie = client_settings.disable_profiling?
213
+ # manual session disable / enable
214
+ if matches_action?('disable', env) || has_disable_cookie
215
+ skip_it = true
216
+ end
217
+
218
+ if matches_action?('enable', env)
219
+ skip_it = false
220
+ config.enabled = true
221
+ end
222
+
223
+ if skip_it || !config.enabled
224
+ status, headers, body = @app.call(env)
225
+ client_settings.disable_profiling = true
226
+ return client_settings.handle_cookie([status, headers, body])
227
+ end
228
+
229
+ # remember that profiling is not disabled (ie enabled)
230
+ client_settings.disable_profiling = false
231
+
232
+ # profile gc
233
+ if matches_action?('profile-gc', env)
234
+ current.measure = false if current
235
+ return serve_profile_gc(env, client_settings)
236
+ end
237
+
238
+ # profile memory
239
+ if matches_action?('profile-memory', env)
240
+ return serve_profile_memory(env, client_settings)
241
+ end
242
+
243
+ # any other requests past this point are going to the app to be profiled
244
+
245
+ MiniProfiler.create_current(env, @config)
246
+
247
+ if matches_action?('normal-backtrace', env)
248
+ client_settings.backtrace_level = ClientSettings::BACKTRACE_DEFAULT
249
+ elsif matches_action?('no-backtrace', env)
250
+ current.skip_backtrace = true
251
+ client_settings.backtrace_level = ClientSettings::BACKTRACE_NONE
252
+ elsif matches_action?('full-backtrace', env) || client_settings.backtrace_full?
253
+ current.full_backtrace = true
254
+ client_settings.backtrace_level = ClientSettings::BACKTRACE_FULL
255
+ elsif client_settings.backtrace_none?
256
+ current.skip_backtrace = true
257
+ end
258
+
259
+ flamegraph = nil
260
+
261
+ trace_exceptions = matches_action?('trace-exceptions', env) && defined? TracePoint
262
+ status, headers, body, exceptions, trace = nil
263
+
264
+ if trace_exceptions
265
+ exceptions = []
266
+ trace = TracePoint.new(:raise) do |tp|
267
+ exceptions << tp.raised_exception
268
+ end
269
+ trace.enable
270
+ end
271
+
272
+ begin
273
+
274
+ # Strip all the caching headers so we don't get 304s back
275
+ # This solves a very annoying bug where rack mini profiler never shows up
276
+ if config.disable_caching
277
+ env['HTTP_IF_MODIFIED_SINCE'] = ''
278
+ env['HTTP_IF_NONE_MATCH'] = ''
279
+ end
280
+
281
+ orig_accept_encoding = env['HTTP_ACCEPT_ENCODING']
282
+ # Prevent response body from being compressed
283
+ env['HTTP_ACCEPT_ENCODING'] = 'identity' if config.suppress_encoding
284
+
285
+ if matches_action?('flamegraph', env) || matches_action?('async-flamegraph', env) || env['HTTP_REFERER'] =~ /pp=async-flamegraph/
286
+ if defined?(StackProf) && StackProf.respond_to?(:run)
287
+ # do not sully our profile with mini profiler timings
288
+ current.measure = false
289
+ match_data = action_parameters(env)['flamegraph_sample_rate']
290
+
291
+ if match_data && !match_data[1].to_f.zero?
292
+ sample_rate = match_data[1].to_f
293
+ else
294
+ sample_rate = config.flamegraph_sample_rate
295
+ end
296
+
297
+ mode_match_data = action_parameters(env)['flamegraph_mode']
298
+
299
+ if mode_match_data && [:cpu, :wall, :object, :custom].include?(mode_match_data.to_sym)
300
+ mode = mode_match_data.to_sym
301
+ else
302
+ mode = config.flamegraph_mode
303
+ end
304
+
305
+ ignore_gc_match_data = action_parameters(env)['flamegraph_ignore_gc']
306
+
307
+ if ignore_gc_match_data
308
+ ignore_gc = ignore_gc_match_data == 'true'
309
+ else
310
+ ignore_gc = config.flamegraph_ignore_gc
311
+ end
312
+
313
+ flamegraph = StackProf.run(
314
+ mode: mode,
315
+ raw: true,
316
+ aggregate: false,
317
+ interval: (sample_rate * 1000).to_i,
318
+ ignore_gc: ignore_gc
319
+ ) do
320
+ status, headers, body = @app.call(env)
321
+ end
322
+ else
323
+ message = "Please install the stackprof gem and require it: add gem 'stackprof' to your Gemfile"
324
+ status, headers, body = @app.call(env)
325
+ body.close if body.respond_to? :close
326
+
327
+ return client_settings.handle_cookie(
328
+ text_result(message, status: status, headers: headers)
329
+ )
330
+ end
331
+ elsif path == '/rack-mini-profiler/requests'
332
+ status, headers, body = [200, { Rack::CONTENT_TYPE => 'text/html' }, [blank_page_html.dup]] # important to dup here!
333
+ else
334
+ status, headers, body = @app.call(env)
335
+ end
336
+ ensure
337
+ trace.disable if trace
338
+ env['HTTP_ACCEPT_ENCODING'] = orig_accept_encoding if config.suppress_encoding
339
+ end
340
+
341
+ skip_it = current.discard
342
+
343
+ if (config.authorization_mode == :allow_authorized && !MiniProfiler.request_authorized?)
344
+ skip_it = true
345
+ end
346
+
347
+ return client_settings.handle_cookie([status, headers, body]) if skip_it
348
+
349
+ # we must do this here, otherwise current[:discard] is not being properly treated
350
+ if trace_exceptions
351
+ body.close if body.respond_to? :close
352
+
353
+ query_params = action_parameters(env)
354
+ trace_exceptions_filter = query_params['trace_exceptions_filter']
355
+ if trace_exceptions_filter
356
+ trace_exceptions_regex = Regexp.new(trace_exceptions_filter)
357
+ exceptions.reject! { |ex| ex.class.name =~ trace_exceptions_regex }
358
+ end
359
+
360
+ return client_settings.handle_cookie(dump_exceptions exceptions)
361
+ end
362
+
363
+ if matches_action?("env", env)
364
+ return tool_disabled_message(client_settings) if !advanced_debugging_enabled?
365
+ body.close if body.respond_to? :close
366
+ return client_settings.handle_cookie(dump_env env)
367
+ end
368
+
369
+ if matches_action?("analyze-memory", env)
370
+ return tool_disabled_message(client_settings) if !advanced_debugging_enabled?
371
+ body.close if body.respond_to? :close
372
+ return client_settings.handle_cookie(analyze_memory)
373
+ end
374
+
375
+ if matches_action?("help", env)
376
+ body.close if body.respond_to? :close
377
+ return client_settings.handle_cookie(help(client_settings, env))
378
+ end
379
+
380
+ page_struct = current.page_struct
381
+ page_struct[:user] = user(env)
382
+ page_struct[:root].record_time((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000)
383
+
384
+ if flamegraph && matches_action?("flamegraph", env)
385
+ body.close if body.respond_to? :close
386
+ return client_settings.handle_cookie(self.flamegraph(flamegraph, path, env))
387
+ elsif flamegraph # async-flamegraph
388
+ page_struct[:has_flamegraph] = true
389
+ page_struct[:flamegraph] = flamegraph
390
+ end
391
+
392
+ begin
393
+ @storage.save(page_struct)
394
+ # no matter what it is, it should be unviewed, otherwise we will miss POST
395
+ @storage.set_unviewed(page_struct[:user], page_struct[:id])
396
+
397
+ # inject headers, script
398
+ if status >= 200 && status < 300
399
+ result = inject_profiler(env, status, headers, body)
400
+ return client_settings.handle_cookie(result) if result
401
+ end
402
+ rescue Exception => e
403
+ if @config.storage_failure != nil
404
+ @config.storage_failure.call(e)
405
+ end
406
+ end
407
+
408
+ client_settings.handle_cookie([status, headers, body])
409
+ ensure
410
+ # Make sure this always happens
411
+ self.current = nil
412
+ end
413
+
414
+ def matches_action?(action, env)
415
+ env['QUERY_STRING'] =~ /#{@config.profile_parameter}=#{action}/ ||
416
+ env['HTTP_X_RACK_MINI_PROFILER'] == action
417
+ end
418
+
419
+ def action_parameters(env)
420
+ Rack::Utils.parse_nested_query(env['QUERY_STRING'])
421
+ end
422
+
423
+ def inject_profiler(env, status, headers, body)
424
+ # mini profiler is meddling with stuff, we can not cache cause we will get incorrect data
425
+ # Rack::ETag has already inserted some nonesense in the chain
426
+ content_type = headers[Rack::CONTENT_TYPE]
427
+
428
+ if config.disable_caching
429
+ headers.delete(Rack::ETAG)
430
+ headers.delete('date') || headers.delete('Date')
431
+ end
432
+
433
+ headers['x-miniprofiler-original-cache-control'] = headers[Rack::CACHE_CONTROL] unless headers[Rack::CACHE_CONTROL].nil?
434
+ headers[Rack::CACHE_CONTROL] = "#{"no-store, " if config.disable_caching}must-revalidate, private, max-age=0"
435
+
436
+ # inject header
437
+ if headers.is_a? Hash
438
+ headers['x-miniprofiler-ids'] = ids_comma_separated(env)
439
+ headers['x-miniprofiler-flamegraph-path'] = flamegraph_path(env) if current.page_struct[:has_flamegraph]
440
+ end
441
+
442
+ if current.inject_js && content_type =~ /text\/html/
443
+ response = Rack::Response.new([], status, headers)
444
+ script = self.get_profile_script(env, headers)
445
+
446
+ if String === body
447
+ response.write inject(body, script)
448
+ else
449
+ body.each { |fragment| response.write inject(fragment, script) }
450
+ end
451
+ body.close if body.respond_to? :close
452
+ response.finish
453
+ else
454
+ nil
455
+ end
456
+ end
457
+
458
+ def inject(fragment, script)
459
+ # find explicit or implicit body
460
+ index = fragment.rindex(/<\/body>/i) || fragment.rindex(/<\/html>/i)
461
+ if index
462
+ # if for whatever crazy reason we dont get a utf string,
463
+ # just force the encoding, no utf in the mp scripts anyway
464
+ if script.respond_to?(:encoding) && script.respond_to?(:force_encoding)
465
+ script = script.force_encoding(fragment.encoding)
466
+ end
467
+
468
+ safe_script = script
469
+ if script.respond_to?(:html_safe)
470
+ safe_script = script.html_safe
471
+ end
472
+
473
+ fragment.insert(index, safe_script)
474
+ else
475
+ fragment
476
+ end
477
+ end
478
+
479
+ def dump_exceptions(exceptions)
480
+ body = "Exceptions raised during request\n\n".dup
481
+ if exceptions.empty?
482
+ body << "No exceptions raised"
483
+ else
484
+ body << "Exceptions: (#{exceptions.size} total)\n"
485
+ exceptions.group_by(&:class).each do |klass, exceptions_per_class|
486
+ body << " #{klass.name} (#{exceptions_per_class.size})\n"
487
+ end
488
+
489
+ body << "\nBacktraces\n"
490
+ exceptions.each_with_index do |e, i|
491
+ body << "##{i + 1}: #{e.class} - \"#{e.message.lines.first.chomp}\"\n #{e.backtrace.join("\n ")}\n\n"
492
+ end
493
+ end
494
+ text_result(body)
495
+ end
496
+
497
+ def dump_env(env)
498
+ body = "Rack Environment\n---------------\n".dup
499
+ env.each do |k, v|
500
+ body << "#{k}: #{v}\n"
501
+ end
502
+
503
+ body << "\n\nEnvironment\n---------------\n"
504
+ ENV.each do |k, v|
505
+ body << "#{k}: #{v}\n"
506
+ end
507
+
508
+ body << "\n\nRuby Version\n---------------\n"
509
+ body << "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL}\n"
510
+
511
+ body << "\n\nInternals\n---------------\n"
512
+ body << "Storage Provider #{config.storage_instance}\n"
513
+ body << "User #{user(env)}\n"
514
+ body << config.storage_instance.diagnostics(user(env)) rescue "no diagnostics implemented for storage"
515
+
516
+ text_result(body)
517
+ end
518
+
519
+ def trim_strings(strings, max_size)
520
+ strings.sort! { |a, b| b[1] <=> a[1] }
521
+ i = 0
522
+ strings.delete_if { |_| (i += 1) > max_size }
523
+ end
524
+
525
+ def analyze_memory
526
+ require 'objspace'
527
+
528
+ utf8 = "utf-8"
529
+
530
+ GC.start
531
+
532
+ trunc = lambda do |str|
533
+ str = str.length > 200 ? str : str[0..200]
534
+
535
+ if str.encoding != Encoding::UTF_8
536
+ str = str.dup
537
+ str.force_encoding(utf8)
538
+
539
+ unless str.valid_encoding?
540
+ # work around bust string with a double conversion
541
+ str.encode!("utf-16", "utf-8", invalid: :replace)
542
+ str.encode!("utf-8", "utf-16")
543
+ end
544
+ end
545
+
546
+ str
547
+ end
548
+
549
+ body = "ObjectSpace stats:\n\n".dup
550
+
551
+ counts = ObjectSpace.count_objects
552
+ total_strings = counts[:T_STRING]
553
+
554
+ body << counts
555
+ .sort { |a, b| b[1] <=> a[1] }
556
+ .map { |k, v| "#{k}: #{v}" }
557
+ .join("\n")
558
+
559
+ strings = []
560
+ string_counts = Hash.new(0)
561
+ sample_strings = []
562
+
563
+ max_size = 1000
564
+ sample_every = total_strings / max_size
565
+
566
+ i = 0
567
+ ObjectSpace.each_object(String) do |str|
568
+ i += 1
569
+ string_counts[str] += 1
570
+ strings << [trunc.call(str), str.length]
571
+ sample_strings << [trunc.call(str), str.length] if i % sample_every == 0
572
+ if strings.length > max_size * 2
573
+ trim_strings(strings, max_size)
574
+ end
575
+ end
576
+
577
+ trim_strings(strings, max_size)
578
+
579
+ body << "\n\n\n1000 Largest strings:\n\n"
580
+ body << strings.map { |s, len| "#{s[0..1000]}\n(len: #{len})\n\n" }.join("\n")
581
+
582
+ body << "\n\n\n1000 Sample strings:\n\n"
583
+ body << sample_strings.map { |s, len| "#{s[0..1000]}\n(len: #{len})\n\n" }.join("\n")
584
+
585
+ body << "\n\n\n1000 Most common strings:\n\n"
586
+ body << string_counts.sort { |a, b| b[1] <=> a[1] }[0..max_size].map { |s, len| "#{trunc.call(s)}\n(x #{len})\n\n" }.join("\n")
587
+
588
+ text_result(body)
589
+ end
590
+
591
+ def text_result(body, status: 200, headers: nil)
592
+ headers = (headers || {}).merge(Rack::CONTENT_TYPE => 'text/plain; charset=utf-8')
593
+ [status, headers, [body]]
594
+ end
595
+
596
+ def ids(env)
597
+ all = ([current.page_struct[:id]] + (@storage.get_unviewed_ids(user(env)) || [])).uniq
598
+ if all.size > @config.max_traces_to_show
599
+ all = all[0...@config.max_traces_to_show]
600
+ @storage.set_all_unviewed(user(env), all)
601
+ end
602
+ all
603
+ end
604
+
605
+ def ids_comma_separated(env)
606
+ ids(env).join(",")
607
+ end
608
+
609
+ def flamegraph_path(env)
610
+ @config.base_url_path + 'flamegraph?id=' + current.page_struct[:id]
611
+ end
612
+
613
+ # cancels automatic injection of profile script for the current page
614
+ def cancel_auto_inject(env)
615
+ current.inject_js = false
616
+ end
617
+
618
+ def cache_control_value
619
+ 86400
620
+ end
621
+
622
+ private
623
+
624
+ def rails_route_from_path(path, method)
625
+ if defined?(Rails) && defined?(ActionController::RoutingError)
626
+ hash = Rails.application.routes.recognize_path(path, method: method)
627
+ if hash && hash[:controller] && hash[:action]
628
+ "#{hash[:controller]}##{hash[:action]}"
629
+ end
630
+ end
631
+ rescue ActionController::RoutingError
632
+ nil
633
+ end
634
+
635
+ def take_snapshot?(path)
636
+ @config.snapshot_every_n_requests > 0 &&
637
+ !path.start_with?(@config.base_url_path) &&
638
+ @storage.should_take_snapshot?(@config.snapshot_every_n_requests)
639
+ end
640
+
641
+ def take_snapshot(env, start)
642
+ MiniProfiler.create_current(env, @config)
643
+ Thread.current[:mp_ongoing_snapshot] = true
644
+ results = @app.call(env)
645
+ status = results[0].to_i
646
+ if status >= 200 && status < 300
647
+ page_struct = current.page_struct
648
+ page_struct[:root].record_time(
649
+ (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000
650
+ )
651
+ custom_fields = MiniProfiler.get_snapshot_custom_fields
652
+ page_struct[:custom_fields] = custom_fields if custom_fields
653
+ if Rack::MiniProfiler.snapshots_transporter?
654
+ Rack::MiniProfiler::SnapshotsTransporter.transport(page_struct)
655
+ else
656
+ group_name = rails_route_from_path(page_struct[:request_path], page_struct[:request_method])
657
+ group_name ||= page_struct[:request_path]
658
+ group_name = "#{page_struct[:request_method]} #{group_name}"
659
+ @storage.push_snapshot(
660
+ page_struct,
661
+ group_name,
662
+ @config
663
+ )
664
+ end
665
+ end
666
+ self.current = nil
667
+ results
668
+ end
669
+ end
670
+ end