summaryrefslogtreecommitdiffstats
path: root/src/bm/bmrequest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bm/bmrequest.cpp')
-rw-r--r--src/bm/bmrequest.cpp702
1 files changed, 581 insertions, 121 deletions
diff --git a/src/bm/bmrequest.cpp b/src/bm/bmrequest.cpp
index 4da76f8..46d1bf1 100644
--- a/src/bm/bmrequest.cpp
+++ b/src/bm/bmrequest.cpp
@@ -104,6 +104,8 @@ BMRequest * BMRequest::create(const QByteArray &data, const QString &msgType)
request = new BMRequest_GetHistories(doc);
else if (type == "GetBMTree")
request = new BMRequest_GetBMTree(doc);
+ else if (type == "GetIXHistories")
+ request = new BMRequest_GetIXHistories(doc);
#ifdef BMDEBUG
else
qDebug() << "invalid request type:" << type;
@@ -4873,6 +4875,7 @@ QByteArray BMRequest_IndexGetValues::toRequestBuffer(QString *)
return xmlConvert(request);
}
+// ### OBSOLETE!
void BMRequest_IndexGetValues::computeIndexValues(
const QList<ResultHistoryInfo *> &rhInfos, int baseTimestamp, const QList<int> &evalTimestamps,
int medianWinSize, const QList<qreal> &baseDistSums, qreal minBaseDistSum,
@@ -5104,15 +5107,20 @@ void BMRequest_IndexGetValues::computeIndexValues(
// if (sizes.at(j) < size050percentile)
// continue;
- qreal targetValue = -1.0;
+ int targetPos = -1;
qreal distSum = -1.0;
- if (rhInfo->getSmoothValue(
- evalTimestamps.at(i), medianWinSize, &targetValue, false, &distSum)) {
- qreal baseValue = -1.0;
- ok = rhInfo->getSmoothValue(baseTimestamp, medianWinSize, &baseValue, true);
+ if (rhInfo->getSmoothPos(
+ evalTimestamps.at(i), medianWinSize, &targetPos, false, &distSum)) {
+
+ const qreal targetVal = rhInfo->value(targetPos);
+
+ int basePos = -1;
+ ok = rhInfo->getSmoothPos(baseTimestamp, medianWinSize, &basePos, true);
Q_ASSERT(ok);
- const qreal diff = BMMisc::log2diff(targetValue, baseValue, rhInfo->metric());
+ const qreal baseVal = rhInfo->value(basePos);
+
+ const qreal diff = BMMisc::log2diff(targetVal, baseVal, rhInfo->metric());
diffs.append(diff);
contr.append(j);
@@ -5167,7 +5175,6 @@ void BMRequest_IndexGetValues::computeIndexValues(
fprintf(stderr, "computing index values (100%% done)\n");
}
-
QByteArray BMRequest_IndexGetValues::toReplyBuffer()
{
QDomElement argsElem = doc.elementsByTagName("args").at(0).toElement();
@@ -5278,13 +5285,9 @@ QByteArray BMRequest_IndexGetValues::toReplyBuffer()
QSqlQuery *query;
-
- // *****************************************************************************************
- // *** PHASE 1: Build the list of result histories that will be examined for a
- // contribution at each evaluation timestamp.
-
- // Get the BM context ID and metric for all result histories that match the
- // logical OR-values. NOTE: Results measuring the "events" metric are skipped for now since
+ // Get the initial list of candidate result histories by getting the BM context ID and metric
+ // for all result histories that match the logical OR-values.
+ // NOTE: Results measuring the "events" metric are skipped for now since
// they seem to be of little value when computing the performance index (they are
// typically integers close to (and often at) zero) ...
query = createQuery();
@@ -5370,15 +5373,6 @@ QByteArray BMRequest_IndexGetValues::toReplyBuffer()
QList<ResultHistoryInfo *> rhInfos;
- // Statistics of rejected result histories:
- int nonPositiveRH = 0; // contains non-positive value
- int invalidBaseRH = 0; // contains too few values before base timestamp
-
- QList<qreal> baseDistSums; // sums of distances of values contributing to base timestamp
- qreal minBaseDistSum = -1.0;
- qreal maxBaseDistSum = -1.0;
-
-
// Loop over initial result history candidates ...
for (int i = 0; i < bmcontextIds.size(); ++i) {
@@ -5404,46 +5398,15 @@ QByteArray BMRequest_IndexGetValues::toReplyBuffer()
}
QList<int> timestamps;
QList<qreal> values;
- bool nonPositiveValueFound = false;
while (query->next()) {
timestamps += query->value(0).toInt();
- const qreal value = query->value(1).toDouble();
- if (value > 0.0) {
- values += value;
- } else {
- nonPositiveValueFound = true;
- break;
- }
+ values += query->value(1).toDouble();
}
deleteQuery(query);
- if (nonPositiveValueFound) {
- // Reject candidate since at least one non-positive value was found ...
- ++nonPositiveRH;
- continue;
- }
-
- qreal baseDistSum;
- if (!ResultHistoryInfo::findTargetPos(
- timestamps, baseTimestamp, medianWinSize, 0, &baseDistSum)) {
- // Reject candidate if the base timestamp is not preceded by enough values
- // (and thus has a higher risk of being an outlier) ...
- ++invalidBaseRH;
- continue;
- }
-
- // Accept candidate ...
ResultHistoryInfo *rhInfo =
new ResultHistoryInfo(bmcontextIds.at(i), timestamps, values, metrics.at(i));
rhInfos.append(rhInfo);
-
- baseDistSums.append(baseDistSum);
- if (baseDistSums.size() == 1) {
- minBaseDistSum = maxBaseDistSum = baseDistSum;
- } else {
- minBaseDistSum = qMin(minBaseDistSum, baseDistSum);
- maxBaseDistSum = qMax(maxBaseDistSum, baseDistSum);
- }
}
if (debugPrint)
@@ -5453,16 +5416,57 @@ QByteArray BMRequest_IndexGetValues::toReplyBuffer()
reply = QString("<reply type=\"%1\" >").arg(name());
+ int nonPositiveRH = 0;
+ int baseValuePos;
+
if (!rhInfos.isEmpty()) {
- computeIndexValues(
- rhInfos, baseTimestamp, evalTimestamps, medianWinSize, baseDistSums, minBaseDistSum,
- maxBaseDistSum, debugPrint, &reply);
- // Free dynamic memory ...
- for (int i = 0; i < rhInfos.size(); ++i)
- delete rhInfos.at(i);
- }
+ // Candidate result histories exist, so compute index values ...
+ IndexAlgorithm1 index(
+ rhInfos, baseTimestamp, evalTimestamps, medianWinSize, &nonPositiveRH);
+ if (!index.isValid()) {
+ return xmlConvert(
+ errorReply(
+ name(), QString("failed to compute index (1): %1").arg(index.invalidReason())));
+ }
+
+ QList<qreal> indexValues;
+ QList<int> contrCounts;
+ QList<QList<Index::RankedInfo> > topContr;
+ const int topContrLimit = 10; // ### hard-coded for now!
+ QString error_;
+ if (!index.computeValues(
+ &indexValues, &baseValuePos, &contrCounts, &error_, &topContr, topContrLimit)) {
+ return xmlConvert(
+ errorReply(name(), QString("failed to compute index (2): %1").arg(error_)));
+ }
+
+ // Add index values to reply ...
+ Q_ASSERT(indexValues.size() == evalTimestamps.size());
+ Q_ASSERT(indexValues.size() == contrCounts.size());
+ for (int i = 0; i < indexValues.size(); ++i) {
+ reply +=
+ QString("<value timestamp=\"%1\" indexValue=\"%2\" contributions=\"%3\" >")
+ .arg(evalTimestamps.at(i))
+ .arg(indexValues.at(i))
+ .arg(contrCounts.at(i));
+
+ for (int j = 0; j < topContr.at(i).size(); ++j) {
+ reply +=
+ QString(
+ "<rankedInfo id=\"%1\" basePos=\"%2\" diffPos1=\"%3\" "
+ "diffPos2=\"%4\" descr=\"%5\" />")
+ .arg(topContr.at(i).at(j).bmcontextId)
+ .arg(topContr.at(i).at(j).basePos)
+ .arg(topContr.at(i).at(j).diffPos1)
+ .arg(topContr.at(i).at(j).diffPos2)
+ .arg(topContr.at(i).at(j).descr);
+ }
+
+ reply += "</value>";
+ }
+ }
//----------------------------------------------------------------------------------------
@@ -5486,9 +5490,9 @@ QByteArray BMRequest_IndexGetValues::toReplyBuffer()
cacheKey_ = Cache::instance()->nextId();
reply += QString(
"<args baseTimestamp=\"%1\" medianWinSize=\"%2\" totalRH=\"%3\" nonPositiveRH=\"%4\" "
- "invalidBaseRH=\"%5\" cacheKey=\"%6\" /></reply>")
- .arg(baseTimestamp).arg(medianWinSize).arg(bmcontextIds.size())
- .arg(nonPositiveRH).arg(invalidBaseRH).arg(cacheKey_);
+ "baseValuePos=\"%5\" cacheKey=\"%6\" /></reply>")
+ .arg(baseTimestamp).arg(medianWinSize).arg(bmcontextIds.size()).arg(nonPositiveRH)
+ .arg(baseValuePos).arg(cacheKey_);
Cache::instance()->put(cacheKey_, reply);
return xmlConvert(reply);
@@ -5513,6 +5517,14 @@ void BMRequest_IndexGetValues::appendToFilterTable(
void BMRequest_IndexGetValues::handleReply_HTML(const QStringList &args) const
{
QString error;
+
+ error = doc.elementsByTagName("reply").at(0).toElement().attributeNode("error").value();
+ if (!error.isEmpty()) {
+ BMMisc::printHTMLErrorPage(
+ QString("failed to create details page: error evaluating index: %1").arg(error));
+ return;
+ }
+
QStringList optValues;
// Get server ...
@@ -5541,16 +5553,27 @@ void BMRequest_IndexGetValues::handleReply_HTML(const QStringList &args) const
reply += "<span style=\"font-size:18\">Qt Performance Index</span>";
reply += "<br /><br /><table><tr><td style=\"background-color:#eeeeee\">";
- reply += "This page shows the performance index evaluated at certain timestamps ";
- reply += "and for certain result histories (i.e. for certain benchmarks in certain contexts, ";
- reply += "where the context represents platform etc.).";
+
+ reply += "This page shows the performance index for certain benchmarks in certain contexts ";
+ reply += "(where the context represents platform etc.).";
+
reply += "<br /><br />";
- reply += "The index value at a given time shows the aggregated performance at that time ";
- reply += "relative to the aggregated performance at a given base time in terms of percentage ";
- reply += "of the latter. A value of 200 thus indicates a doubled performance (i.e. twice ";
- reply += "the FPS or half the walltime), while 50 indicates a halved performance.";
- reply += "</td></tr></table>";
+ reply += "The <u>change</u> in index value between any two points in time indicates the ";
+ reply += "average performance increase during this time period in terms of a log2-based ";
+ reply += "difference. ";
+ reply += "An index value <u>change</u> of 1, 0, and -1 thus indicates a doubled, unchanged, ";
+ reply += "and halved performance respectively. ";
+ reply += "(The corresponding linear factors would be 2, 1, and 0.5.).";
+
+ reply += "<br /><br />";
+
+ reply += "<b>Note:</b> Individual index values bear no useful meaning when regarded in ";
+ reply += "isolation. The graph is however shifted vertically so that each individual index ";
+ reply += "value indicates the change from the index value at the given base time (the index ";
+ reply += "value of the latter is then 0 and 1 in the log2 and linear domain respectively).";
+
+ reply += "</td></tr></table>\n";
QDomElement argsElem = doc.elementsByTagName("args").at(0).toElement();
@@ -5561,7 +5584,11 @@ void BMRequest_IndexGetValues::handleReply_HTML(const QStringList &args) const
// reply += QString("arg %1: &gt;%2&lt;<br />").arg(i).arg(args.at(i));
// reply += "<br />";
- reply += "<br />\n<img src=\"bmclientwrapper?command=";
+ // Image ...
+ reply +=
+ "<br />\n<img usemap=\"#plotmap\" style=\"border-style:none\" "
+ "src=\"bmclientwrapper?command=";
+
for (int i = args.indexOf("-server"); i < args.size(); ++i)
reply += QString("%1 ")
.arg((args.at(i) == "detailspage") ? QLatin1String("plot") : args.at(i));
@@ -5569,15 +5596,96 @@ void BMRequest_IndexGetValues::handleReply_HTML(const QStringList &args) const
reply += "-httpheader ";
reply += "\" />\n<br />\n<br />\n";
+ // Image map ...
+ reply += "<br />\n<map name=\"plotmap\">\n";
+ bool ok;
- error = doc.elementsByTagName("reply").at(0).toElement().attributeNode("error").value();
- if (!error.isEmpty()) {
- reply = QString("failed to create table: error evaluating index: %1").arg(error);
- BMMisc::printHTMLErrorPage(reply);
+ // ... get timestamps, index values, and contributions ...
+ QList<int> timestamps;
+ QList<qreal> indexValues;
+ QList<qreal> linearIndexValues;
+ QList<int> contributions;
+ QDomNodeList valueNodes = doc.elementsByTagName("value");
+ for (int i = 0; i < valueNodes.size(); ++i) {
+ QDomElement valueElem = valueNodes.at(i).toElement();
+
+ timestamps += valueElem.attributeNode("timestamp").value().toInt(&ok);
+ Q_ASSERT(ok);
+
+ const qreal indexValue = valueElem.attributeNode("indexValue").value().toDouble(&ok);
+ Q_ASSERT(ok);
+ indexValues += indexValue;
+ linearIndexValues += qPow(2, indexValue);
+
+ contributions += valueElem.attributeNode("contributions").value().toInt(&ok);
+ Q_ASSERT(ok);
+ }
+
+ // ... get point infos ...
+ Plotter *plotter = new IndexPlotter(timestamps, indexValues, contributions);
+ QList<Plotter::PointInfo> pointInfos;
+ if (plotter->createImage(&error, &pointInfos).isNull()) {
+ BMMisc::printHTMLErrorPage(
+ QString("failed to extract point infos: %1").arg(error));
+ return;
+ }
+ Q_ASSERT(pointInfos.size() == timestamps.size());
+
+ // ... extract the web server from the style sheet ...
+ QRegExp rx("^(\\S+:\\d+)\\D+$");
+ if (rx.indexIn(styleSheet) == -1) {
+ BMMisc::printHTMLErrorPage(QString("failed to extract web server from style sheet"));
return;
}
+ const QString webServer = rx.cap(1);
+
+ // ... add <area> tags for valid points ...
+ for (int i = 0; i < pointInfos.size(); ++i) {
+ if (contributions.at(i) == 0)
+ continue; // skip invalid point
+
+ QString url = QString("%1/cgi-bin/bmclientwrapper").arg(webServer);
+ url += QString("?command=-server %1").arg(server);
+ url += QString(" get ixhistories detailspage -stylesheet %1").arg(styleSheet);
+ url += QString(" -evaltimestamp %1").arg(timestamps.at(i));
+
+ QDomElement valueElem = valueNodes.at(i).toElement();
+
+ QDomNodeList rankedInfoNodes = valueElem.elementsByTagName("rankedInfo");
+ if (rankedInfoNodes.size() == 0)
+ continue; // skip if no ranked infos were found (typically the case for for the first
+ // valid point)
+
+ for (int j = 0; j < rankedInfoNodes.size(); ++j) {
+ QDomElement rankedInfoElem = rankedInfoNodes.at(j).toElement();
+
+ const int bmcontextId = rankedInfoElem.attributeNode("id").value().toInt(&ok);
+ Q_ASSERT(ok);
+ const int basePos = rankedInfoElem.attributeNode("basePos").value().toInt(&ok);
+ Q_ASSERT(ok);
+ const int diffPos1 = rankedInfoElem.attributeNode("diffPos1").value().toInt(&ok);
+ Q_ASSERT(ok);
+ const int diffPos2 = rankedInfoElem.attributeNode("diffPos2").value().toInt(&ok);
+ Q_ASSERT(ok);
+ const QString descr = rankedInfoElem.attributeNode("descr").value();
+
+ url += QString(" -rankedinfo %1 %2 %3 %4 '%5'")
+ .arg(bmcontextId).arg(basePos).arg(diffPos1).arg(diffPos2).arg(descr);
+ }
+
+ QRect rect = pointInfos.at(i).rect.toAlignedRect();
+ reply += QString(
+ "<area shape=\"rect\" coords=\"%1,%2,%3,%4\" title=\"%5\" href=\"%6\" />\n")
+ .arg(rect.topLeft().x())
+ .arg(rect.topLeft().y())
+ .arg(rect.bottomRight().x())
+ .arg(rect.bottomRight().y())
+ .arg(pointInfos.at(i).value)
+ .arg(url);
+ }
+
+ reply += "</map>\n";
- bool ok;
QDateTime dateTime;
@@ -5637,12 +5745,7 @@ void BMRequest_IndexGetValues::handleReply_HTML(const QStringList &args) const
reply +=
QString("<tr><td>Rejected due to existence of non-positive values:</td><td>%1</td></tr>\n")
.arg(nonPositiveRH);
- const int invalidBaseRH = argsElem.attributeNode("invalidBaseRH").value().toInt(&ok);
- Q_ASSERT(ok);
- reply += QString(
- "<tr><td>Rejected due to too few values before the base time:</td><td>%1</td></tr>\n")
- .arg(invalidBaseRH);
- const int finalTotalRH = totalRH - (nonPositiveRH + invalidBaseRH);
+ const int finalTotalRH = totalRH - nonPositiveRH;
Q_ASSERT(ok);
reply += QString("<tr><td>Final set:</td><td>%1</td></tr>\n").arg(finalTotalRH);
reply += "</table>\n";
@@ -5652,19 +5755,14 @@ void BMRequest_IndexGetValues::handleReply_HTML(const QStringList &args) const
reply += "\n<br /><br /><table style=\"border: 0px\">\n";
reply += "<tr style=\"border: 0px\">\n";
- QDomNodeList valueNodes = doc.elementsByTagName("value");
-
- // Left table ...
+ // Table 1 ...
reply += "<td style=\"border: 0px\"><table>\n";
reply += "<tr><th></th><th>Date</th><th>Contributions</th></tr>\n";
for (int i = 0; i < valueNodes.size(); ++i) {
const int i_ = (valueNodes.size() - 1) - i;
- QDomElement valueElem = valueNodes.at(i_).toElement();
- const int timestamp = valueElem.attributeNode("timestamp").value().toInt(&ok);
- Q_ASSERT(ok);
- const int contributions = valueElem.attributeNode("contributions").value().toInt(&ok);
- Q_ASSERT(ok);
+ const int timestamp = timestamps.at(i_);
+ const int contributions_ = contributions.at(i_);
const QString style = QString("%1")
.arg((timestamp >= baseTimestamp)
@@ -5677,23 +5775,21 @@ void BMRequest_IndexGetValues::handleReply_HTML(const QStringList &args) const
reply += QString("<td class=\"%1\">%2</td>").arg(style).arg(dateTime.toString());
reply += QString("<td class=\"%1\">%2%3</td>")
.arg(style)
- .arg(contributions)
+ .arg(contributions_)
.arg((finalTotalRH == 0)
? QString()
- : QString(" (%1%)").arg(100 * (qreal(contributions) / finalTotalRH), 0, 'f', 2));
+ : QString(" (%1%)").arg(100 * (qreal(contributions_) / finalTotalRH), 0, 'f', 2));
reply += "</tr>\n";
}
reply += "</td></table>\n";
- // Middle table ...
+ // Table 2 ...
reply += "<td style=\"border: 0px\"><table>\n";
reply += "<tr><th>Timestamp</th></tr>\n";
for (int i = 0; i < valueNodes.size(); ++i) {
const int i_ = (valueNodes.size() - 1) - i;
- QDomElement valueElem = valueNodes.at(i_).toElement();
- const int timestamp = valueElem.attributeNode("timestamp").value().toInt(&ok);
- Q_ASSERT(ok);
+ const int timestamp = timestamps.at(i_);
const QString style = QString("%1")
.arg((timestamp >= baseTimestamp)
@@ -5706,28 +5802,29 @@ void BMRequest_IndexGetValues::handleReply_HTML(const QStringList &args) const
}
reply += "</td></table>\n";
- // Right table ...
+ const int baseValuePos = argsElem.attributeNode("baseValuePos").value().toInt(&ok);
+ Q_ASSERT(ok);
+ const qreal baseValue = indexValues.at(baseValuePos);
+ const qreal linearBaseValue = qPow(2, baseValue);
+
+ // Table 3 (log2 base diff factor) ...
reply += "<td style=\"border: 0px\"><table>\n";
- reply += "<tr><th>Value</th></tr>\n";
+ reply += "<tr><th>Base diff (log2)</th></tr>\n";
for (int i = 0; i < valueNodes.size(); ++i) {
const int i_ = (valueNodes.size() - 1) - i;
- QDomElement valueElem = valueNodes.at(i_).toElement();
- const qreal indexValue = valueElem.attributeNode("indexValue").value().toDouble(&ok);
- Q_ASSERT(ok);
- const qreal powIndexValue = 100 * qPow(2, indexValue);
- const int contributions = valueElem.attributeNode("contributions").value().toInt(&ok);
- Q_ASSERT(ok);
+ const qreal indexValue = indexValues.at(i_) - baseValue;
+ const int contributions_ = contributions.at(i_);
const QString style = "index_table";
reply += "<tr>";
- if (contributions > 0) {
+ if (contributions_ > 0) {
reply += QString(
"<td class=\"%1\" style=\"background-color:%2\">%3</td>")
.arg(style)
.arg(BMMisc::redToGreenColor(indexValue, 0.01, 0.5, true))
- .arg(powIndexValue);
+ .arg(indexValue);
} else {
reply += QString(
"<td class=\"%1\" style=\"color:red; background-color:white\">n/a</td>").arg(style);
@@ -5736,6 +5833,32 @@ void BMRequest_IndexGetValues::handleReply_HTML(const QStringList &args) const
}
reply += "</td></table>\n";
+ // Table 4 (linear base diff factor) ...
+ reply += "<td style=\"border: 0px\"><table>\n";
+ reply += "<tr><th>Base diff (linear)</th></tr>\n";
+
+ for (int i = 0; i < valueNodes.size(); ++i) {
+ const int i_ = (valueNodes.size() - 1) - i;
+ const qreal indexValue = indexValues.at(i_) - baseValue;
+ const qreal linearIndexValue = linearIndexValues.at(i_) - linearBaseValue;
+ const int contributions_ = contributions.at(i_);
+
+ const QString style = "index_table";
+
+ reply += "<tr>";
+ if (contributions_ > 0) {
+ reply += QString(
+ "<td class=\"%1\" style=\"background-color:%2\">%3</td>")
+ .arg(style)
+ .arg(BMMisc::redToGreenColor(indexValue, 0.01, 0.5, true))
+ .arg(linearIndexValue);
+ } else {
+ reply += QString(
+ "<td class=\"%1\" style=\"color:red; background-color:white\">n/a</td>").arg(style);
+ }
+ reply += "</tr>\n";
+ }
+ reply += "</td></table>\n";
reply += "</table>\n";
@@ -5758,6 +5881,8 @@ void BMRequest_IndexGetValues::handleReply_Image(const QStringList &args) const
const int baseTimestamp = argsElem.attributeNode("baseTimestamp").value().toInt(&ok);
Q_ASSERT(ok);
+ const int baseValuePos = argsElem.attributeNode("baseValuePos").value().toInt(&ok);
+ Q_ASSERT(ok);
// Get timestamps, index values, and contributions ...
QList<int> timestamps;
@@ -5772,7 +5897,7 @@ void BMRequest_IndexGetValues::handleReply_Image(const QStringList &args) const
const qreal indexValue = valueElem.attributeNode("indexValue").value().toDouble(&ok);
Q_ASSERT(ok);
- indexValues += 100 * qPow(2, indexValue);
+ indexValues += indexValue;
contributions += valueElem.attributeNode("contributions").value().toInt(&ok);
Q_ASSERT(ok);
@@ -5780,7 +5905,8 @@ void BMRequest_IndexGetValues::handleReply_Image(const QStringList &args) const
// Create the plot ...
QString error_;
- Plotter *plotter = new IndexPlotter(timestamps, indexValues, contributions, baseTimestamp);
+ Plotter *plotter =
+ new IndexPlotter(timestamps, indexValues, contributions, baseTimestamp, baseValuePos);
const QImage image = plotter->createImage(&error_);
if (!image.isNull()) {
BMMisc::printImageOutput(
@@ -6501,9 +6627,15 @@ QByteArray BMRequest_GetHistories::toReplyBuffer()
void BMRequest_GetHistories::handleReply_HTML(const QStringList &args) const
{
- // 2 B DONE!
-
QString error;
+
+ error = doc.elementsByTagName("reply").at(0).toElement().attributeNode("error").value();
+ if (!error.isEmpty()) {
+ BMMisc::printHTMLErrorPage(
+ QString("failed to create details page: error getting histories: %1").arg(error));
+ return;
+ }
+
QStringList optValues;
// Get server ...
@@ -6578,14 +6710,6 @@ void BMRequest_GetHistories::handleReply_HTML(const QStringList &args) const
reply += "-httpheader ";
reply += "\" />\n<br />\n<br />\n";
-
- error = doc.elementsByTagName("reply").at(0).toElement().attributeNode("error").value();
- if (!error.isEmpty()) {
- reply = QString("failed to create details page: error getting histories: %1").arg(error);
- BMMisc::printHTMLErrorPage(reply);
- return;
- }
-
reply += "</body></html>";
BMMisc::printHTMLOutput(reply);
@@ -6824,3 +6948,339 @@ void BMRequest_GetBMTree::handleReply_JSON(const QStringList &args) const
BMMisc::printJSONOutput(reply);
}
+
+
+// --- GetIXHistories ---
+QByteArray BMRequest_GetIXHistories::toRequestBuffer(QString *)
+{
+ QString request =
+ QString(
+ "<request type=\"%1\"><args evalTimestamp=\"%2\" cacheKey=\"%3\" />")
+ .arg(name()).arg(evalTimestamp).arg(cacheKey);
+
+ for (int i = 0; i < rankedInfos.size(); ++i)
+ request += QString(
+ "<rankedInfo id=\"%1\" basePos=\"%2\" diffPos1=\"%3\" diffPos2=\"%4\" descr=\"%5\" />")
+ .arg(rankedInfos.at(i).bmcontextId)
+ .arg(rankedInfos.at(i).basePos)
+ .arg(rankedInfos.at(i).diffPos1)
+ .arg(rankedInfos.at(i).diffPos2)
+ .arg(rankedInfos.at(i).descr);
+
+ request += "</request>";
+
+ return xmlConvert(request);
+}
+
+QByteArray BMRequest_GetIXHistories::toReplyBuffer()
+{
+ QDomElement argsElem = doc.elementsByTagName("args").at(0).toElement();
+
+ QString error;
+ QString reply;
+ bool ok;
+
+ // Check if a cached reply can be returned ...
+ int cacheKey_ = argsElem.attributeNode("cacheKey").value().toInt(&ok);
+ if (ok) {
+ reply = Cache::instance()->get(cacheKey_);
+ if (!reply.isEmpty())
+ return xmlConvert(reply);
+ }
+
+ // Compute from scratch ...
+
+ // Get eval timestamp ...
+ evalTimestamp = argsElem.attributeNode("evalTimestamp").value().toInt(&ok);
+ Q_ASSERT(ok);
+
+ // Get ranked infos ...
+ QDomNodeList rankedInfoNodes = doc.elementsByTagName("rankedInfo");
+ for (int i = 0; i < rankedInfoNodes.size(); ++i) {
+ QDomElement rankedInfoElem = rankedInfoNodes.at(i).toElement();
+ const int bmcontextId = rankedInfoElem.attributeNode("id").value().toInt(&ok);
+ Q_ASSERT(ok);
+ const int basePos = rankedInfoElem.attributeNode("basePos").value().toInt(&ok);
+ Q_ASSERT(ok);
+ const int diffPos1 = rankedInfoElem.attributeNode("diffPos1").value().toInt(&ok);
+ Q_ASSERT(ok);
+ const int diffPos2 = rankedInfoElem.attributeNode("diffPos2").value().toInt(&ok);
+ Q_ASSERT(ok);
+ const QString descr = rankedInfoElem.attributeNode("descr").value();
+
+ rankedInfos.append(Index::RankedInfo(bmcontextId, basePos, diffPos1, diffPos2, descr));
+ }
+
+ QSqlQuery *query;
+
+ reply = QString("<reply type=\"%1\" >").arg(name());
+
+ // Add result histories to reply ...
+ for (int i = 0; i < rankedInfos.size(); ++i) {
+
+ // Get meta info ...
+ query = createQuery();
+ if (!query->exec(
+ QString(
+ "SELECT testCase, testFunction, dataTag, metric.name, platform.name, "
+ " host.name, branch.gitRepo, branch.gitBranch "
+ " FROM bmcontext, benchmark, context, metric, platform, host, branch"
+ " WHERE benchmarkid = benchmark.id"
+ " AND contextId = context.id"
+ " AND metricId = metric.id"
+ " AND platformId = platform.id"
+ " AND hostId = host.id"
+ " AND branchId = branch.id"
+ " AND bmcontext.id = %1;")
+ .arg(rankedInfos.at(i).bmcontextId))) {
+ deleteQuery(query);
+ return xmlConvert(
+ errorReply(
+ *query, name(), "failed to get result history meta info (exec() failed)"));
+ }
+ QString testCase;
+ QString testFunction;
+ QString dataTag;
+ QString metric;
+ QString platform;
+ QString host;
+ QString gitRepo;
+ QString gitBranch;
+ while (query->next()) {
+ testCase = query->value(0).toString();
+ testFunction = query->value(1).toString();
+ dataTag = query->value(2).toString();
+ metric = query->value(3).toString();
+ platform = query->value(4).toString();
+ host = query->value(5).toString();
+ gitRepo = query->value(6).toString();
+ gitBranch = query->value(7).toString();
+ }
+ deleteQuery(query);
+
+ reply += QString(
+ "<resultHistory testCase=\"%1\" testFunction=\"%2\" dataTag=\"%3\" metric=\"%4\" "
+ "platform=\"%5\" host=\"%6\" gitRepo=\"%7\" gitBranch=\"%8\" basePos=\"%9\" "
+ "diffPos1=\"%10\" diffPos2=\"%11\" descr=\"%12\" >")
+ .arg(testCase)
+ .arg(testFunction)
+ .arg(dataTag)
+ .arg(metric)
+ .arg(platform)
+ .arg(host)
+ .arg(gitRepo)
+ .arg(gitBranch)
+ .arg(rankedInfos.at(i).basePos)
+ .arg(rankedInfos.at(i).diffPos1)
+ .arg(rankedInfos.at(i).diffPos2)
+ .arg(rankedInfos.at(i).descr);
+
+ // Get time series ...
+ query = createQuery();
+ if (!query->exec(
+ QString(
+ "SELECT timestamp, value FROM result "
+ "WHERE bmcontextId=%1 "
+ "ORDER BY timestamp ASC;")
+ .arg(rankedInfos.at(i).bmcontextId))) {
+ deleteQuery(query);
+ return xmlConvert(
+ errorReply(*query, name(), "failed to get time series (exec() failed)"));
+ }
+ QList<int> timestamps;
+ QList<qreal> values;
+ while (query->next()) {
+ timestamps += query->value(0).toInt();
+ values += query->value(1).toDouble();
+ }
+ deleteQuery(query);
+
+ for (int j = 0; j < timestamps.size(); ++j)
+ reply += QString("<val t=\"%1\" v=\"%2\" />").arg(timestamps.at(j)).arg(values.at(j));
+
+ reply += "</resultHistory>";
+ }
+
+ // Finalize the reply and cache it ...
+ cacheKey_ = Cache::instance()->nextId();
+ reply += QString("<args evalTimestamp=\"%1\" cacheKey=\"%2\" /></reply>")
+ .arg(evalTimestamp)
+ .arg(cacheKey_);
+ Cache::instance()->put(cacheKey_, reply);
+
+ return xmlConvert(reply);
+}
+
+void BMRequest_GetIXHistories::handleReply_HTML(const QStringList &args) const
+{
+ QString error;
+
+ error = doc.elementsByTagName("reply").at(0).toElement().attributeNode("error").value();
+ if (!error.isEmpty()) {
+ BMMisc::printHTMLErrorPage(
+ QString(
+ "failed to create details page for main index contributors: %1").arg(error));
+ return;
+ }
+
+ QStringList optValues;
+
+ // Get server ...
+ QString server;
+ if (BMMisc::getOption(args, "-server", &optValues, 1, 0, &error)) {
+ server = optValues.first().trimmed();
+ } else {
+ BMMisc::printHTMLErrorPage("-server option not found");
+ return;
+ }
+
+ // Get style sheet ...
+ QString styleSheet;
+ if (BMMisc::getOption(args, "-stylesheet", &optValues, 1, 0, &error)) {
+ styleSheet = optValues.first().trimmed();
+ } else {
+ BMMisc::printHTMLErrorPage("-stylesheet option not found");
+ return;
+ }
+
+ QDomElement argsElem = doc.elementsByTagName("args").at(0).toElement();
+
+ bool ok;
+
+ // Get eval timestamp ...
+ evalTimestamp = argsElem.attributeNode("evalTimestamp").value().toInt(&ok);
+ Q_ASSERT(ok);
+ QDateTime evalDateTime;
+ evalDateTime.setTime_t(evalTimestamp);
+
+ // *** Header ***
+ QString reply = QString("<html>\n<head>\n");
+ reply += QString("<link rel=\"stylesheet\" type=\"text/css\" href=\"%1\" />\n").arg(styleSheet);
+ reply += QString("</head>\n<body>\n");
+
+ reply += "<span style=\"font-size:18\">Main Index Value Contributors</span>";
+
+ reply += "<br /><br /><table><tr><td style=\"background-color:#eeeeee\">";
+ reply += QString(
+ "This page shows the %1 result histories that contributed most significantly ")
+ .arg(doc.elementsByTagName("resultHistory").size());
+ reply += QString("to the index value at timestamp %1 (%2)")
+ .arg(evalTimestamp).arg(evalDateTime.toString());
+ reply += "</td></tr></table>\n";
+
+
+ // *** Plot ***
+
+ // reply += "<br />args:<br />";
+ // for (int i = 0; i < args.size(); ++i)
+ // reply += QString("arg %1: &gt;%2&lt;<br />").arg(i).arg(args.at(i));
+ // reply += "<br />";
+
+ reply += "<br />\n<img src=\"bmclientwrapper?command=";
+ for (int i = args.indexOf("-server"); i < args.size(); ++i)
+ reply += QString("%1 ")
+ .arg((args.at(i) == "detailspage") ? QLatin1String("plot") : args.at(i));
+ reply += QString("-cachekey %1 ").arg(argsElem.attributeNode("cacheKey").value());
+ reply += "-httpheader ";
+ reply += "\" />\n<br />\n<br />\n";
+
+
+ reply += "</body></html>";
+
+ BMMisc::printHTMLOutput(reply);
+}
+
+void BMRequest_GetIXHistories::handleReply_Image(const QStringList &args) const
+{
+ const QString error =
+ doc.elementsByTagName("reply").at(0).toElement().attributeNode("error").value();
+
+ if (!error.isEmpty()) {
+ BMMisc::printHTMLErrorPage(
+ QString(
+ "failed to create plot for main index contributors: %1").arg(error));
+ return;
+ }
+
+ QDomElement argsElem = doc.elementsByTagName("args").at(0).toElement();
+
+ bool ok;
+
+ // Get eval timestamp ...
+ evalTimestamp = argsElem.attributeNode("evalTimestamp").value().toInt(&ok);
+ Q_ASSERT(ok);
+
+ QList<ResultHistoryInfo *> rhInfos;
+ QList<int> basePos;
+ QList<QList<int> > extraPos;
+ QStringList descr;
+
+ // Loop over result histories ...
+ QDomNodeList rhNodes = doc.elementsByTagName("resultHistory");
+ for (int i = 0; i < rhNodes.size(); ++i) {
+ QDomElement rhElem = rhNodes.at(i).toElement();
+
+ // Get the context ...
+ const QString metric = rhElem.attributeNode("metric").value();
+ const QString platform = rhElem.attributeNode("platform").value();
+ const QString host = rhElem.attributeNode("host").value();
+ const QString gitRepo = rhElem.attributeNode("gitRepo").value();
+ const QString gitBranch = rhElem.attributeNode("gitBranch").value();
+
+ // Get the benchmark ...
+ const QString testCase = rhElem.attributeNode("testCase").value();
+ const QString testFunction = rhElem.attributeNode("testFunction").value();
+ const QString dataTag = rhElem.attributeNode("dataTag").value();
+
+ // Get the time series ...
+ QList<int> timestamps;
+ QList<qreal> values;
+ QDomNodeList valueNodes = rhElem.elementsByTagName("val");
+ for (int j = 0; j < valueNodes.size(); ++j) {
+ bool ok;
+ QDomElement valueElem = valueNodes.at(j).toElement();
+ timestamps += valueElem.attributeNode("t").value().toInt(&ok);
+ Q_ASSERT(ok);
+ values += valueElem.attributeNode("v").value().toDouble(&ok);
+ Q_ASSERT(ok);
+ }
+
+ rhInfos.append(
+ new ResultHistoryInfo(
+ -1, timestamps, values, metric, platform, host, gitRepo, gitBranch, testCase,
+ testFunction, dataTag));
+
+ // Get the base- and diff positions and description ...
+ const int basePos_ = rhElem.attributeNode("basePos").value().toInt(&ok);
+ Q_ASSERT(ok);
+ const int diffPos1 = rhElem.attributeNode("diffPos1").value().toInt(&ok);
+ Q_ASSERT(ok);
+ const int diffPos2 = rhElem.attributeNode("diffPos2").value().toInt(&ok);
+ Q_ASSERT(ok);
+
+ basePos.append(basePos_);
+ extraPos.append(QList<int>() << diffPos1 << diffPos2);
+
+ descr.append(rhElem.attributeNode("descr").value());
+ }
+
+ // Create image ...
+ QString error_;
+ Plotter *plotter =
+ new HistoriesPlotter(rhInfos, true, evalTimestamp, &basePos, &extraPos, &descr);
+ const QImage image = plotter->createImage(&error_);
+
+ // Free dynamic memory ...
+ for (int i = 0; i < rhInfos.size(); ++i)
+ delete rhInfos.at(i);
+
+ // Output image ...
+ if (!image.isNull()) {
+ BMMisc::printImageOutput(
+ image, "png", QString(), BMMisc::hasOption(args, "-httpheader"));
+ } else {
+ BMMisc::printHTMLErrorPage(
+ QString(
+ "failed to create plot for main index contributors: %1").arg(error_));
+ }
+}