rack-mini-profiler 0.9.6 → 0.9.9.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack-mini-profiler might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e83bf4519ed83cd942b168c34783ee321a34e63f
4
- data.tar.gz: f53964ce3946d426cac21d760e49ee37cf5e0fea
3
+ metadata.gz: a2b10bd196bae4ba4dd1b18d3bdf54744e895f86
4
+ data.tar.gz: 33866dddf039f21eca7380c23fd31a09071d8666
5
5
  SHA512:
6
- metadata.gz: a2e447437d2fb8dc5872fe3d751ee535b0bdb0e6a6e35644b0627412b126ce80535571c63408a0845f71aa5afc3065ce75fc8482746dd7ba617e60d0a69efa05
7
- data.tar.gz: 81c35341c085d80ac5eee5b2423cdb8c6e3dbb9f9d3d08493ddb613b7349aa107dcffe4b7e5d54217066f54425269b29d65592519b35f84d1755411fc327ab0d
6
+ metadata.gz: 010b6b5b681db1dc8fddab3100c450a533be17b53290d07e9fafd89e29ac3fe6c6659371b978796455ebc8c6131192c1386186f1285abb81e5a1ccffcfe9e79a
7
+ data.tar.gz: 9cacdd67dc52d42556e6e0793d76675bf3bbc0ed27a748cd6f407b5996dc7a2f437e7ed559f905620ed267595bf3f79cf5744b25f5b4b7fd9f527c9c4b2bdf10
@@ -1,14 +1,54 @@
1
1
  # CHANGELOG
2
2
 
3
- ## 0.9.6 - 2014-07-08 (Sam Saffron)
3
+ ##
4
+
5
+ ## 0.9.9.2 2016-03-06
6
+
7
+ - [FEATURE] on pageTransition collapse previously expanded timings
8
+
9
+ ## 0.9.9.1 2016-03-06
10
+
11
+ - [FEATURE] expost MiniProfiler.pageTransition() for use by SPA web apps (@sam)
12
+
13
+ ## 0.9.9 2016-03-06
14
+
15
+ - [FIX] removes alias_method_chain in favor of alias_method until Ruby 1.9.3 (@ayfredlund)
16
+ - [FIX] Dont block mongo when already patched for another db (@rrooding @kbrock)
17
+ - [FIX] get_profile_script when running under passenger configured with RailsBaseURI (@nspring)
18
+ - [FEATURE] Add support for neo4j (@ProGM)
19
+ - [FIX] ArgumentError: comparison of String with 200 failed (@paweljw)
20
+ - [FEATURE] Add support for Riak (@janx)
21
+ - [PERF] GC profiler much faster (@dgynn)
22
+ - [FIX] If local storage is disabled don't bomb out (@elia)
23
+ - [FIX] Create tmp directory when actually using it (@kbrock)
24
+ - [ADDED] Default collapse_results setting that collapses multiple timings on same page to a single one (@sam)
25
+ - [ADDED] Rack::MiniProfiler.profile_singleton_method (@kbrock)
26
+ - [CHANGE] Added Rack 2.0 support (and dropped support for Rack 1.1) (@dgynn)
27
+
28
+ ## 0.9.8 - 2015-11-27 (Sam Saffron)
29
+
30
+ - [FEATURE] disable_env_dump config setting (@mathias)
31
+ - [FEATURE] set X-MiniProfiler-Ids for all 2XX reqs (@tymagu2)
32
+ - [FEATURE] add support for NoBrainer (rethinkdb) profiling (@niv)
33
+ - [FEATURE] add oracle enhanced adapter profiling (@rrooding)
34
+ - [FEATURE] pp=profile-memory can now parse query params (@dgynn)
35
+
36
+
37
+ ## 0.9.7 - 2015-08-03 (Sam Saffron)
38
+
39
+ - [FEATURE] remove confusing pp=profile-gc-time (Nate Berkopec)
40
+ - [FEATURE] truncate strings in pp=analyze-memory (Nate Berkopec)
41
+ - [FEATURE] rename pp=profile-gc-ruby-head to pp=profile-memory (Nate Berkopec)
42
+
43
+ ## 0.9.6 - 2015-07-08 (Sam Saffron)
4
44
 
5
45
  - [FIX] incorrect truncation in pp=analyze-memory
6
46
 
7
- ## 0.9.5 - 2014-07-08 (Sam Saffron)
47
+ ## 0.9.5 - 2015-07-08 (Sam Saffron)
8
48
 
9
49
  - [FEATURE] improve pp=analyze-memory
10
50
 
11
- ## 0.9.4 - 2014-07-08 (Sam Saffron)
51
+ ## 0.9.4 - 2015-07-08 (Sam Saffron)
12
52
  - [UX] added a link to "more" actions in profiler
13
53
  - [FEATURE] pp=help now displays links
14
54
  - [FEATURE] simple memory report with pp=analyze-memory
data/README.md CHANGED
@@ -6,7 +6,7 @@ Middleware that displays speed badge for every html page. Designed to work both
6
6
 
7
7
  #### Features
8
8
 
9
- * database profiling. Currently supports Mysql2, Postgres, and Mongoid3 (with fallback support to ActiveRecord)
9
+ * database profiling. Currently supports Mysql2, Postgres, Oracle (oracle_enhanced ~> 1.5.0) and Mongoid3 (with fallback support to ActiveRecord)
10
10
 
11
11
  #### Learn more
12
12
 
@@ -66,11 +66,17 @@ end
66
66
 
67
67
  ```ruby
68
68
  require 'rack-mini-profiler'
69
+
70
+ home = lambda { |env|
71
+ [200, {'Content-Type' => 'text/html'}, ["<html><body>hello!</body></html>"]]
72
+ }
73
+
69
74
  builder = Rack::Builder.new do
70
75
  use Rack::MiniProfiler
71
-
72
- map('/') { run get }
76
+ map('/') { run home }
73
77
  end
78
+
79
+ run builder
74
80
  ```
75
81
 
76
82
  #### Sinatra
@@ -89,20 +95,21 @@ To generate [flamegraphs](http://samsaffron.com/archive/2013/03/19/flame-graphs-
89
95
  * add the [**flamegraph**](https://github.com/SamSaffron/flamegraph) gem to your Gemfile
90
96
  * visit a page in your app with `?pp=flamegraph`
91
97
 
92
- Flamegraph generation is supported in MRI 2.0 and 2.1 only.
98
+ Flamegraph generation is supported in MRI 2.0, 2.1, and 2.2 only.
93
99
 
94
100
 
95
- ## Access control in production
101
+ ## Access control in non-development environments
96
102
 
97
103
  rack-mini-profiler is designed with production profiling in mind. To enable that just run `Rack::MiniProfiler.authorize_request` once you know a request is allowed to profile.
98
104
 
99
105
  ```ruby
100
- # A hook in your ApplicationController
101
- def authorize
102
- if current_user.is_admin?
103
- Rack::MiniProfiler.authorize_request
106
+ # inside your ApplicationController
107
+
108
+ before_action do
109
+ if current_user && current_user.is_admin?
110
+ Rack::MiniProfiler.authorize_request
111
+ end
104
112
  end
105
- end
106
113
  ```
107
114
 
108
115
  ## Configuration
@@ -121,7 +128,7 @@ To disable this behavior, use the following config setting:
121
128
 
122
129
  ```ruby
123
130
  # Do not let rack-mini-profiler disable caching
124
- Rack::MiniProfiler.config.disable_caching = false # defaults to true
131
+ Rack::MiniProfiler.config.disable_caching = false # defaults to true
125
132
  ```
126
133
 
127
134
  ### Storage
@@ -144,11 +151,11 @@ if Rails.env.production?
144
151
  end
145
152
  ```
146
153
 
147
- MemoryStore stores results in a processes heap - something that does not work well in a multi process environment.
148
- FileStore stores results in the file system - something that may not work well in a multi machine environment.
149
- RedisStore/MemcacheStore work in multi process and multi machine environments (RedisStore only saves results for up to 24 hours so it won't continue to fill up Redis).
154
+ `MemoryStore` stores results in a processes heap - something that does not work well in a multi process environment.
155
+ `FileStore` stores results in the file system - something that may not work well in a multi machine environment.
156
+ `RedisStore`/`MemcacheStore` work in multi process and multi machine environments (`RedisStore` only saves results for up to 24 hours so it won't continue to fill up Redis).
150
157
 
151
- Additionally you may implement an AbstractStore for your own provider.
158
+ Additionally you may implement an `AbstractStore` for your own provider.
152
159
 
153
160
  ### User result segregation
154
161
 
@@ -166,6 +173,29 @@ Rack::MiniProfiler.config.user_provider = Proc.new{ |env| CurrentUser.get(env) }
166
173
 
167
174
  The string this function returns should be unique for each user on the system (for anonymous you may need to fall back to ip address)
168
175
 
176
+ ### Profiling specific methods
177
+
178
+ You can increase the granularity of profiling by measuring the performance of specific methods. Add methods of interest to an initializer.
179
+
180
+ ```ruby
181
+ Rails.application.config.to_prepare do
182
+ ::Rack::MiniProfiler.profile_singleton_method(User, :non_admins) { |a| "executing all_non_admins" }
183
+ ::Rack::MiniProfiler.profile_method(User, :favorite_post) { |a| "executing favorite_post" }
184
+ end
185
+ ```
186
+
187
+ ### Using in SPA applications
188
+
189
+ Single page applications built using Ember, Angular or other frameworks need some special care, as routes often change without a full page load.
190
+
191
+ On route transition always call:
192
+
193
+ ```
194
+ window.MiniProfiler.pageTransition();
195
+ ```
196
+
197
+ This method will remove profiling information that was related to previous page and clear aggregate statistics.
198
+
169
199
  ### Configuration Options
170
200
 
171
201
  You can set configuration options using the configuration accessor on `Rack::MiniProfiler`.
@@ -177,16 +207,23 @@ Rack::MiniProfiler.config.start_hidden = true
177
207
  ```
178
208
  The available configuration options are:
179
209
 
180
- * pre_authorize_cb - A lambda callback you can set to determine whether or not mini_profiler should be visible on a given request. Default in a Rails environment is only on in development mode. If in a Rack app, the default is always on.
181
- * position - Can either be 'right' or 'left'. Default is 'left'.
182
- * skip_paths - Specifies path list that can be skipped.
183
- * skip_schema_queries - Whether or not you want to log the queries about the schema of your tables. Default is 'false', 'true' in rails development.
184
- * auto_inject (default true) - when false the miniprofiler script is not injected in the page
185
- * backtrace_filter - a regex you can use to filter out unwanted lines from the backtraces
186
- * toggle_shortcut (default Alt+P) - a jquery.hotkeys.js-style keyboard shortcut, used to toggle the mini_profiler's visibility. See https://github.com/jeresig/jquery.hotkeys for more info.
187
- * start_hidden (default false) - Whether or not you want the mini_profiler to be visible when loading a page
188
- * backtrace_threshold_ms (default zero) - Minimum SQL query elapsed time before a backtrace is recorded. Backtrace recording can take a couple of milliseconds on rubies earlier than 2.0, impacting performance for very small queries.
189
- * flamegraph_sample_rate (default 0.5ms) - How often fast_stack should get stack trace info to generate flamegraphs
210
+ Option|Default|Description
211
+ -------|---|--------
212
+ pre_authorize_cb|Rails: dev only<br>Rack: always on|A lambda callback that returns true to make mini_profiler visible on a given request.
213
+ position|`'left'`|Display mini_profiler on `'right'` or `'left'`.
214
+ skip_paths|`[]`|Paths that skip profiling.
215
+ skip_schema_queries|Rails dev: `'true'`<br>Othwerwise: `'false'`|`'true'` to log schema queries.
216
+ auto_inject|`true`|`true` to inject the miniprofiler script in the page.
217
+ backtrace_ignores|`[]`|Regexes of lines to be removed from backtraces.
218
+ backtrace_includes|Rails: `[/^\/?(app|config|lib|test)/]`<br>Rack: `[]`|Regexes of lines to keep in backtraces.
219
+ backtrace_remove|rails: `Rails.root`<br>Rack: `nil`|A string or regex to remove part of each line in the backtrace.
220
+ toggle_shortcut|Alt+P|Keyboard shortcut to toggle the mini_profiler's visibility. See [jquery.hotkeys](https://github.com/jeresig/jquery.hotkeys).
221
+ start_hidden|`false`|`false` to make mini_profiler visible on page load.
222
+ backtrace_threshold_ms|`0`|Minimum SQL query elapsed time before a backtrace is recorded. Backtrace recording can take a couple of milliseconds on rubies earlier than 2.0, impacting performance for very small queries.
223
+ flamegraph_sample_rate|`0.5ms`|How often to capture stack traces for flamegraphs.
224
+ disable_env_dump|`false`|`true` disables `?pp=env`, which prevents sending ENV vars over HTTP.
225
+ base_url_path|`'/mini-profiler-resources/'`|Path for assets; added as a prefix when naming assets and sought when responding to requests.
226
+ collapse_results|`true`|If multiple timing results exist in a single page, collapse them till clicked.
190
227
 
191
228
  ### Custom middleware ordering (required if using `Rack::Deflate` with Rails)
192
229
 
@@ -267,6 +267,7 @@
267
267
  .profiler-results.profiler-left {
268
268
  left: 0px;
269
269
  }
270
+ .profiler-results.profiler-left.profiler-no-controls .profiler-totals,
270
271
  .profiler-results.profiler-left.profiler-no-controls .profiler-result:last-child .profiler-button,
271
272
  .profiler-results.profiler-left .profiler-controls {
272
273
  -webkit-border-bottom-right-radius: 10px;
@@ -280,6 +281,7 @@
280
281
  .profiler-results.profiler-right {
281
282
  right: 0px;
282
283
  }
284
+ .profiler-results.profiler-right.profiler-no-controls .profiler-totals,
283
285
  .profiler-results.profiler-right.profiler-no-controls .profiler-result:last-child .profiler-button,
284
286
  .profiler-results.profiler-right .profiler-controls {
285
287
  -webkit-border-bottom-left-radius: 10px;
@@ -316,6 +318,16 @@
316
318
  color: #fff;
317
319
  font-weight: normal;
318
320
  }
321
+ .profiler-results .profiler-totals .profiler-reqs {
322
+ font-family: Consolas, monospace, serif;
323
+ font-size: 10px;
324
+ margin-left: 6px;
325
+ }
326
+ .profiler-results .profiler-totals .profiler-reqs:before {
327
+ font-family: Consolas, monospace, serif;
328
+ content: "×";
329
+ margin-right: 1px;
330
+ }
319
331
  .profiler-results .profiler-controls {
320
332
  display: block;
321
333
  font-size: 12px;
@@ -7,12 +7,19 @@ var MiniProfiler = (function () {
7
7
  controls,
8
8
  fetchedIds = [],
9
9
  fetchingIds = [], // so we never pull down a profiler twice
10
- ajaxStartTime
10
+ ajaxStartTime,
11
+ totalsControl,
12
+ reqs = 0,
13
+ expandedResults = false,
14
+ totalTime = 0;
11
15
  ;
12
16
 
13
- var hasLocalStorage = function () {
17
+ var hasLocalStorage = function (keyPrefix) {
14
18
  try {
15
- return 'localStorage' in window && window['localStorage'] !== null;
19
+ // attempt to save to localStorage as Safari private windows will throw an error
20
+ localStorage[keyPrefix+'-test'] = '1';
21
+ localStorage.removeItem(keyPrefix+'-test');
22
+ return 'localStorage' in window && window['localStorage'] !== null ;
16
23
  } catch (e) {
17
24
  return false;
18
25
  }
@@ -23,7 +30,7 @@ var MiniProfiler = (function () {
23
30
  };
24
31
 
25
32
  var save = function (keyPrefix, value) {
26
- if (!hasLocalStorage()) { return; }
33
+ if (!hasLocalStorage(keyPrefix)) { return; }
27
34
 
28
35
  // clear old keys with this prefix, if any
29
36
  for (var i = 0; i < localStorage.length; i++) {
@@ -37,7 +44,7 @@ var MiniProfiler = (function () {
37
44
  };
38
45
 
39
46
  var load = function (keyPrefix) {
40
- if (!hasLocalStorage()) { return null; }
47
+ if (!hasLocalStorage(keyPrefix)) { return null; }
41
48
 
42
49
  return localStorage[getVersionedKey(keyPrefix)];
43
50
  };
@@ -62,7 +69,7 @@ var MiniProfiler = (function () {
62
69
  };
63
70
 
64
71
  var getClientPerformance = function() {
65
- return window.performance == null ? null : window.performance;
72
+ return window.performance === null ? null : window.performance;
66
73
  };
67
74
 
68
75
  var fetchResults = function (ids) {
@@ -86,7 +93,7 @@ var MiniProfiler = (function () {
86
93
 
87
94
  clientPerformance = getClientPerformance();
88
95
 
89
- if (clientPerformance != null) {
96
+ if (clientPerformance !== null) {
90
97
  // ie is buggy strip out functions
91
98
  var copy = { navigation: {}, timing: {} };
92
99
 
@@ -114,7 +121,7 @@ var MiniProfiler = (function () {
114
121
 
115
122
  }
116
123
  }
117
- } else if (ajaxStartTime != null && clientProbes && clientProbes.length > 0) {
124
+ } else if (ajaxStartTime !== null && clientProbes && clientProbes.length > 0) {
118
125
  clientPerformance = { timing: { navigationStart: ajaxStartTime.getTime() } };
119
126
  ajaxStartTime = null;
120
127
  }
@@ -146,9 +153,36 @@ var MiniProfiler = (function () {
146
153
  return $('#profilerTemplate').tmpl(json);
147
154
  };
148
155
 
156
+
149
157
  var buttonShow = function (json) {
150
158
  var result = renderTemplate(json);
151
159
 
160
+ totalTime += parseFloat(json.duration_milliseconds, 10);
161
+ reqs++;
162
+
163
+ if (!controls && reqs > 1 && options.collapseResults && !expandedResults) {
164
+ if (!totalsControl) {
165
+ container.find('.profiler-result').hide();
166
+
167
+ totalsControl = $("<div class='profiler-result'><div class='profiler-button profiler-totals'></div></div>");
168
+ totalsControl.appendTo(container);
169
+ totalsControl.click(function(){
170
+ totalsControl.parent().find('.profiler-result').show();
171
+ totalsControl.hide();
172
+ expandedResults = true;
173
+ });
174
+
175
+ totalsControl.find('.profiler-button').show();
176
+ }
177
+
178
+ var reqsHtml = reqs > 1 ? ("<span class='profiler-reqs'>" + reqs + "</span>") : "";
179
+ totalsControl.find('.profiler-button').html("<span class='profiler-number'>" +
180
+ totalTime.toFixed(1) + "</span> <span class='profiler-unit'>ms</span>" +
181
+ reqsHtml);
182
+
183
+ result.hide();
184
+ }
185
+
152
186
  if (controls)
153
187
  result.insertBefore(controls);
154
188
  else
@@ -480,7 +514,7 @@ var MiniProfiler = (function () {
480
514
  PageRequestManager.add_endRequest(function (sender, args) {
481
515
  if (args) {
482
516
  var response = args.get_response();
483
- if (response.get_responseAvailable() && response._xmlHttpRequest != null) {
517
+ if (response.get_responseAvailable() && response._xmlHttpRequest !== null) {
484
518
  var stringIds = args.get_response().getResponseHeader('X-MiniProfiler-Ids');
485
519
  if (stringIds) {
486
520
  var ids = typeof JSON != 'undefined' ? JSON.parse(stringIds) : eval(stringIds);
@@ -557,14 +591,14 @@ var MiniProfiler = (function () {
557
591
  }
558
592
  }
559
593
 
560
- if (this.miniprofiler.prev_onreadystatechange != null)
594
+ if (this.miniprofiler.prev_onreadystatechange !== null)
561
595
  return this.miniprofiler.prev_onreadystatechange.apply(this, arguments);
562
596
  };
563
597
  }
564
598
  }
565
599
 
566
600
  return _send.apply(this, arguments);
567
- }
601
+ };
568
602
  }
569
603
 
570
604
  // some elements want to be hidden on certain doc events
@@ -594,11 +628,12 @@ var MiniProfiler = (function () {
594
628
  var maxTraces = parseInt(script.getAttribute('data-max-traces'), 10);
595
629
  }
596
630
 
597
- if (script.getAttribute('data-trivial') === 'true') var trivial = true;
598
- if (script.getAttribute('data-children') == 'true') var children = true;
599
- if (script.getAttribute('data-controls') == 'true') var controls = true;
600
- if (script.getAttribute('data-authorized') == 'true') var authorized = true;
601
- if (script.getAttribute('data-start-hidden') == 'true') var startHidden = true;
631
+ var collapseResults = script.getAttribute('data-collapse-results') === 'true';
632
+ var trivial = script.getAttribute('data-trivial') === 'true';
633
+ var children = script.getAttribute('data-children') === 'true';
634
+ var controls = script.getAttribute('data-controls') === 'true';
635
+ var authorized = script.getAttribute('data-authorized') === 'true';
636
+ var startHidden = script.getAttribute('data-start-hidden') === 'true';
602
637
 
603
638
  return {
604
639
  ids: ids,
@@ -612,7 +647,8 @@ var MiniProfiler = (function () {
612
647
  currentId: currentId,
613
648
  authorized: authorized,
614
649
  toggleShortcut: toggleShortcut,
615
- startHidden: startHidden
650
+ startHidden: startHidden,
651
+ collapseResults: collapseResults
616
652
  };
617
653
  })();
618
654
 
@@ -710,6 +746,17 @@ var MiniProfiler = (function () {
710
746
  unbindDocumentEvents();
711
747
  },
712
748
 
749
+ pageTransition: function() {
750
+ if (totalsControl) {
751
+ totalsControl.remove();
752
+ totalsControl = null;
753
+ }
754
+ reqs = 0;
755
+ totalTime = 0;
756
+ expandedResults = false;
757
+ $('.profiler-results .profiler-result').remove();
758
+ },
759
+
713
760
  getClientTimingByName: function (clientTiming, name) {
714
761
 
715
762
  for (var i = 0; i < clientTiming.timings.length; i++) {
@@ -842,7 +889,7 @@ var MiniProfiler = (function () {
842
889
  var processTimes = function (elem, parent) {
843
890
  var duration = { start: elem.start_milliseconds, finish: (elem.start_milliseconds + elem.duration_milliseconds) };
844
891
  elem.richTiming = [duration];
845
- if (parent != null) {
892
+ if (parent !== null) {
846
893
  elem.parent = parent;
847
894
  elem.parent.richTiming = removeDuration(elem.parent.richTiming, duration);
848
895
  }
@@ -877,7 +924,7 @@ var MiniProfiler = (function () {
877
924
 
878
925
  var determineGap = function (gap, node, match) {
879
926
  var overlap = determineOverlap(gap, node);
880
- if (match == null || overlap > match.duration) {
927
+ if (match === null || overlap > match.duration) {
881
928
  match = { name: node.name, duration: overlap };
882
929
  }
883
930
  else if (match.name == node.name) {
@@ -247,7 +247,7 @@
247
247
  }
248
248
 
249
249
  // ajaxed-in results will be appended to this
250
- .profiler-results
250
+ .profiler-results
251
251
  {
252
252
  z-index:@zindex + 3;
253
253
  position:fixed;
@@ -258,7 +258,7 @@
258
258
  &.profiler-left {
259
259
  left:0px;
260
260
 
261
- &.profiler-no-controls .profiler-result:last-child .profiler-button, .profiler-controls {
261
+ &.profiler-no-controls .profiler-totals, &.profiler-no-controls .profiler-result:last-child .profiler-button, .profiler-controls {
262
262
  -webkit-border-bottom-right-radius: @radius;
263
263
  -moz-border-radius-bottomright: @radius;
264
264
  border-bottom-right-radius: @radius;
@@ -272,7 +272,7 @@
272
272
  &.profiler-right {
273
273
  right:0px;
274
274
 
275
- &.profiler-no-controls .profiler-result:last-child .profiler-button, .profiler-controls {
275
+ &.profiler-no-controls .profiler-totals, &.profiler-no-controls .profiler-result:last-child .profiler-button, .profiler-controls {
276
276
  -webkit-border-bottom-left-radius: @radius;
277
277
  -moz-border-radius-bottomleft: @radius;
278
278
  border-bottom-left-radius: @radius;
@@ -306,6 +306,20 @@
306
306
  }
307
307
  }
308
308
 
309
+
310
+ .profiler-totals {
311
+ .profiler-reqs {
312
+ font-family: @codeFonts;
313
+ font-size: 10px;
314
+ margin-left: 6px;
315
+ &:before {
316
+ font-family: @codeFonts;
317
+ content: "×";
318
+ margin-right: 1px;
319
+ }
320
+ }
321
+ }
322
+
309
323
  .profiler-controls {
310
324
  display: block;
311
325
  font-size:12px;