blob: c1fb68f79170e3c139315f79e7401c859dec1f5e [file] [log] [blame]
/*
* Copyright (C) 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
MetricsBubbleView = function(analyzer, queue)
{
BaseObject.call(this);
this.element = document.createElement("div");
this.element.classList.add("queue-view");
this._results = null;
this._queue = queue;
analyzer.addEventListener(Analyzer.Event.Starting, this._clearResults, this);
analyzer.addEventListener(Analyzer.Event.QueueResults, this._queueResultsAdded, this);
this._updateSoon();
};
MetricsBubbleView.UpdateSoonTimeout = 100; // 100 ms
BaseObject.addConstructorFunctions(MetricsBubbleView);
MetricsBubbleView.prototype = {
constructor: MetricsBubbleView,
__proto__: BaseObject.prototype,
_clearResults: function()
{
this._results = null;
this._updateSoon();
},
_queueResultsAdded: function(event)
{
if (this._queue.id !== event.data.queueID)
return;
this._results = event.data;
this._updateSoon();
},
_update: function()
{
this.element.removeChildren();
function addLine(element, text)
{
var line = document.createElement("div");
line.textContent = text;
line.classList.add("result-line");
element.appendChild(line);
}
function addError(element, text)
{
addLine(element, text);
element.lastChild.classList.add("error-line");
}
function addDivider(element)
{
var divider = document.createElement("div");
divider.classList.add("divider");
element.appendChild(divider);
}
function pluralizeMinutes(intervalInSeconds)
{
if (intervalInSeconds < 60)
return "less than a minute";
else if (intervalInSeconds < 120)
return "1\xa0minute";
else
return Math.round(intervalInSeconds / 60) + "\xa0minutes";
}
function formatPercentage(fraction)
{
if (fraction > 0 && fraction < 0.01)
return "< 1%";
else
return Math.round(fraction * 100) + "%";
}
if (!this._results)
return;
if (this._queue.id === "style-queue") {
addDivider(this.element);
addLine(this.element, "Time to result:");
addLine(this.element, "- median: " + pluralizeMinutes(this._results.medianTotalTimeInSeconds) + ";");
addLine(this.element, "- average: " + pluralizeMinutes(this._results.averageTotalTimeInSeconds) + ".");
} else {
if (this._results.totalPatches !== this._results.patchesWithRetriesCount) {
addDivider(this.element);
var numberOfPatchesThatHadFinalResultsAtFirstTry = this._results.totalPatches - this._results.patchesWithRetriesCount - this._results.patchesThatDidNotCompleteCount + this._results.patchesThatSpinnedAndDidNotCompleteCount;
var text = formatPercentage(numberOfPatchesThatHadFinalResultsAtFirstTry / this._results.totalPatches) + " of patches ";
text += (this._queue.id === "commit-queue") ? "were landed or rejected" : "had final results";
text += " at first try. Time to result:"
addLine(this.element, text);
addLine(this.element, "- median: " + pluralizeMinutes(this._results.medianTotalTimeForPatchesThatWereNotRetriedInSeconds) + ";");
addLine(this.element, "- average: " + pluralizeMinutes(this._results.averageTotalTimeForPatchesThatWereNotRetriedInSeconds) + ".");
if (this._results.patchesThatDidNotApplyCount !== this._results.patchesThatSpinnedAndCeasedToApplyCount) {
if (this._results.patchesThatDidNotApplyCount - this._results.patchesThatSpinnedAndCeasedToApplyCount === numberOfPatchesThatHadFinalResultsAtFirstTry)
addLine(this.element, "None of these patches applied to trunk.");
else
addLine(this.element, "This includes " + formatPercentage((this._results.patchesThatDidNotApplyCount - this._results.patchesThatSpinnedAndCeasedToApplyCount) / this._results.totalPatches) + " that did not apply to trunk.");
}
}
if (this._results.patchesThatDidNotCompleteCount !== this._results.patchesThatSpinnedAndDidNotCompleteCount) {
addDivider(this.element);
addLine(this.element, formatPercentage((this._results.patchesThatDidNotCompleteCount - this._results.patchesThatSpinnedAndDidNotCompleteCount) / this._results.totalPatches) + " of patches ceased to be eligible for processing before the first try finished.");
}
if (this._results.patchesWithRetriesCount) {
addDivider(this.element);
var text = formatPercentage(this._results.patchesWithRetriesCount / this._results.totalPatches) + " of patches had to be retried";
if (this._results.patchesThatSpinnedAndDidNotCompleteCount) {
text += ", including " + formatPercentage(this._results.patchesThatSpinnedAndDidNotCompleteCount / this._results.totalPatches) + " that kept being retried until the patch became ineligible for processing";
if (this._results.patchesThatSpinnedAndCeasedToApplyCount)
text += ", and " + formatPercentage(this._results.patchesThatSpinnedAndCeasedToApplyCount / this._results.totalPatches) + " that kept being retried until the patch ceased to apply to trunk.";
else
text += ".";
} else if (this._results.patchesThatSpinnedAndCeasedToApplyCount)
text += ", including " + formatPercentage(this._results.patchesThatSpinnedAndCeasedToApplyCount / this._results.totalPatches) + " that were spinning until the patch ceased to apply to trunk.";
else
text += ".";
if (this._results.patchesThatSpinnedAndPassedOrFailedCount) {
text += (this._results.patchesWithRetriesCount === this._results.patchesThatSpinnedAndPassedOrFailedCount) ? " All of them" : " " + formatPercentage(this._results.patchesThatSpinnedAndPassedOrFailedCount / this._results.totalPatches);
text += " finally produced a result, which took:";
addLine(this.element, text);
addLine(this.element, "- median: " + pluralizeMinutes(this._results.medianTotalTimeForPatchesThatSpinnedAndPassedOrFailedInSeconds) + ";");
addLine(this.element, "- average: " + pluralizeMinutes(this._results.averageTotalTimeForPatchesThatSpinnedAndPassedOrFailedInSeconds) + ".");
} else
addLine(this.element, text);
}
}
addDivider(this.element);
addLine(this.element, "Wait time before processing started:");
addLine(this.element, "- median: " + pluralizeMinutes(this._results.medianWaitTimeInSeconds) + ";");
addLine(this.element, "- average: " + pluralizeMinutes(this._results.averageWaitTimeInSeconds) + ".");
addLine(this.element, "- worst: " + pluralizeMinutes(this._results.maximumWaitTimeInSeconds) + ".");
if (this._results.medianWaitTimeInSeconds < 3 * 60 && this._results.patchesThatWaitedMoreThan3MinutesCount)
addLine(this.element, formatPercentage(this._results.patchesThatWaitedMoreThan3MinutesCount / this._results.totalPatches) + " of patches had a wait time of more than 3 minutes.");
if (this._results.patchesThatCausedInternalError.length) {
addDivider(this.element);
if (this._results.patchesThatCausedInternalError.length === 1)
addError(this.element, "One patch caused internal error");
else
addError(this.element, this._results.patchesThatCausedInternalError.length + "\xa0patches caused internal error, please see patch numbers in console log.");
console.log("Patches that caused internal error for " + this._results.queueID + ": " + this._results.patchesThatCausedInternalError);
}
},
_updateSoon: function()
{
setTimeout(this._update.bind(this), MetricsBubbleView.UpdateSoonTimeout);
},
};