home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 June / PersonalComputerWorld-June2009-CoverdiscCD.iso / Software / Freeware / Firebug 1.3.3 / firebug-1.3.3-fx.xpi / content / firebug / profiler.js < prev    next >
Encoding:
JavaScript  |  2009-02-19  |  12.7 KB  |  404 lines

  1. /* See license.txt for terms of usage */
  2.  
  3.  
  4. FBL.ns(function() { with (FBL) {
  5.  
  6. var toggleProfiling = $("fbToggleProfiling");
  7.  
  8. // ************************************************************************************************
  9.  
  10. Firebug.Profiler = extend(Firebug.Module,
  11. {
  12.     showContext: function(browser, context)
  13.     {
  14.         this.setEnabled(context);
  15.     },
  16.  
  17.     onPanelActivate: function(context, init, panelName)
  18.     {
  19.         if (panelName == "net" || panelName == "script")
  20.             this.setEnabled(context);
  21.     },
  22.  
  23.     onPanelDeactivate: function(context, init, panelName)
  24.     {
  25.        if (panelName == "net" || panelName == "script")
  26.             this.setEnabled(context);
  27.     },
  28.  
  29.     setEnabled: function(context)
  30.     {
  31.         // The profiler is available only if the debugger (script panel) and console are enabled.
  32.         var debuggerEnabled = Firebug.Debugger.isEnabled(context);
  33.         var consoleEnabled = Firebug.Console.isEnabled(context);
  34.         toggleProfiling.disabled = !debuggerEnabled || !consoleEnabled;
  35.  
  36.         // Update button's tooltip.
  37.         var tooltipText = toggleProfiling.disabled ? $STR("ProfileButton.Disabled.Tooltip")
  38.             : $STR("ProfileButton.Enabled.Tooltip");
  39.         toggleProfiling.setAttribute("tooltiptext", tooltipText);
  40.     },
  41.  
  42.     toggleProfiling: function(context)
  43.     {
  44.         if (fbs.profiling)
  45.             this.stopProfiling(context);
  46.         else
  47.             this.startProfiling(context);
  48.     },
  49.  
  50.     startProfiling: function(context, title)
  51.     {
  52.         fbs.startProfiling();
  53.  
  54.         context.chrome.setGlobalAttribute("cmd_toggleProfiling", "checked", "true");
  55.  
  56.         var isCustomMessage = !!title;
  57.         if (!isCustomMessage)
  58.             title = $STR("ProfilerStarted");
  59.  
  60.         context.profileRow = this.logProfileRow(context, title);
  61.         context.profileRow.customMessage = isCustomMessage ;
  62.     },
  63.  
  64.     stopProfiling: function(context, cancelReport)
  65.     {
  66.         var totalTime = fbs.stopProfiling();
  67.         if (totalTime == -1)
  68.             return;
  69.  
  70.         context.chrome.setGlobalAttribute("cmd_toggleProfiling", "checked", "false");
  71.  
  72.         if (cancelReport)
  73.             delete context.profileRow;
  74.         else
  75.             this.logProfileReport(context)
  76.     },
  77.  
  78.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  79.  
  80.     logProfileRow: function(context, title)
  81.     {
  82.         var row = Firebug.Console.openGroup(title, context, "profile",
  83.             Firebug.Profiler.ProfileCaption, true, null, true);
  84.         setClass(row, "profilerRunning");
  85.  
  86.         Firebug.Console.closeGroup(context, true);
  87.  
  88.         return row;
  89.     },
  90.  
  91.     logProfileReport: function(context)
  92.     {
  93.         var calls = [];
  94.         var totalCalls = 0;
  95.         var totalTime = 0;
  96.  
  97.         var sourceFileMap = context.sourceFileMap;
  98.         jsd.enumerateScripts({enumerateScript: function(script)
  99.         {
  100.             if (script.callCount)
  101.             {
  102.                 var sourceLink = FBL.getSourceLinkForScript(script, context);
  103.                 if (sourceLink && sourceLink.href in sourceFileMap)
  104.                 {
  105.                     var call = new ProfileCall(script, context, script.callCount, script.totalExecutionTime,
  106.                         script.totalOwnExecutionTime, script.minExecutionTime, script.maxExecutionTime, sourceLink);
  107.                     calls.push(call);
  108.  
  109.                     totalCalls += script.callCount;
  110.                     totalTime += script.totalOwnExecutionTime;
  111.  
  112.                     script.clearProfileData();
  113.                 }
  114.             }
  115.         }});
  116.  
  117.         for (var i = 0; i < calls.length; ++i)
  118.             calls[i].percent = Math.round((calls[i].totalOwnTime/totalTime) * 100 * 100) / 100;
  119.  
  120.         calls.sort(function(a, b)
  121.         {
  122.            return a.totalOwnTime < b.totalOwnTime ? 1 : -1;
  123.         });
  124.  
  125.         totalTime = Math.round(totalTime * 1000) / 1000;
  126.  
  127.         var groupRow = context.profileRow && context.profileRow.ownerDocument
  128.             ? context.profileRow
  129.             : this.logProfileRow(context, "");
  130.         delete context.profileRow;
  131.  
  132.         removeClass(groupRow, "profilerRunning");
  133.  
  134.         if (totalCalls > 0)
  135.         {
  136.             var captionBox = getElementByClass(groupRow, "profileCaption");
  137.             if (!groupRow.customMessage)
  138.                 captionBox.textContent = $STR("Profile");
  139.             var timeBox = getElementByClass(groupRow, "profileTime");
  140.             timeBox.textContent = $STRF("ProfileTime", [totalTime, totalCalls]);
  141.  
  142.             var groupBody = groupRow.lastChild;
  143.             var sizer = Firebug.Profiler.ProfileTable.tag.replace({}, groupBody);
  144.             var table = sizer.firstChild;
  145.             var tHeader = table.lastChild;  // no rows inserted.
  146.  
  147.             var tag = Firebug.Profiler.ProfileCall.tag;
  148.             var insert = tag.insertRows;
  149.  
  150.             for (var i = 0; i < calls.length; ++i) {
  151.                 calls[i].index = i;
  152.                 context.throttle(insert, tag, [{object: calls[i]}, tHeader]);
  153.             }
  154.  
  155.             context.throttle(groupRow.scrollIntoView, groupRow);
  156.         }
  157.         else
  158.         {
  159.             var captionBox = getElementByClass(groupRow, "profileCaption");
  160.             captionBox.textContent = $STR("NothingToProfile");
  161.         }
  162.     }
  163. });
  164.  
  165. // ************************************************************************************************
  166.  
  167. Firebug.Profiler.ProfileTable = domplate(
  168. {
  169.     tag:
  170.       DIV({class: "profileSizer" },
  171.         TABLE({class: "profileTable", cellspacing: 0, cellpadding: 0, width: "100%"},
  172.             TBODY({class: "profileTbody"},
  173.                 TR({class: "headerRow", onclick: "$onClick"},
  174.                     TD({class: "headerCell alphaValue"},
  175.                         DIV({class: "headerCellBox"},
  176.                             $STR("Function")
  177.                         )
  178.                     ),
  179.                     TD({class: "headerCell"},
  180.                         DIV({class: "headerCellBox", title: $STR("CallsHeaderTooltip")},
  181.                             $STR("Calls")
  182.                         )
  183.                     ),
  184.                     TD({class: "headerCell headerSorted"},
  185.                         DIV({class: "headerCellBox", title: $STR("PercentTooltip")},
  186.                             $STR("Percent")
  187.                         )
  188.                     ),
  189.                     TD({class: "headerCell"},
  190.                         DIV({class: "headerCellBox", title: $STR("OwnTimeHeaderTooltip")},
  191.                             $STR("OwnTime")
  192.                         )
  193.                     ),
  194.                     TD({class: "headerCell"},
  195.                         DIV({class: "headerCellBox", title: $STR("TimeHeaderTooltip")},
  196.                             $STR("Time")
  197.                         )
  198.                     ),
  199.                     TD({class: "headerCell"},
  200.                         DIV({class: "headerCellBox", title: $STR("AvgHeaderTooltip")},
  201.                             $STR("Avg")
  202.                         )
  203.                     ),
  204.                     TD({class: "headerCell"},
  205.                         DIV({class: "headerCellBox", title: $STR("MinHeaderTooltip")},
  206.                             $STR("Min")
  207.                         )
  208.                     ),
  209.                     TD({class: "headerCell"},
  210.                         DIV({class: "headerCellBox", title: $STR("MaxHeaderTooltip")},
  211.                             $STR("Max")
  212.                         )
  213.                     ),
  214.                     TD({class: "headerCell alphaValue"},
  215.                         DIV({class: "headerCellBox"},
  216.                             $STR("File")
  217.                         )
  218.                     )
  219.                 )
  220.             )
  221.           )
  222.         ),
  223.  
  224.     onClick: function(event)
  225.     {
  226.         var table = getAncestorByClass(event.target, "profileTable");
  227.         var header = getAncestorByClass(event.target, "headerCell");
  228.         if (!header)
  229.             return;
  230.  
  231.         var numerical = !hasClass(header, "alphaValue");
  232.  
  233.         var colIndex = 0;
  234.         for (header = header.previousSibling; header; header = header.previousSibling)
  235.             ++colIndex;
  236.  
  237.         this.sort(table, colIndex, numerical);
  238.     },
  239.  
  240.     sort: function(table, colIndex, numerical)
  241.     {
  242.         var tbody = getChildByClass(table, "profileTbody");
  243.  
  244.         var values = [];
  245.         for (var row = tbody.childNodes[1]; row; row = row.nextSibling)
  246.         {
  247.             var cell = row.childNodes[colIndex];
  248.             var value = numerical ? parseFloat(cell.textContent) : cell.textContent;
  249.             values.push({row: row, value: value});
  250.         }
  251.  
  252.         values.sort(function(a, b) { return a.value < b.value ? -1 : 1; });
  253.  
  254.         var headerRow = tbody.firstChild;
  255.         var headerSorted = getChildByClass(headerRow, "headerSorted");
  256.         removeClass(headerSorted, "headerSorted");
  257.  
  258.         var header = headerRow.childNodes[colIndex];
  259.         setClass(header, "headerSorted");
  260.  
  261.         if (!header.sorted || header.sorted == 1)
  262.         {
  263.             removeClass(header, "sortedDescending");
  264.             setClass(header, "sortedAscending");
  265.  
  266.             header.sorted = -1;
  267.  
  268.             for (var i = 0; i < values.length; ++i) {
  269.                 values[i].row.setAttribute("odd", (i % 2));
  270.                 tbody.appendChild(values[i].row);
  271.             }
  272.         }
  273.         else
  274.         {
  275.             removeClass(header, "sortedAscending");
  276.             setClass(header, "sortedDescending");
  277.  
  278.             header.sorted = 1;
  279.  
  280.             for (var i = values.length-1; i >= 0; --i) {
  281.                 values[i].row.setAttribute("odd", (Math.abs(i-values.length-1) % 2));
  282.                 tbody.appendChild(values[i].row);
  283.             }
  284.         }
  285.     }
  286. });
  287.  
  288. // ************************************************************************************************
  289.  
  290. Firebug.Profiler.ProfileCaption = domplate(Firebug.Rep,
  291. {
  292.     tag:
  293.         SPAN({class: "profileTitle"},
  294.             SPAN({class: "profileCaption"}, "$objects"),
  295.             " ",
  296.             SPAN({class: "profileTime"}, "")
  297.         )
  298. });
  299.  
  300. // ************************************************************************************************
  301.  
  302. Firebug.Profiler.ProfileCall = domplate(Firebug.Rep,
  303. {
  304.     tag:
  305.         TR({odd: "$object|isOddRow"},
  306.             TD(
  307.                 FirebugReps.OBJECTLINK("$object|getCallName")
  308.             ),
  309.             TD("$object.callCount"),
  310.             TD("$object.percent%"),
  311.             TD("$object.totalOwnTime|roundTime\\ms"),
  312.             TD("$object.totalTime|roundTime\\ms"),
  313.             TD("$object|avgTime|roundTime\\ms"),
  314.             TD("$object.minTime|roundTime\\ms"),
  315.             TD("$object.maxTime|roundTime\\ms"),
  316.             TD({class: "linkCell"},
  317.                 TAG(FirebugReps.SourceLink.tag, {object: "$object|getSourceLink"})
  318.             )
  319.         ),
  320.  
  321.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  322.  
  323.     isOddRow: function(call)
  324.     {
  325.         return (call.index % 2) ? 1 : 0;
  326.     },
  327.  
  328.     getCallName: function(call)
  329.     {
  330.         return cropString(getFunctionName(call.script, call.context), 60);
  331.     },
  332.  
  333.     avgTime: function(call)
  334.     {
  335.         return call.totalTime / call.callCount;
  336.     },
  337.  
  338.     getSourceLink: function(call)
  339.     {
  340.         return call.sourceLink;
  341.     },
  342.  
  343.     roundTime: function(ms)
  344.     {
  345.         return Math.round(ms * 1000) / 1000;
  346.     },
  347.  
  348.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  349.  
  350.     className: "profile",
  351.  
  352.     supportsObject: function(object)
  353.     {
  354.         return object instanceof ProfileCall;
  355.     },
  356.  
  357.     inspectObject: function(call, context)
  358.     {
  359.         var sourceLink = this.getSourceLink(call);
  360.         context.chrome.select(sourceLink);
  361.     },
  362.  
  363.     getTooltip: function(call)
  364.     {
  365.         try
  366.         {
  367.             var fn = call.script.functionObject.getWrappedValue();
  368.             return FirebugReps.Func.getTooltip(fn, call.context);
  369.         }
  370.         catch (exc)
  371.         {
  372.         }
  373.     },
  374.  
  375.     getContextMenuItems: function(call, target, context)
  376.     {
  377.         var fn = call.script.functionObject.getWrappedValue();
  378.         return FirebugReps.Func.getContextMenuItems(fn, call.script, context);
  379.     }
  380. });
  381.  
  382. // ************************************************************************************************
  383.  
  384. function ProfileCall(script, context, callCount, totalTime, totalOwnTime, minTime, maxTime, sourceLink)
  385. {
  386.     this.script = script;
  387.     this.context = context;
  388.     this.callCount = callCount;
  389.     this.totalTime = totalTime;
  390.     this.totalOwnTime = totalOwnTime;
  391.     this.minTime = minTime;
  392.     this.maxTime = maxTime;
  393.     this.sourceLink = sourceLink;
  394. }
  395.  
  396. // ************************************************************************************************
  397.  
  398. Firebug.registerModule(Firebug.Profiler);
  399. Firebug.registerRep(Firebug.Profiler.ProfileCall);
  400.  
  401. // ************************************************************************************************
  402.  
  403. }});
  404.