rack-mini-profiler 0.1.19 → 0.1.24

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.

Potentially problematic release.


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

Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/Ruby/CHANGELOG +127 -0
  3. data/{README.md → Ruby/README.md} +40 -9
  4. data/Ruby/lib/html/includes.css +451 -0
  5. data/{lib → Ruby/lib}/html/includes.js +135 -24
  6. data/{lib → Ruby/lib}/html/includes.less +38 -35
  7. data/{lib → Ruby/lib}/html/includes.tmpl +40 -15
  8. data/{lib → Ruby/lib}/html/jquery.1.7.1.js +1 -1
  9. data/{lib → Ruby/lib}/html/jquery.tmpl.js +1 -1
  10. data/{lib → Ruby/lib}/html/list.css +0 -0
  11. data/{lib → Ruby/lib}/html/list.js +7 -6
  12. data/{lib → Ruby/lib}/html/list.tmpl +0 -0
  13. data/Ruby/lib/html/profile_handler.js +1 -0
  14. data/{lib → Ruby/lib}/html/share.html +0 -0
  15. data/{lib → Ruby/lib}/mini_profiler/client_settings.rb +0 -0
  16. data/{lib → Ruby/lib}/mini_profiler/client_timer_struct.rb +1 -1
  17. data/{lib → Ruby/lib}/mini_profiler/config.rb +57 -52
  18. data/{lib → Ruby/lib}/mini_profiler/context.rb +11 -10
  19. data/Ruby/lib/mini_profiler/custom_timer_struct.rb +22 -0
  20. data/Ruby/lib/mini_profiler/flame_graph.rb +54 -0
  21. data/Ruby/lib/mini_profiler/gc_profiler.rb +107 -0
  22. data/{lib → Ruby/lib}/mini_profiler/page_timer_struct.rb +7 -2
  23. data/{lib → Ruby/lib}/mini_profiler/profiler.rb +214 -142
  24. data/{lib → Ruby/lib}/mini_profiler/profiling_methods.rb +131 -100
  25. data/{lib → Ruby/lib}/mini_profiler/request_timer_struct.rb +20 -1
  26. data/{lib → Ruby/lib}/mini_profiler/sql_timer_struct.rb +0 -0
  27. data/{lib → Ruby/lib}/mini_profiler/storage/abstract_store.rb +31 -27
  28. data/{lib → Ruby/lib}/mini_profiler/storage/file_store.rb +111 -109
  29. data/Ruby/lib/mini_profiler/storage/memcache_store.rb +53 -0
  30. data/{lib → Ruby/lib}/mini_profiler/storage/memory_store.rb +65 -63
  31. data/Ruby/lib/mini_profiler/storage/redis_store.rb +54 -0
  32. data/{lib → Ruby/lib}/mini_profiler/timer_struct.rb +0 -0
  33. data/Ruby/lib/mini_profiler/version.rb +5 -0
  34. data/{lib → Ruby/lib}/mini_profiler_rails/railtie.rb +3 -2
  35. data/Ruby/lib/patches/net_patches.rb +14 -0
  36. data/{lib → Ruby/lib}/patches/sql_patches.rb +89 -48
  37. data/{lib → Ruby/lib}/rack-mini-profiler.rb +2 -1
  38. data/rack-mini-profiler.gemspec +8 -6
  39. metadata +55 -65
  40. data/CHANGELOG +0 -83
  41. data/lib/html/includes.css +0 -75
  42. data/lib/html/profile_handler.js +0 -62
  43. data/lib/mini_profiler/storage/redis_store.rb +0 -44
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
- var MiniProfiler = (function ($) {
2
+ var MiniProfiler = (function () {
3
+ var $;
3
4
 
4
5
  var options,
5
6
  container,
@@ -60,11 +61,10 @@ var MiniProfiler = (function ($) {
60
61
  }
61
62
  };
62
63
 
63
- var getClientPerformance = function () {
64
+ var getClientPerformance = function() {
64
65
  return window.performance == null ? null : window.performance;
65
- }
66
+ };
66
67
 
67
- var waitedForEnd = 0;
68
68
  var fetchResults = function (ids) {
69
69
  var clientPerformance, clientProbes, i, j, p, id, idx;
70
70
 
@@ -364,6 +364,9 @@ var MiniProfiler = (function ($) {
364
364
  popupHide(button, popup);
365
365
  }
366
366
  });
367
+ $(document).bind('keydown', options.toggleShortcut, function(e) {
368
+ $('.profiler-results').toggle();
369
+ });
367
370
  };
368
371
 
369
372
  var initFullView = function () {
@@ -432,6 +435,7 @@ var MiniProfiler = (function ($) {
432
435
  // get master page profiler results
433
436
  fetchResults(options.ids);
434
437
  });
438
+ if (options.startHidden) container.hide();
435
439
  }
436
440
  else {
437
441
  fetchResults(options.ids);
@@ -449,11 +453,12 @@ var MiniProfiler = (function ($) {
449
453
  };
450
454
 
451
455
  // fetch profile results for any ajax calls
456
+ // note, this does not use $ cause we want to hook into the main jQuery
452
457
  if (jQuery && jQuery(document) && jQuery(document).ajaxComplete) {
453
458
  jQuery(document).ajaxComplete(jQueryAjaxComplete);
454
459
  }
455
460
 
456
- if (jQuery(document).ajaxStart)
461
+ if (jQuery && jQuery(document).ajaxStart)
457
462
  jQuery(document).ajaxStart(function () { ajaxStartTime = new Date(); });
458
463
 
459
464
  // fetch results after ASP Ajax calls
@@ -506,20 +511,92 @@ var MiniProfiler = (function ($) {
506
511
  });
507
512
  }
508
513
 
514
+ if (typeof (MooTools) != 'undefined' && typeof (Request) != 'undefined') {
515
+ Request.prototype.addEvents({
516
+ onComplete: function() {
517
+ var stringIds = this.xhr.getResponseHeader('X-MiniProfiler-Ids');
518
+ if (stringIds) {
519
+ var ids = typeof JSON != 'undefined' ? JSON.parse(stringIds) : eval(stringIds);
520
+ fetchResults(ids);
521
+ }
522
+ }
523
+ });
524
+ }
525
+
526
+ // add support for AngularJS, which use the basic XMLHttpRequest object.
527
+ if (window.angular && typeof (XMLHttpRequest) != 'undefined') {
528
+ var _send = XMLHttpRequest.prototype.send;
529
+
530
+ XMLHttpRequest.prototype.send = function sendReplacement(data) {
531
+ this._onreadystatechange = this.onreadystatechange;
532
+
533
+ this.onreadystatechange = function onReadyStateChangeReplacement() {
534
+ if (this.readyState == 4) {
535
+ var stringIds = this.getResponseHeader('X-MiniProfiler-Ids');
536
+ if (stringIds) {
537
+ var ids = typeof JSON != 'undefined' ? JSON.parse(stringIds) : eval(stringIds);
538
+ fetchResults(ids);
539
+ }
540
+ }
541
+
542
+ return this._onreadystatechange.apply(this, arguments);
543
+ }
544
+
545
+ return _send.apply(this, arguments);
546
+ }
547
+ }
548
+
509
549
  // some elements want to be hidden on certain doc events
510
550
  bindDocumentEvents();
511
551
  };
512
552
 
513
553
  return {
514
554
 
515
- init: function (opt) {
516
-
517
- options = opt || {};
555
+ init: function () {
556
+ var script = document.getElementById('mini-profiler');
557
+ if (!script || !script.getAttribute) return;
558
+
559
+ options = (function () {
560
+ var version = script.getAttribute('data-version');
561
+ var path = script.getAttribute('data-path');
562
+
563
+ var currentId = script.getAttribute('data-current-id');
564
+
565
+ var ids = script.getAttribute('data-ids');
566
+ if (ids) ids = ids.split(',');
567
+
568
+ var position = script.getAttribute('data-position');
569
+
570
+ var toggleShortcut = script.getAttribute('data-toggle-shortcut');
571
+
572
+ if (script.getAttribute('data-max-traces'))
573
+ var maxTraces = parseInt(script.getAttribute('data-max-traces'));
574
+
575
+ if (script.getAttribute('data-trivial') === 'true') var trivial = true;
576
+ if (script.getAttribute('data-children') == 'true') var children = true;
577
+ if (script.getAttribute('data-controls') == 'true') var controls = true;
578
+ if (script.getAttribute('data-authorized') == 'true') var authorized = true;
579
+ if (script.getAttribute('data-start-hidden') == 'true') var startHidden = true;
580
+
581
+ return {
582
+ ids: ids,
583
+ path: path,
584
+ version: version,
585
+ renderPosition: position,
586
+ showTrivial: trivial,
587
+ showChildrenTime: children,
588
+ maxTracesToShow: maxTraces,
589
+ showControls: controls,
590
+ currentId: currentId,
591
+ authorized: authorized,
592
+ toggleShortcut: toggleShortcut,
593
+ startHidden: startHidden
594
+ }
595
+ })();
518
596
 
519
597
  var doInit = function () {
520
598
  // when rendering a shared, full page, this div will exist
521
599
  container = $('.profiler-result-full');
522
-
523
600
  if (container.length) {
524
601
  if (window.location.href.indexOf("&trivial=1") > 0) {
525
602
  options.showTrivial = true
@@ -546,24 +623,49 @@ var MiniProfiler = (function ($) {
546
623
  document.getElementsByTagName('head')[0].appendChild(sc);
547
624
  };
548
625
 
549
- if (options.authorized) {
550
- var url = options.path + "includes.css?v=" + options.version;
551
- if (document.createStyleSheet) {
552
- document.createStyleSheet(url);
626
+ var wait = 0;
627
+ var finish = false;
628
+ var deferInit = function() {
629
+ if (finish) return;
630
+ if (window.performance && window.performance.timing && window.performance.timing.loadEventEnd == 0 && wait < 10000) {
631
+ setTimeout(deferInit, 100);
632
+ wait += 100;
553
633
  } else {
554
- $('head').append($('<link rel="stylesheet" type="text/css" href="' + url + '" />'));
634
+ finish = true;
635
+ init();
555
636
  }
637
+ };
556
638
 
557
- if (!$.tmpl) {
558
- load(options.path + 'jquery.tmpl.js?v=' + options.version, doInit);
639
+ var init = function() {
640
+ if (options.authorized) {
641
+ var url = options.path + "includes.css?v=" + options.version;
642
+ if (document.createStyleSheet) {
643
+ document.createStyleSheet(url);
644
+ } else {
645
+ $('head').append($('<link rel="stylesheet" type="text/css" href="' + url + '" />'));
646
+ }
647
+ if (!$.tmpl) {
648
+ load(options.path + 'jquery.tmpl.js?v=' + options.version, doInit);
649
+ } else {
650
+ doInit();
651
+ }
559
652
  } else {
560
653
  doInit();
561
654
  }
655
+ };
656
+
657
+ if (typeof(jQuery) == 'function') {
658
+ var jQueryVersion = jQuery.fn.jquery.split('.');
562
659
  }
563
- else {
564
- doInit();
660
+ if (jQueryVersion && parseInt(jQueryVersion[0]) < 2 && parseInt(jQueryVersion[1]) >= 7) {
661
+ MiniProfiler.jQuery = $ = jQuery;
662
+ $(deferInit);
663
+ } else {
664
+ load(options.path + "jquery.1.7.1.js?v=" + options.version, function() {
665
+ MiniProfiler.jQuery = $ = jQuery.noConflict(true);
666
+ $(deferInit);
667
+ });
565
668
  }
566
-
567
669
  },
568
670
 
569
671
  getClientTimingByName: function (clientTiming, name) {
@@ -650,7 +752,7 @@ var MiniProfiler = (function ($) {
650
752
  // start adding at the root and recurse down
651
753
  addToResults(root);
652
754
 
653
- var removeDuration = function (list, duration) {
755
+ var removeDuration = function(list, duration) {
654
756
 
655
757
  var newList = [];
656
758
  for (var i = 0; i < list.length; i++) {
@@ -674,7 +776,7 @@ var MiniProfiler = (function ($) {
674
776
  }
675
777
 
676
778
  return newList;
677
- }
779
+ };
678
780
 
679
781
  var processTimes = function (elem, parent) {
680
782
  var duration = { start: elem.StartMilliseconds, finish: (elem.StartMilliseconds + elem.DurationMilliseconds) };
@@ -696,7 +798,7 @@ var MiniProfiler = (function ($) {
696
798
  // sort results by time
697
799
  result.sort(function (a, b) { return a.StartMilliseconds - b.StartMilliseconds; });
698
800
 
699
- var determineOverlap = function (gap, node) {
801
+ var determineOverlap = function(gap, node) {
700
802
  var overlap = 0;
701
803
  for (var i = 0; i < node.richTiming.length; i++) {
702
804
  var current = node.richTiming[i];
@@ -710,7 +812,7 @@ var MiniProfiler = (function ($) {
710
812
  overlap += Math.min(gap.finish, current.finish) - Math.max(gap.start, current.start);
711
813
  }
712
814
  return overlap;
713
- }
815
+ };
714
816
 
715
817
  var determineGap = function (gap, node, match) {
716
818
  var overlap = determineOverlap(gap, node);
@@ -783,7 +885,16 @@ var MiniProfiler = (function ($) {
783
885
  return (duration || 0).toFixed(1);
784
886
  }
785
887
  };
786
- })(jQueryMP);
888
+ })();
889
+
890
+ MiniProfiler.init();
891
+
892
+ // jquery.hotkeys.js
893
+ // https://github.com/jeresig/jquery.hotkeys/blob/master/jquery.hotkeys.js
894
+
895
+ (function(d){function h(g){if("string"===typeof g.data){var h=g.handler,j=g.data.toLowerCase().split(" ");g.handler=function(b){if(!(this!==b.target&&(/textarea|select/i.test(b.target.nodeName)||"text"===b.target.type))){var c="keypress"!==b.type&&d.hotkeys.specialKeys[b.which],e=String.fromCharCode(b.which).toLowerCase(),a="",f={};b.altKey&&"alt"!==c&&(a+="alt+");b.ctrlKey&&"ctrl"!==c&&(a+="ctrl+");b.metaKey&&(!b.ctrlKey&&"meta"!==c)&&(a+="meta+");b.shiftKey&&"shift"!==c&&(a+="shift+");c?f[a+c]=
896
+ !0:(f[a+e]=!0,f[a+d.hotkeys.shiftNums[e]]=!0,"shift+"===a&&(f[d.hotkeys.shiftNums[e]]=!0));c=0;for(e=j.length;c<e;c++)if(f[j[c]])return h.apply(this,arguments)}}}}d.hotkeys={version:"0.8",specialKeys:{8:"backspace",9:"tab",13:"return",16:"shift",17:"ctrl",18:"alt",19:"pause",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"del",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"*",107:"+",
897
+ 109:"-",110:".",111:"/",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",121:"f10",122:"f11",123:"f12",144:"numlock",145:"scroll",191:"/",224:"meta"},shiftNums:{"`":"~",1:"!",2:"@",3:"#",4:"$",5:"%",6:"^",7:"&",8:"*",9:"(","0":")","-":"_","=":"+",";":": ","'":'"',",":"<",".":">","/":"?","\\":"|"}};d.each(["keydown","keyup","keypress"],function(){d.event.special[this]={add:h}})})(jQuery);
787
898
 
788
899
  // prettify.js
789
900
  // http://code.google.com/p/google-code-prettify/
@@ -49,7 +49,7 @@
49
49
  // styles shared between popup view and full view
50
50
  .profiler-result
51
51
  {
52
-
52
+
53
53
  .profiler-toggle-duration-with-children
54
54
  {
55
55
  float: right;
@@ -143,6 +143,9 @@
143
143
  float:left;
144
144
  margin-left:0px;
145
145
  }
146
+ &.profiler-custom-link {
147
+ float:left;
148
+ }
146
149
  }
147
150
  }
148
151
  }
@@ -189,15 +192,15 @@
189
192
  text-align:right;
190
193
  margin-bottom:5px;
191
194
  }
192
-
195
+
193
196
  .profiler-gap-info, .profiler-gap-info td { background-color: #ccc;}
194
197
  .profiler-gap-info {
195
198
  .profiler-unit {color: #777;}
196
199
  .profiler-info {text-align: right}
197
200
  &.profiler-trivial-gaps {display: none}
198
- }
199
-
200
- .profiler-trivial-gap-container { text-align: center;}
201
+ }
202
+
203
+ .profiler-trivial-gap-container { text-align: center;}
201
204
 
202
205
  // prettify colors
203
206
  .str{color:maroon}
@@ -287,24 +290,24 @@
287
290
  }
288
291
  }
289
292
 
290
- .profiler-controls {
291
- display: block;
292
- font-size:12px;
293
- font-family: @codeFonts;
294
- cursor:default;
295
- text-align: center;
296
-
297
- span {
298
- border-right: 1px solid @mutedColor;
299
- padding-right: 5px;
300
- margin-right: 5px;
301
- cursor:pointer;
302
- }
303
-
304
- span:last-child {
305
- border-right: none;
306
- }
307
- }
293
+ .profiler-controls {
294
+ display: block;
295
+ font-size:12px;
296
+ font-family: @codeFonts;
297
+ cursor:default;
298
+ text-align: center;
299
+
300
+ span {
301
+ border-right: 1px solid @mutedColor;
302
+ padding-right: 5px;
303
+ margin-right: 5px;
304
+ cursor:pointer;
305
+ }
306
+
307
+ span:last-child {
308
+ border-right: none;
309
+ }
310
+ }
308
311
 
309
312
  .profiler-popup {
310
313
  display:none;
@@ -367,19 +370,19 @@
367
370
  }
368
371
  }
369
372
 
370
- &.profiler-min .profiler-result {
371
- display: none;
372
- }
373
+ &.profiler-min .profiler-result {
374
+ display: none;
375
+ }
373
376
 
374
- &.profiler-min .profiler-controls span {
375
- display: none;
376
- }
377
+ &.profiler-min .profiler-controls span {
378
+ display: none;
379
+ }
377
380
 
378
- &.profiler-min .profiler-controls .profiler-min-max {
379
- border-right: none;
380
- padding: 0px;
381
- margin: 0px;
382
- }
381
+ &.profiler-min .profiler-controls .profiler-min-max {
382
+ border-right: none;
383
+ padding: 0px;
384
+ margin: 0px;
385
+ }
383
386
  }
384
387
 
385
388
  // popup results' queries will be displayed in front of this
@@ -465,4 +468,4 @@
465
468
  }
466
469
  }
467
470
  }
468
- }
471
+ }
@@ -27,10 +27,13 @@
27
27
  {{if HasSqlTimings}}
28
28
  <th colspan="2">query time (ms)</th>
29
29
  {{/if}}
30
+ {{each CustomTimingNames}}
31
+ <th colspan="2">${$value.toLowerCase()} (ms)</th>
32
+ {{/each}}
30
33
  </tr>
31
34
  </thead>
32
35
  <tbody>
33
- {{tmpl(Root) "#timingTemplate"}}
36
+ {{tmpl({timing:Root, page:this.data}) "#timingTemplate"}}
34
37
  </tbody>
35
38
  <tfoot>
36
39
  <tr>
@@ -46,6 +49,12 @@
46
49
  <span class="profiler-unit">% in sql</span>
47
50
  </td>
48
51
  {{/if}}
52
+ {{each CustomTimingNames}}
53
+ <td colspan="2" class="profiler-number profiler-percentage-in-sql" title="${CustomTimingStats[$value].Count} ${$value.toLowerCase()} invocations spent ${MiniProfiler.formatDuration(CustomTimingStats[$value].Duration)} ms of total request time">
54
+ ${MiniProfiler.formatDuration(CustomTimingStats[$value].Duration / DurationMilliseconds * 100)}
55
+ <span class="profiler-unit">% in ${$value.toLowerCase()}</span>
56
+ </td>
57
+ {{/each}}
49
58
  </tr>
50
59
  </tfoot>
51
60
  </table>
@@ -114,6 +123,9 @@
114
123
 
115
124
  <script id="linksTemplate" type="text/x-jquery-tmpl">
116
125
  <a href="${MiniProfiler.shareUrl(Id)}" class="profiler-share-profiler-results" target="_blank">share</a>
126
+ {{if CustomLink}}
127
+ <a href="${CustomLink}" class="profiler-custom-link" target="_blank">${CustomLinkName}</a>
128
+ {{/if}}
117
129
  {{if HasTrivialTimings}}
118
130
  <a class="profiler-toggle-trivial" data-show-on-load="${HasAllTrivialTimings}" title="toggles any rows with &lt; ${TrivialDurationThresholdMilliseconds} ms">
119
131
  show trivial
@@ -123,37 +135,50 @@
123
135
 
124
136
  <script id="timingTemplate" type="text/x-jquery-tmpl">
125
137
 
126
- <tr class="{{if IsTrivial }}profiler-trivial{{/if}}" data-timing-id="${Id}">
127
- <td class="profiler-label" title="{{if Name && Name.length > 45 }}${Name}{{/if}}">
128
- <span class="profiler-indent">${MiniProfiler.renderIndent(Depth)}</span> ${Name.slice(0,45)}{{if Name && Name.length > 45 }}...{{/if}}
138
+ <tr class="{{if timing.IsTrivial }}profiler-trivial{{/if}}" data-timing-id="${timing.Id}">
139
+ <td class="profiler-label" title="{{if timing.Name && timing.Name.length > 45 }}${timing.Name}{{/if}}">
140
+ <span class="profiler-indent">${MiniProfiler.renderIndent(timing.Depth)}</span> ${timing.Name.slice(0,45)}{{if timing.Name && timing.Name.length > 45 }}...{{/if}}
129
141
  </td>
130
142
  <td class="profiler-duration" title="duration of this step without any children's durations">
131
- ${MiniProfiler.formatDuration(DurationWithoutChildrenMilliseconds)}
143
+ ${MiniProfiler.formatDuration(timing.DurationWithoutChildrenMilliseconds)}
132
144
  </td>
133
145
  <td class="profiler-duration profiler-duration-with-children" title="duration of this step and its children">
134
- ${MiniProfiler.formatDuration(DurationMilliseconds)}
146
+ ${MiniProfiler.formatDuration(timing.DurationMilliseconds)}
135
147
  </td>
136
148
  <td class="profiler-duration time-from-start" title="time elapsed since profiling started">
137
- <span class="profiler-unit">+</span>${MiniProfiler.formatDuration(StartMilliseconds)}
149
+ <span class="profiler-unit">+</span>${MiniProfiler.formatDuration(timing.StartMilliseconds)}
138
150
  </td>
139
151
 
140
- {{if HasSqlTimings}}
141
- <td class="profiler-duration {{if HasDuplicateSqlTimings}}profiler-warning{{/if}}" title="{{if HasDuplicateSqlTimings}}duplicate queries detected - {{/if}}{{if ExecutedReaders > 0 || ExecutedScalars > 0 || ExecutedNonQueries > 0}}${ExecutedReaders} reader, ${ExecutedScalars} scalar, ${ExecutedNonQueries} non-query statements executed{{/if}}">
152
+ {{if timing.HasSqlTimings}}
153
+ <td class="profiler-duration {{if timing.HasDuplicateSqlTimings}}profiler-warning{{/if}}" title="{{if timing.HasDuplicateSqlTimings}}duplicate queries detected - {{/if}}{{if timing.ExecutedReaders > 0 || timing.ExecutedScalars > 0 || timing.ExecutedNonQueries > 0}}${timing.ExecutedReaders} reader, ${timing.ExecutedScalars} scalar, ${timing.ExecutedNonQueries} non-query statements executed{{/if}}">
142
154
  <a class="profiler-queries-show">
143
- {{if HasDuplicateSqlTimings}}<span class="profiler-nuclear">!</span>{{/if}}
144
- ${SqlTimings.length} <span class="profiler-unit">sql</span>
155
+ {{if timing.HasDuplicateSqlTimings}}<span class="profiler-nuclear">!</span>{{/if}}
156
+ ${timing.SqlTimings.length} <span class="profiler-unit">sql</span>
145
157
  </a>
146
158
  </td>
147
159
  <td class="profiler-duration" title="aggregate duration of all queries in this step (excludes children)">
148
- ${MiniProfiler.formatDuration(SqlTimingsDurationMilliseconds)}
160
+ ${MiniProfiler.formatDuration(timing.SqlTimingsDurationMilliseconds)}
149
161
  </td>
150
162
  {{/if}}
151
163
 
164
+ {{each page.CustomTimingNames}}
165
+ {{if timing.CustomTimings && timing.CustomTimings[$value]}}
166
+ <td class="profiler-duration" title="aggregate number of all ${$value.toLowerCase()} invocations in this step (excludes children)">
167
+ ${timing.CustomTimings[$value].length} ${$value.toLowerCase()}
168
+ </td>
169
+ <td class="profiler-duration" title="aggregate duration of all ${$value.toLowerCase()} invocations in this step (excludes children)">
170
+ ${MiniProfiler.formatDuration(timing.CustomTimingStats[$value].Duration)}
171
+ </td>
172
+ {{else}}
173
+ <td colspan="2"></td>
174
+ {{/if}}
175
+ {{/each}}
176
+
152
177
  </tr>
153
178
 
154
- {{if HasChildren}}
155
- {{each Children}}
156
- {{tmpl($value) "#timingTemplate"}}
179
+ {{if timing.HasChildren}}
180
+ {{each timing.Children}}
181
+ {{tmpl({timing: $value, page: page}) "#timingTemplate"}}
157
182
  {{/each}}
158
183
  {{/if}}
159
184