summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristiaan Janssen <christiaan.janssen@nokia.com>2012-02-06 16:08:56 +0100
committerChristiaan Janssen <christiaan.janssen@nokia.com>2012-02-08 16:02:17 +0100
commitbd438d1c5c9cc1c6e31271a84d9824aa8b2b4ff9 (patch)
tree5f4dd4dd470d243de9e141d5b7933d792141d77d
parent1d1d2f0b748e33caaa78d9b14fb34847cd0323f7 (diff)
downloadqt-creator-bd438d1c5c9cc1c6e31271a84d9824aa8b2b4ff9.tar.gz
QmlProfiler: display binding loops
Change-Id: Ib553f67b25e614bd210959ce82bc970daa228fdb Reviewed-by: Kai Koehne <kai.koehne@nokia.com>
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilereventlist.cpp68
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilereventlist.h10
-rw-r--r--src/plugins/qmlprofiler/qml/MainView.qml2
-rw-r--r--src/plugins/qmlprofiler/qml/Overview.js15
-rw-r--r--src/plugins/qmlprofiler/qml/RangeDetails.qml6
-rw-r--r--src/plugins/qmlprofiler/qmlprofilereventview.cpp20
-rw-r--r--src/plugins/qmlprofiler/timelineview.cpp77
-rw-r--r--src/plugins/qmlprofiler/timelineview.h3
8 files changed, 192 insertions, 9 deletions
diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp b/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp
index 51531af821..0bca4e029a 100644
--- a/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp
+++ b/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp
@@ -71,6 +71,7 @@ QmlEventData::QmlEventData()
timePerCall = 0;
percentOfTime = 0;
medianTime = 0;
+ isBindingLoop = false;
}
QmlEventData::~QmlEventData()
@@ -99,6 +100,7 @@ QmlEventData &QmlEventData::operator=(const QmlEventData &ref)
percentOfTime = ref.percentOfTime;
medianTime = ref.medianTime;
eventId = ref.eventId;
+ isBindingLoop = ref.isBindingLoop;
qDeleteAll(parentHash.values());
parentHash.clear();
@@ -182,6 +184,8 @@ struct QmlEventStartTimeData {
// animation-related data
int frameRate;
int animationCount;
+
+ int bindingLoopHead;
};
struct QmlEventTypeCount {
@@ -790,6 +794,9 @@ void QmlProfilerEventList::compileStatistics(qint64 startTime, qint64 endTime)
iter.key()->medianTime = iter.value().at(iter.value().count()/2);
}
}
+
+ // find binding loops
+ findBindingLoops(startTime, endTime);
}
void QmlProfilerEventList::prepareForDisplay()
@@ -1063,6 +1070,59 @@ void QmlProfilerEventList::finishedRewritingDetails()
emit reloadDetailLabels();
}
+void QmlProfilerEventList::findBindingLoops(qint64 startTime, qint64 endTime)
+{
+ // first clear existing data
+ foreach (QmlEventData *event, d->m_eventDescriptions.values()) {
+ event->isBindingLoop = false;
+ foreach (QmlEventSub *parentEvent, event->parentHash.values())
+ parentEvent->inLoopPath = false;
+ foreach (QmlEventSub *childEvent, event->childrenHash.values())
+ childEvent->inLoopPath = false;
+ }
+
+ QList <QmlEventData *> stackRefs;
+ QList <QmlEventStartTimeData *> stack;
+ int fromIndex = findFirstIndex(startTime);
+ int toIndex = findLastIndex(endTime);
+
+ for (int i = 0; i < d->m_startTimeSortedList.count(); i++) {
+ QmlEventData *currentEvent = d->m_startTimeSortedList[i].description;
+ QmlEventStartTimeData *inTimeEvent = &d->m_startTimeSortedList[i];
+ inTimeEvent->bindingLoopHead = -1;
+
+ // managing call stack
+ for (int j = stack.count() - 1; j >= 0; j--) {
+ if (stack[j]->startTime + stack[j]->length < inTimeEvent->startTime) {
+ stack.removeAt(j);
+ stackRefs.removeAt(j);
+ }
+ }
+
+ bool loopDetected = stackRefs.contains(currentEvent);
+ stack << inTimeEvent;
+ stackRefs << currentEvent;
+
+ if (loopDetected) {
+ if (i >= fromIndex && i <= toIndex) {
+ // for the statistics
+ currentEvent->isBindingLoop = true;
+ for (int j = stackRefs.indexOf(currentEvent); j < stackRefs.count()-1; j++) {
+ QmlEventSub *nextEventSub = stackRefs[j]->childrenHash.value(stackRefs[j+1]->eventHashStr);
+ nextEventSub->inLoopPath = true;
+ QmlEventSub *prevEventSub = stackRefs[j+1]->parentHash.value(stackRefs[j]->eventHashStr);
+ prevEventSub->inLoopPath = true;
+ }
+ }
+
+ // use crossed references to find index in starttimesortedlist
+ QmlEventStartTimeData *head = stack[stackRefs.indexOf(currentEvent)];
+ inTimeEvent->bindingLoopHead = d->m_endTimeSortedList[head->endTimeIndex].startTimeIndex;
+ d->m_startTimeSortedList[inTimeEvent->bindingLoopHead].bindingLoopHead = i;
+ }
+ }
+}
+
// get list of events between A and B:
// find fist event with endtime after A -> aa
// find last event with starttime before B -> bb
@@ -1689,10 +1749,16 @@ QString QmlProfilerEventList::getDetails(int index) const
return d->m_startTimeSortedList[index].description->details;
}
-int QmlProfilerEventList::getEventId(int index) const {
+int QmlProfilerEventList::getEventId(int index) const
+{
return d->m_startTimeSortedList[index].description->eventId;
}
+int QmlProfilerEventList::getBindingLoopDest(int index) const
+{
+ return d->m_startTimeSortedList[index].bindingLoopHead;
+}
+
int QmlProfilerEventList::getFramerate(int index) const
{
return d->m_startTimeSortedList[index].frameRate;
diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlist.h b/src/libs/qmljsdebugclient/qmlprofilereventlist.h
index b8eef3a2bc..b5b9660354 100644
--- a/src/libs/qmljsdebugclient/qmlprofilereventlist.h
+++ b/src/libs/qmljsdebugclient/qmlprofilereventlist.h
@@ -65,16 +65,18 @@ struct QMLJSDEBUGCLIENT_EXPORT QmlEventData
double percentOfTime;
qint64 medianTime;
int eventId;
+ bool isBindingLoop;
QmlEventData &operator=(const QmlEventData &ref);
};
struct QMLJSDEBUGCLIENT_EXPORT QmlEventSub {
- QmlEventSub(QmlEventData *from) : reference(from), duration(0), calls(0) {}
- QmlEventSub(QmlEventSub *from) : reference(from->reference), duration(from->duration), calls(from->calls) {}
+ QmlEventSub(QmlEventData *from) : reference(from), duration(0), calls(0), inLoopPath(false) {}
+ QmlEventSub(QmlEventSub *from) : reference(from->reference), duration(from->duration), calls(from->calls), inLoopPath(from->inLoopPath) {}
QmlEventData *reference;
qint64 duration;
qint64 calls;
+ bool inLoopPath;
};
struct QMLJSDEBUGCLIENT_EXPORT QV8EventData
@@ -142,11 +144,13 @@ public:
Q_INVOKABLE int getColumn(int index) const;
Q_INVOKABLE QString getDetails(int index) const;
Q_INVOKABLE int getEventId(int index) const;
+ Q_INVOKABLE int getBindingLoopDest(int index) const;
Q_INVOKABLE int getFramerate(int index) const;
Q_INVOKABLE int getAnimationCount(int index) const;
Q_INVOKABLE int getMaximumAnimationCount() const;
Q_INVOKABLE int getMinimumAnimationCount() const;
+
// per-type data
Q_INVOKABLE int uniqueEventsOfType(int type) const;
Q_INVOKABLE int maxNestingForType(int type) const;
@@ -206,6 +210,8 @@ private:
void prepareForDisplay();
void linkEndsToStarts();
void reloadDetails();
+ void findBindingLoops(qint64 startTime, qint64 endTime);
+ bool checkBindingLoop(QmlEventData *from, QmlEventData *current, QList<QmlEventData *>visited);
private:
class QmlProfilerEventListPrivate;
diff --git a/src/plugins/qmlprofiler/qml/MainView.qml b/src/plugins/qmlprofiler/qml/MainView.qml
index bf0fd6f6f6..557d8279e6 100644
--- a/src/plugins/qmlprofiler/qml/MainView.qml
+++ b/src/plugins/qmlprofiler/qml/MainView.qml
@@ -269,6 +269,7 @@ Rectangle {
rangeDetails.file = "";
rangeDetails.line = -1;
rangeDetails.column = 0;
+ rangeDetails.isBindingLoop = false;
}
function selectNextWithId( eventId )
@@ -424,6 +425,7 @@ Rectangle {
rangeDetails.line = qmlEventList.getLine(selectedItem);
rangeDetails.column = qmlEventList.getColumn(selectedItem);
rangeDetails.type = root.names[qmlEventList.getType(selectedItem)];
+ rangeDetails.isBindingLoop = qmlEventList.getBindingLoopDest(selectedItem)!==-1;
rangeDetails.visible = true;
diff --git a/src/plugins/qmlprofiler/qml/Overview.js b/src/plugins/qmlprofiler/qml/Overview.js
index 7aeeabd971..4fe625a7fd 100644
--- a/src/plugins/qmlprofiler/qml/Overview.js
+++ b/src/plugins/qmlprofiler/qml/Overview.js
@@ -95,6 +95,21 @@ function drawData(canvas, ctxt, region)
highest[ty] = xx+eventWidth;
}
}
+
+ // binding loops
+ ctxt.strokeStyle = "orange";
+ ctxt.lineWidth = 2;
+ var radius = 1;
+ for (var ii = 0; ii < qmlEventList.count(); ++ii) {
+ if (qmlEventList.getBindingLoopDest(ii) >= 0) {
+ var xcenter = Math.round(qmlEventList.getStartTime(ii) +
+ qmlEventList.getDuration(ii) -
+ qmlEventList.traceStartTime()) * spacing;
+ var ycenter = Math.round(bump + qmlEventList.getType(ii) * blockHeight + blockHeight/2);
+ ctxt.arc(xcenter, ycenter, radius, 0, 2*Math.PI, true);
+ ctxt.stroke();
+ }
+ }
}
function drawTimeBar(canvas, ctxt, region)
diff --git a/src/plugins/qmlprofiler/qml/RangeDetails.qml b/src/plugins/qmlprofiler/qml/RangeDetails.qml
index 7013affe08..324c0903b0 100644
--- a/src/plugins/qmlprofiler/qml/RangeDetails.qml
+++ b/src/plugins/qmlprofiler/qml/RangeDetails.qml
@@ -42,6 +42,7 @@ Item {
property string file
property int line
property int column
+ property bool isBindingLoop
property bool locked: view.selectionLocked
@@ -153,6 +154,11 @@ Item {
return (file.length !== 0) ? (file + ":" + rangeDetails.line) : "";
}
}
+ Detail {
+ visible: isBindingLoop
+ opacity: content.length != 0 ? 1 : 0
+ label: qsTr("Binding loop detected")
+ }
}
}
diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.cpp b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
index f89a966f9b..57eb3d4fa2 100644
--- a/src/plugins/qmlprofiler/qmlprofilereventview.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
@@ -54,6 +54,16 @@ using namespace QmlJsDebugClient;
namespace QmlProfiler {
namespace Internal {
+struct Colors {
+ Colors () {
+ this->bindingLoopBackground = QColor("orange").lighter();
+ }
+
+ QColor bindingLoopBackground;
+};
+
+Q_GLOBAL_STATIC(Colors, colors)
+
////////////////////////////////////////////////////////////////////////////////////
class EventsViewItem : public QStandardItem
@@ -531,6 +541,11 @@ void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFrom
newRow.at(0)->setData(QVariant(binding->location.line),LineRole);
newRow.at(0)->setData(QVariant(binding->location.column),ColumnRole);
newRow.at(0)->setData(QVariant(binding->eventId),EventIdRole);
+ if (binding->isBindingLoop)
+ foreach (QStandardItem *item, newRow) {
+ item->setBackground(colors()->bindingLoopBackground);
+ item->setToolTip(tr("Binding loop detected"));
+ }
// append
parentItem->appendRow(newRow);
@@ -877,6 +892,11 @@ void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *eventList)
newRow.at(0)->setData(QVariant(event->reference->eventId), EventIdRole);
newRow.at(2)->setData(QVariant(event->duration));
newRow.at(3)->setData(QVariant(event->calls));
+ if (event->inLoopPath)
+ foreach (QStandardItem *item, newRow) {
+ item->setBackground(colors()->bindingLoopBackground);
+ item->setToolTip(tr("Part of binding loop"));
+ }
} else {
QV8EventSub *event = v8List->at(index);
newRow << new EventsViewItem(event->reference->displayName);
diff --git a/src/plugins/qmlprofiler/timelineview.cpp b/src/plugins/qmlprofiler/timelineview.cpp
index aa971f166f..3fd0d4c07d 100644
--- a/src/plugins/qmlprofiler/timelineview.cpp
+++ b/src/plugins/qmlprofiler/timelineview.cpp
@@ -107,9 +107,10 @@ void TimelineView::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget
int firstIndex = m_eventList->findFirstIndex(m_startTime);
int lastIndex = m_eventList->findLastIndex(m_endTime);
- drawItemsToPainter(p, firstIndex, lastIndex);
- drawSelectionBoxes(p);
+ drawItemsToPainter(p, firstIndex, lastIndex);
+ drawSelectionBoxes(p, firstIndex, lastIndex);
+ drawBindingLoopMarkers(p, firstIndex, lastIndex);
m_lastStartTime = m_startTime;
m_lastEndTime = m_endTime;
@@ -166,13 +167,11 @@ void TimelineView::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex)
}
}
-void TimelineView::drawSelectionBoxes(QPainter *p)
+void TimelineView::drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex)
{
if (m_selectedItem == -1)
return;
- int fromIndex = m_eventList->findFirstIndex(m_startTime);
- int toIndex = m_eventList->findLastIndex(m_endTime);
int id = m_eventList->getEventId(m_selectedItem);
p->setBrush(Qt::transparent);
@@ -216,6 +215,74 @@ void TimelineView::drawSelectionBoxes(QPainter *p)
}
}
+void TimelineView::drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex)
+{
+ int destindex;
+ int xfrom, xto, eventType;
+ int yfrom, yto;
+ int radius = DefaultRowHeight / 3;
+ QPen shadowPen = QPen(QColor("grey"),2);
+ QPen markerPen = QPen(QColor("orange"),2);
+ QBrush shadowBrush = QBrush(QColor("grey"));
+ QBrush markerBrush = QBrush(QColor("orange"));
+
+ p->save();
+ for (int i = fromIndex; i <= toIndex; i++) {
+ destindex = m_eventList->getBindingLoopDest(i);
+ if (destindex >= 0) {
+ // from
+ xfrom = (m_eventList->getStartTime(i) + m_eventList->getDuration(i)/2 -
+ m_startTime) * m_spacing;
+ eventType = m_eventList->getType(i);
+ if (m_rowsExpanded[eventType])
+ yfrom = m_rowStarts[eventType] + DefaultRowHeight*
+ (m_eventList->eventPosInType(i) + 1);
+ else
+ yfrom = m_rowStarts[eventType] + DefaultRowHeight*m_eventList->getNestingLevel(i);
+
+ yfrom += DefaultRowHeight / 2;
+
+ // to
+ xto = (m_eventList->getStartTime(destindex) + m_eventList->getDuration(destindex)/2 -
+ m_startTime) * m_spacing;
+ eventType = m_eventList->getType(destindex);
+ if (m_rowsExpanded[eventType])
+ yto = m_rowStarts[eventType] + DefaultRowHeight*
+ (m_eventList->eventPosInType(destindex) + 1);
+ else
+ yto = m_rowStarts[eventType] + DefaultRowHeight *
+ m_eventList->getNestingLevel(destindex);
+
+ yto += DefaultRowHeight / 2;
+
+ // radius
+ int eventWidth = m_eventList->getDuration(i) * m_spacing;
+ radius = 5;
+ if (radius * 2 > eventWidth)
+ radius = eventWidth / 2;
+ if (radius < 2)
+ radius = 2;
+
+ // shadow
+ int shadowoffset = 2;
+ p->setPen(shadowPen);
+ p->setBrush(shadowBrush);
+ p->drawEllipse(QPoint(xfrom, yfrom + shadowoffset), radius, radius);
+ p->drawEllipse(QPoint(xto, yto + shadowoffset), radius, radius);
+ p->drawLine(QPoint(xfrom, yfrom + shadowoffset), QPoint(xto, yto + shadowoffset));
+
+
+ // marker
+ p->setPen(markerPen);
+ p->setBrush(markerBrush);
+ p->drawEllipse(QPoint(xfrom, yfrom), radius, radius);
+ p->drawEllipse(QPoint(xto, yto), radius, radius);
+ p->drawLine(QPoint(xfrom, yfrom), QPoint(xto, yto));
+ }
+ }
+ p->restore();
+}
+
void TimelineView::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
// special case: if there is a drag area below me, don't accept the
diff --git a/src/plugins/qmlprofiler/timelineview.h b/src/plugins/qmlprofiler/timelineview.h
index 9b04b6dc5b..dcb3bbfc3f 100644
--- a/src/plugins/qmlprofiler/timelineview.h
+++ b/src/plugins/qmlprofiler/timelineview.h
@@ -182,7 +182,8 @@ protected:
private:
QColor colorForItem(int itemIndex);
void drawItemsToPainter(QPainter *p, int fromIndex, int toIndex);
- void drawSelectionBoxes(QPainter *p);
+ void drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex);
+ void drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex);
void manageClicked();
void manageHovered(int x, int y);